diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index b6d1da49d..d950633bf 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -7,6 +7,9 @@ // Features to add to the dev container. More info: https://containers.dev/features. "features": { "ghcr.io/devcontainers/features/github-cli:1": {}, + "ghcr.io/devcontainers/features/node:1": { + "version": "22" + }, "ghcr.io/devcontainers/features/python:1": {} }, diff --git a/.devcontainer/postCreateCommands.sh b/.devcontainer/postCreateCommands.sh index bc62f4925..2d43cfea2 100755 --- a/.devcontainer/postCreateCommands.sh +++ b/.devcontainer/postCreateCommands.sh @@ -1,4 +1,6 @@ -#/!bin/sh +#!/bin/sh + +set -e # Command to run during postCreateCommand # Done in script because when done via object the commands are run in parallel @@ -7,4 +9,5 @@ # https://containers.dev/implementors/json_reference/#formatting-string-vs-array-properties pip install -r requirements.txt +make sdk-install make html diff --git a/.gitignore b/.gitignore index 4ccc56e36..564bb7782 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,5 @@ _build **/.DS_Store .local/ .vscode +node_modules/ +_static/vendor/evo-sdk.js diff --git a/.readthedocs.yml b/.readthedocs.yml index a429d8270..6c283608a 100644 --- a/.readthedocs.yml +++ b/.readthedocs.yml @@ -12,6 +12,11 @@ build: os: "ubuntu-22.04" tools: python: "3.10" + nodejs: "22" + jobs: + pre_build: + - npm ci + - npm run build:sdk python: install: diff --git a/CLAUDE.md b/CLAUDE.md index 9f614f5ce..6d4be0a09 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -14,7 +14,12 @@ python -m venv venv source venv/bin/activate # or venv\Scripts\activate on Windows pip install -r requirements.txt -# Build documentation +# Install JavaScript build dependencies (once, on a fresh checkout; needed to +# bundle the Evo SDK for the interactive tutorial runners) +make sdk-install + +# Build documentation (also regenerates the gitignored SDK bundle via the +# `sdk` target) make html # Clean build @@ -30,7 +35,12 @@ python3 scripts/tutorial-sync/sync_tutorial_code.py --source /path/to/platform-t python3 scripts/tutorial-sync/sync_tutorial_code.py --check --source /path/to/platform-tutorials # View built documentation -# Open _build/html/index.html in browser +# Most pages can be opened directly in a browser +# Open _build/html/index.html + +# To use interactive tutorial widgets, serve the build over HTTP because +# browsers block their SDK module import from file:// pages +python -m http.server 8000 -d _build/html ``` ## Architecture @@ -64,6 +74,7 @@ The site uses a hierarchical structure with: - GitHub integration for edit links and source references - Google Analytics tracking configured - Uses pydata-sphinx-theme with custom CSS overrides +- Interactive tutorial runners (`_static/js/interactive-tutorial.js`) import the Evo SDK from `_static/vendor/evo-sdk.js`, a gitignored bundle produced by `npm run build:sdk` (esbuild, pinned via `package-lock.json`). Read the Docs builds it in a `pre_build` job; locally `make html` rebuilds it automatically (run `make sdk-install` once first). To change the SDK version, update `package.json`/`package-lock.json` and rebuild. ## Editing Guidelines @@ -100,4 +111,4 @@ For the full per-release endpoint review process (proto diff, example refresh, d - Configuration: `conf.py`, `requirements.txt` - Templates: `_templates/*.html` - Assets: `_static/**/*` -- Build output: `_build/html/` (excluded from git) \ No newline at end of file +- Build output: `_build/html/` (excluded from git) diff --git a/Makefile b/Makefile index d4bb2cbb9..dc089887f 100644 --- a/Makefile +++ b/Makefile @@ -7,12 +7,33 @@ SPHINXOPTS ?= SPHINXBUILD ?= sphinx-build SOURCEDIR = . BUILDDIR = _build +NPM ?= npm +SDK_BUNDLER = node_modules/.bin/esbuild # Put it first so that "make" without argument is like "make help". help: @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) -.PHONY: help Makefile +.PHONY: help sdk-install sdk html Makefile + +# Install the pinned JavaScript build dependencies for local development. +# Read the Docs performs this step separately in .readthedocs.yml. +sdk-install: + $(NPM) ci + +# Generate the browser SDK asset copied by Sphinx with the other static files. +sdk: $(SDK_BUNDLER) + $(NPM) run build:sdk + +# Give a useful setup hint instead of letting npm fail with "esbuild: not found". +$(SDK_BUNDLER): + @echo "Missing JavaScript build dependencies." + @echo "Run 'make sdk-install' once, then retry 'make html'." + @false + +# Keep the generated SDK bundle current for local HTML builds. +html: sdk + @$(SPHINXBUILD) -M html "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) # Catch-all target: route all unknown targets to Sphinx using the new # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). diff --git a/_static/css/pydata-overrides.css b/_static/css/pydata-overrides.css index 2600e17fd..619bf8922 100644 --- a/_static/css/pydata-overrides.css +++ b/_static/css/pydata-overrides.css @@ -192,3 +192,243 @@ sphinx search extension interface. max-height: 60rem; overflow: auto; } +/* Reusable, browser-executed blocks for safe tutorial operations. */ +.interactive-tutorial { + margin: 1rem 0 1.5rem; + padding: 1rem; + border: 1px solid var(--pst-color-border); + border-radius: 0.5rem; + background: var(--pst-color-surface); +} + +.interactive-tutorial__header, +.interactive-tutorial__controls, +.interactive-tutorial__summary { + display: flex; + align-items: center; + gap: 0.75rem; +} + +.interactive-tutorial__header { + justify-content: space-between; + margin-bottom: 1rem; +} + +/* Integrated runner layout used directly beneath a tutorial code example. */ +.interactive-tutorial--integrated { + margin-top: -0.5rem; + border-top-left-radius: 0.25rem; + border-top-right-radius: 0.25rem; +} + +.interactive-tutorial__toolbar { + display: flex; + align-items: center; + justify-content: space-between; + gap: 1rem; + margin: -1rem -1rem 1rem; + padding: 0.65rem 1rem; + border-bottom: 1px solid var(--pst-color-border); + font-size: 0.9rem; +} + +.interactive-tutorial__help, +.interactive-tutorial__connection, +.interactive-tutorial__empty { + color: var(--pst-color-text-muted); + font-size: 0.85rem; +} + +.interactive-tutorial__connection[data-state="connected"] { + color: #16834a; +} + +.interactive-tutorial__connection[data-state="error"] { + color: var(--pst-color-danger); +} + +.interactive-tutorial__connection:empty { + display: none; +} + +.interactive-tutorial__label { + display: block; + margin-bottom: 0.35rem; + font-weight: 600; +} + +.interactive-tutorial__field { + flex: 1 1 12rem; + min-width: 0; +} + +.interactive-tutorial__field--wide { + flex-basis: 28rem; +} + +.interactive-tutorial__field--small { + flex: 0 1 6rem; +} + +.interactive-tutorial__field .interactive-tutorial__input { + width: 100%; +} + +.interactive-tutorial__actions { + display: flex; + gap: 0.75rem; + margin-top: 0.75rem; +} + +.interactive-tutorial__controls--secondary { + margin-top: 0.75rem; +} + +.interactive-tutorial__input { + flex: 1 1 28rem; + min-width: 0; + padding: 0.55rem 0.7rem; + border: 1px solid var(--pst-color-border); + border-radius: 0.3rem; + background: var(--pst-color-background); + color: var(--pst-color-text-base); + font-family: var(--pst-font-family-monospace); +} + +.interactive-tutorial__button { + padding: 0.4rem 0.8rem; + border: 1px solid var(--pst-color-primary); + border-radius: 0.3rem; + background: var(--pst-color-primary); + color: #fff; + cursor: pointer; + white-space: nowrap; +} + +.interactive-tutorial__button--secondary { + background: transparent; + color: var(--pst-color-primary); +} + +.interactive-tutorial__button--text { + padding-right: 0.25rem; + padding-left: 0.25rem; + border-color: transparent; + background: transparent; + color: var(--pst-color-primary); +} + +.interactive-tutorial__button--text:hover { + text-decoration: underline; +} + +.interactive-tutorial__button:disabled { + cursor: wait; + opacity: 0.65; +} + +.interactive-tutorial__help { + margin-top: 0.45rem; +} + +.interactive-tutorial__source { + margin-top: 1rem; +} + +.interactive-tutorial__source summary { + cursor: pointer; + font-weight: 600; +} + +.interactive-tutorial__source pre { + margin: 0.5rem 0 0; + padding: 0.85rem; + border: 1px solid var(--pst-color-border); + border-radius: 0.3rem; + background: var(--pst-color-on-background); + overflow: auto; +} + +.interactive-tutorial__result { + margin-top: 1rem; + padding: 0.85rem; + border-radius: 0.3rem; + background: var(--pst-color-on-background); +} + +.interactive-tutorial--integrated .interactive-tutorial__result { + padding-top: 0.6rem; + padding-bottom: 0.6rem; +} + +.interactive-tutorial--integrated .interactive-tutorial__summary { + margin-bottom: 0.35rem; +} + +.interactive-tutorial__summary { + flex-wrap: wrap; + margin-bottom: 0.75rem; +} + +.interactive-tutorial__metric { + min-width: 0; + max-width: 100%; + padding-right: 1rem; +} + +.interactive-tutorial__metric strong, +.interactive-tutorial__metric span { + display: block; +} + +.interactive-tutorial__metric strong { + overflow-wrap: anywhere; +} + +.interactive-tutorial__metric span { + color: var(--pst-color-text-muted); + font-size: 0.75rem; +} + +.interactive-tutorial__json { + max-height: 28rem; + margin: 0; + overflow: auto; + white-space: pre; + font-size: 0.82rem; +} + +.interactive-tutorial__error { + color: var(--pst-color-danger); +} + +@media (max-width: 767.98px) { + .interactive-tutorial__controls, + .interactive-tutorial__actions { + align-items: stretch; + flex-direction: column; + } + + .interactive-tutorial__input, + .interactive-tutorial__field, + .interactive-tutorial__field--wide, + .interactive-tutorial__field--small { + flex: none; + width: 100%; + } + + .interactive-tutorial__summary { + align-items: stretch; + flex-direction: column; + } + + .interactive-tutorial__metric { + padding-right: 0; + } + + .interactive-tutorial__toolbar { + align-items: flex-start; + flex-direction: column; + gap: 0.2rem; + } +} diff --git a/_static/dashmint-lite.html b/_static/dashmint-lite.html index 841166c65..c3be817cf 100644 --- a/_static/dashmint-lite.html +++ b/_static/dashmint-lite.html @@ -119,7 +119,7 @@

Browse cards

// package and serves it as a browser-native ES module. Pinned to the same // version the React app at ../package.json depends on so both UIs behave // identically against the same testnet contract. - import { EvoSDK } from 'https://esm.sh/@dashevo/evo-sdk@4.1.0'; + import { EvoSDK } from 'https://esm.sh/@dashevo/evo-sdk@4.1.1'; // The token-enabled "card" data contract is already published on testnet by // the React app. Anyone querying with the same contract id hits the same diff --git a/_static/dashnote-lite.html b/_static/dashnote-lite.html index e1241e054..0c4438060 100644 --- a/_static/dashnote-lite.html +++ b/_static/dashnote-lite.html @@ -129,7 +129,7 @@

Get note by ID

// package and serves it as a browser-native ES module. Pinned to the same // version the React app at ../package.json depends on so both UIs behave // identically against the same testnet contract. - import { EvoSDK } from 'https://esm.sh/@dashevo/evo-sdk@4.1.0'; + import { EvoSDK } from 'https://esm.sh/@dashevo/evo-sdk@4.1.1'; // The "note" data contract is already published on testnet by the React app. // Anyone querying with the same contract id hits the same documents. diff --git a/_static/dashproof-lite.html b/_static/dashproof-lite.html index 8898daed1..583e2f3cd 100644 --- a/_static/dashproof-lite.html +++ b/_static/dashproof-lite.html @@ -120,7 +120,7 @@

History by chainId

// package and serves it as a browser-native ES module. Pinned to the same // version the React app at ../package.json depends on so both UIs behave // identically against the same testnet contract. - import { EvoSDK } from 'https://esm.sh/@dashevo/evo-sdk@4.1.0'; + import { EvoSDK } from 'https://esm.sh/@dashevo/evo-sdk@4.1.1'; // The "anchor" data contract is already published on testnet by the React app. // Anyone querying with the same contract id hits the same documents. diff --git a/_static/js/interactive-tutorial.js b/_static/js/interactive-tutorial.js new file mode 100644 index 000000000..16d6bef39 --- /dev/null +++ b/_static/js/interactive-tutorial.js @@ -0,0 +1,384 @@ +// Resolve from this script rather than the current documentation page so the +// URL works at every Sphinx nesting level. The bundle is generated by the +// Read the Docs pre-build job and copied with the other static assets. +const SDK_URL = new URL('../vendor/evo-sdk.js', document.currentScript.src).href; +const DPNS_CONTRACT_ID = 'GWRSAVFMjXx8HpQFaNJMqBV7MBgMK4br5UESsB4S31Ec'; + +// Explicit allow-list of safe, read-only tutorial operations. The UI obtains +// its displayed source directly from these functions, preventing code drift. +async function getNetworkStatus({ EvoSDK }) { + const sdk = EvoSDK.testnetTrusted(); + await sdk.connect(); + const status = await sdk.system.status(); + return status.toJSON(); +} + +async function fetchIdentity({ EvoSDK, identityId }) { + const sdk = EvoSDK.testnetTrusted(); + await sdk.connect(); + const identity = await sdk.identities.fetch(identityId); + return identity?.toJSON() ?? null; +} + +async function fetchContract({ EvoSDK, dataContractId }) { + const sdk = EvoSDK.testnetTrusted(); + await sdk.connect(); + const contract = await sdk.contracts.fetch(dataContractId); + return contract?.toJSON() ?? null; +} + +async function getContractHistory({ EvoSDK, dataContractId }) { + const sdk = EvoSDK.testnetTrusted(); + await sdk.connect(); + return sdk.contracts.getHistory({ dataContractId }); +} + +async function queryDocuments({ EvoSDK, dataContractId, documentTypeName, limit }) { + const sdk = EvoSDK.testnetTrusted(); + await sdk.connect(); + return sdk.documents.query({ + dataContractId, + documentTypeName, + limit: Number(limit), + }); +} + +async function resolveName({ EvoSDK, name }) { + const sdk = EvoSDK.testnetTrusted(); + await sdk.connect(); + return sdk.dpns.resolveName(name); +} + +async function getIdentityNames({ EvoSDK, identityId }) { + const sdk = EvoSDK.testnetTrusted(); + await sdk.connect(); + return sdk.dpns.usernames({ identityId }); +} + +async function searchNames({ EvoSDK, prefix }) { + const sdk = EvoSDK.testnetTrusted(); + await sdk.connect(); + const normalizedPrefix = await sdk.dpns.convertToHomographSafe(prefix); + return sdk.documents.query({ + dataContractId: DPNS_CONTRACT_ID, + documentTypeName: 'domain', + where: [ + ['normalizedParentDomainName', '==', 'dash'], + ['normalizedLabel', 'startsWith', normalizedPrefix], + ], + orderBy: [['normalizedLabel', 'asc']], + }); +} + +async function getTokenInfo({ EvoSDK, dataContractId, tokenPosition, identityId, recipientId }) { + const sdk = EvoSDK.testnetTrusted(); + await sdk.connect(); + const tokenId = await sdk.tokens.calculateId(dataContractId, Number(tokenPosition)); + const contractInfo = await sdk.tokens.contractInfo(tokenId); + const totalSupply = await sdk.tokens.totalSupply(tokenId); + const statuses = await sdk.tokens.statuses([tokenId]); + const identityBalances = await sdk.tokens.identityBalances(identityId, [tokenId]); + const recipientBalances = await sdk.tokens.identityBalances(recipientId, [tokenId]); + const status = statuses.get(tokenId); + + return { + tokenId: tokenId.toString(), + contractInfo: contractInfo?.toJSON() ?? null, + totalSupply: totalSupply?.totalSupply ?? 0n, + isPaused: status?.isPaused ?? null, + identityBalance: identityBalances.get(tokenId) ?? 0n, + recipientBalance: recipientBalances.get(tokenId) ?? 0n, + }; +} + +const operations = { + 'network-status': getNetworkStatus, + 'identity-fetch': fetchIdentity, + 'contract-fetch': fetchContract, + 'contract-history': getContractHistory, + 'documents-query': queryDocuments, + 'name-resolve': resolveName, + 'identity-names': getIdentityNames, + 'name-search': searchNames, + 'token-info': getTokenInfo, +}; + +const operationConstants = { + 'name-search': { DPNS_CONTRACT_ID }, +}; + +const text = (value) => String(value ?? '—'); + +function integer(value) { + if (value == null || value === '') return '—'; + if (typeof value === 'bigint') return value.toLocaleString(); + if (typeof value === 'number') { + return Number.isSafeInteger(value) ? value.toLocaleString() : text(value); + } + if (typeof value === 'string' && /^[+-]?\d+$/.test(value)) { + return BigInt(value).toLocaleString(); + } + return text(value); +} + +function metric(label, value) { + const wrapper = document.createElement('div'); + wrapper.className = 'interactive-tutorial__metric'; + const strong = document.createElement('strong'); + strong.textContent = text(value); + const caption = document.createElement('span'); + caption.textContent = label; + wrapper.append(strong, caption); + return wrapper; +} + +function normalize(value) { + if (value && typeof value.toJSON === 'function') return normalize(value.toJSON()); + if (value instanceof Map) { + return [...value.entries()].map(([key, item]) => ({ mapKey: text(key), ...normalize(item) })); + } + if (Array.isArray(value)) return value.map(normalize); + if (value && typeof value === 'object') { + return Object.fromEntries(Object.entries(value).map(([key, item]) => [key, normalize(item)])); + } + return value; +} + +function renderResult(container, rawValue, renderer) { + const value = renderer === 'name' ? text(rawValue) : normalize(rawValue); + const summary = document.createElement('div'); + summary.className = 'interactive-tutorial__summary'; + if (renderer === 'identity') { + summary.append( + metric('Identity ID', value.id), + metric('Balance (credits)', integer(value.balance ?? 0)), + metric('Revision', value.revision), + metric('Public keys', value.publicKeys?.length ?? 0), + ); + } else if (renderer === 'contract') { + summary.append( + metric('Contract ID', value.id), + metric('Owner', value.ownerId), + metric('Version', value.version), + metric('Document types', Object.keys(value.documentSchemas ?? {}).length), + ); + } else if (renderer === 'history') { + const timestamps = value + .map((entry) => Number(entry.mapKey)) + .filter(Number.isFinite) + .sort((a, b) => a - b); + const versions = value + .map((entry) => Number(entry.version)) + .filter(Number.isFinite); + summary.append( + metric('Revisions', value.length), + metric('Latest version', versions.length ? Math.max(...versions) : '—'), + metric('First revision', timestamps.length ? new Date(timestamps[0]).toLocaleString() : '—'), + metric('Latest revision', timestamps.length ? new Date(timestamps.at(-1)).toLocaleString() : '—'), + ); + } else if (renderer === 'name') { + summary.append(metric('Resolved identity ID', value)); + } else if (renderer === 'names') { + summary.append( + metric('Names returned', value.length), + metric('Names', value.length ? value.join(', ') : '(none)'), + ); + } else if (renderer === 'name-search') { + const names = value.map((entry) => { + const label = entry.label ?? entry.normalizedLabel; + const parent = entry.parentDomainName ?? entry.normalizedParentDomainName; + return [label, parent].filter(Boolean).join('.'); + }); + summary.append( + metric('Matches', value.length), + metric('Names', names.length ? names.join(', ') : '(none)'), + ); + } else if (renderer === 'token') { + summary.append( + metric('Token ID', value.tokenId), + metric('Total supply', integer(value.totalSupply)), + metric('Status', value.isPaused == null ? 'No status published' : value.isPaused ? 'Paused' : 'Active'), + metric('Identity balance', integer(value.identityBalance)), + metric('Recipient balance', integer(value.recipientBalance)), + ); + } else if (renderer === 'documents') { + summary.append(metric('Documents returned', value.length)); + } else if (renderer === 'status') { + summary.append( + metric('Network', value.network?.chainId), + metric('Latest block', integer(value.chain?.latestBlockHeight)), + metric('Sync status', value.chain?.isCatchingUp ? 'Catching up' : 'Synced'), + metric('Peers', integer(value.network?.peersCount)), + metric('DAPI', value.version?.software?.dapi), + metric('Drive', value.version?.software?.drive), + metric('Tenderdash', value.version?.software?.tenderdash), + ); + } + + const details = document.createElement('details'); + const label = document.createElement('summary'); + label.textContent = 'Raw response'; + const output = document.createElement('pre'); + output.className = 'interactive-tutorial__json'; + output.textContent = JSON.stringify( + value, + (_key, item) => (typeof item === 'bigint' ? item.toString() : item), + 2, + ); + details.append(label, output); + container.replaceChildren(summary, details); +} + +function renderMessage(container, message, isError = false) { + const messageElement = document.createElement('div'); + messageElement.className = isError + ? 'interactive-tutorial__error' + : 'interactive-tutorial__empty'; + messageElement.textContent = message; + container.replaceChildren(messageElement); +} + +async function initialize(block) { + const operationName = block.dataset.operation; + const operation = operations[operationName]; + const network = block.dataset.network ?? 'testnet'; + const renderer = block.dataset.renderer ?? 'status'; + const inputs = [...block.querySelectorAll('[data-param]')]; + const sourceElement = block.querySelector('[data-role="source"]'); + const runButton = block.querySelector('[data-role="run"]'); + const resetButton = block.querySelector('[data-role="reset"]'); + const result = block.querySelector('[data-role="result"]'); + const connection = block.querySelector('[data-role="connection"]'); + + if (!sourceElement || !runButton || !resetButton || !result || !connection) return; + if (!operation) { + renderMessage(result, `Interactive tutorial is not configured correctly: ${operationName ?? 'missing operation'}.`, true); + return; + } + inputs.forEach((input) => { input.value = input.dataset.defaultValue ?? ''; }); + + // Derive the display from the same function object invoked by run(). The DOM + // remains non-executable, while current parameter values make the call clear. + function updateDisplayedSource() { + const constants = Object.entries(operationConstants[operationName] ?? {}).map( + ([name, value]) => `const ${name} = ${JSON.stringify(value)};`, + ); + const declarations = inputs.map( + (input) => `const ${input.dataset.param} = ${JSON.stringify(input.value)};`, + ); + const argumentNames = inputs.map((input) => input.dataset.param); + const invocationArguments = ['EvoSDK', ...argumentNames].join(', '); + sourceElement.textContent = [ + ...constants, + constants.length ? '' : null, + operation.toString(), + '', + ...declarations, + declarations.length ? '' : null, + `const result = await ${operation.name}({ ${invocationArguments} });`, + ].filter((line) => line !== null).join('\n'); + } + updateDisplayedSource(); + + let sdkPromise; + async function loadSdk() { + sdkPromise ??= import(SDK_URL).then( + ({ EvoSDK }) => EvoSDK, + (error) => { + sdkPromise = undefined; + throw error; + }, + ); + return sdkPromise; + } + + let runGeneration = 0; + async function run() { + const missing = inputs.find((input) => input.required && !input.value.trim()); + if (missing) { + renderMessage(result, `Enter ${missing.dataset.label ?? 'a value'}.`, true); + missing.focus(); + return; + } + + const invalidNumber = inputs.find((input) => { + if (input.type !== 'number') return false; + const value = Number(input.value); + const minimum = input.min === '' ? -Infinity : Number(input.min); + const maximum = input.max === '' ? Infinity : Number(input.max); + return !Number.isFinite(value) + || !Number.isInteger(value) + || value < minimum + || value > maximum; + }); + if (invalidNumber) { + const bounds = [ + invalidNumber.min === '' ? null : `at least ${invalidNumber.min}`, + invalidNumber.max === '' ? null : `at most ${invalidNumber.max}`, + ].filter(Boolean).join(' and '); + renderMessage( + result, + `Enter ${invalidNumber.dataset.label ?? 'a value'} as a whole number${bounds ? ` ${bounds}` : ''}.`, + true, + ); + invalidNumber.focus(); + return; + } + + const parameters = Object.fromEntries( + inputs.map((input) => [input.dataset.param, input.value]), + ); + const generation = ++runGeneration; + const isCurrent = () => generation === runGeneration; + runButton.disabled = true; + inputs.forEach((input) => { input.disabled = true; }); + renderMessage(result, 'Running query…'); + try { + connection.textContent = 'Loading SDK…'; + connection.dataset.state = 'connecting'; + const sdkClass = await loadSdk(); + if (!isCurrent()) return; + connection.textContent = `Connecting to ${network}…`; + const output = await operation({ ...parameters, EvoSDK: sdkClass }); + if (!isCurrent()) return; + connection.textContent = `Connected to ${network}`; + connection.dataset.state = 'connected'; + if (output == null) { + renderMessage(result, 'No result was found.', true); + return; + } + renderResult(result, output, renderer); + } catch (error) { + if (!isCurrent()) return; + connection.textContent = 'Run failed'; + connection.dataset.state = 'error'; + renderMessage(result, `Query failed: ${error?.message ?? error}`, true); + } finally { + if (isCurrent()) { + runButton.disabled = false; + inputs.forEach((input) => { input.disabled = false; }); + } + } + } + + runButton.addEventListener('click', run); + inputs.forEach((input) => { + input.addEventListener('input', updateDisplayedSource); + input.addEventListener('keydown', (event) => { + if (event.key === 'Enter') run(); + }); + }); + resetButton.addEventListener('click', () => { + runGeneration += 1; + inputs.forEach((input) => { input.value = input.dataset.defaultValue ?? ''; }); + runButton.disabled = false; + inputs.forEach((input) => { input.disabled = false; }); + connection.textContent = ''; + delete connection.dataset.state; + updateDisplayedSource(); + renderMessage(result, 'Run the query to inspect the result.'); + inputs[0]?.focus(); + }); +} + +document.querySelectorAll('.interactive-tutorial').forEach(initialize); diff --git a/conf.py b/conf.py index 96cf214df..e99b3bacb 100644 --- a/conf.py +++ b/conf.py @@ -45,6 +45,7 @@ '.devcontainer', '.codex', '.local', + 'node_modules', 'scripts', 'img/dev/gifs/README.md', 'docs/other', @@ -142,3 +143,7 @@ def setup(app): app.add_js_file('js/pydata-search-close.js') + # Keep this a classic script so it can resolve the generated SDK bundle + # relative to its own URL. Local previews must serve _build/html over HTTP; + # browsers block the bundle's module import from file:// pages. + app.add_js_file('js/interactive-tutorial.js', defer='defer') diff --git a/docs/tutorials/connecting-to-testnet.md b/docs/tutorials/connecting-to-testnet.md index 4fa78d6ed..ffe5c2fb9 100644 --- a/docs/tutorials/connecting-to-testnet.md +++ b/docs/tutorials/connecting-to-testnet.md @@ -47,6 +47,26 @@ try { } ``` +```{raw} html +
+
+ Run this example on testnet + +
+
+ + +
+
+ View browser code +
+
+
+
Connect to inspect the current system status.
+
+
+``` + Once this returns successfully, you're ready to begin developing! See the [Quickstart](../tutorials/introduction.md#quickstart) for recommended next steps. For details on SDK methods, please refer to the [SDK documentation](https://evo-sdk.dash.org/docs.html). ## Connect to a Local Devnet diff --git a/docs/tutorials/contracts-and-documents/retrieve-a-data-contract.md b/docs/tutorials/contracts-and-documents/retrieve-a-data-contract.md index 422b826f6..113905a6c 100644 --- a/docs/tutorials/contracts-and-documents/retrieve-a-data-contract.md +++ b/docs/tutorials/contracts-and-documents/retrieve-a-data-contract.md @@ -34,6 +34,31 @@ try { } ``` +```{raw} html +
+
+ Run this example on testnet + +
+ +
+ + + +
+
+ View browser code +
+
+
+
Run the query to inspect the contract.
+
+
+``` + ## Example Data Contract The following example response shows a retrieved contract: diff --git a/docs/tutorials/contracts-and-documents/retrieve-data-contract-history.md b/docs/tutorials/contracts-and-documents/retrieve-data-contract-history.md index c5918c5c2..a36f438e4 100644 --- a/docs/tutorials/contracts-and-documents/retrieve-data-contract-history.md +++ b/docs/tutorials/contracts-and-documents/retrieve-data-contract-history.md @@ -49,6 +49,31 @@ try { } ``` +```{raw} html +
+
+ Run this example on testnet + +
+ +
+ + + +
+
+ View browser code +
+
+
+
Run the query to inspect contract revisions.
+
+
+``` + ## Example data contract history The following example response shows a retrieved contract history: diff --git a/docs/tutorials/contracts-and-documents/retrieve-documents.md b/docs/tutorials/contracts-and-documents/retrieve-documents.md index 21121f09e..f1bb8d936 100644 --- a/docs/tutorials/contracts-and-documents/retrieve-documents.md +++ b/docs/tutorials/contracts-and-documents/retrieve-documents.md @@ -41,6 +41,47 @@ try { } ``` +```{raw} html +
+
+ Run this example on testnet + +
+
+
+ + +
+
+ + +
+
+ + +
+
+
+ + +
+
+ View browser code +
+
+
+
Run the query to inspect matching documents.
+
+
+``` + ### Queries The example code uses a very basic query to return only two results. More extensive querying capabilities are covered in the [query syntax reference](../../reference/query-syntax.md). diff --git a/docs/tutorials/identities-and-names/retrieve-a-name.md b/docs/tutorials/identities-and-names/retrieve-a-name.md index 70586df1e..8a71f024f 100644 --- a/docs/tutorials/identities-and-names/retrieve-a-name.md +++ b/docs/tutorials/identities-and-names/retrieve-a-name.md @@ -34,6 +34,31 @@ try { } ``` +```{raw} html +
+
+ Run this example on testnet + +
+ +
+ + + +
+
+ View browser code +
+
+
+
Resolve the name to inspect its identity ID.
+
+
+``` + **Example Response** ```text @@ -66,6 +91,31 @@ try { } ``` +```{raw} html +
+
+ Run this example on testnet + +
+ +
+ + + +
+
+ View browser code +
+
+
+
Run the query to inspect names owned by this identity.
+
+
+``` + **Example Response** ```text @@ -111,6 +161,31 @@ try { } ``` +```{raw} html +
+
+ Run this example on testnet + +
+ +
+ + + +
+
+ View browser code +
+
+
+
Run the query to inspect matching names.
+
+
+``` + **Example Response** ```text diff --git a/docs/tutorials/identities-and-names/retrieve-an-identity.md b/docs/tutorials/identities-and-names/retrieve-an-identity.md index da19ae18e..298eb88ef 100644 --- a/docs/tutorials/identities-and-names/retrieve-an-identity.md +++ b/docs/tutorials/identities-and-names/retrieve-an-identity.md @@ -38,6 +38,43 @@ try { } ``` +```{raw} html +
+
+ Run this example on testnet + +
+ +
+ + + +
+
+ View browser code +
+
+
+
Run the query to inspect the identity.
+
+
+``` + ## Example Identity The following example response shows a retrieved identity: diff --git a/docs/tutorials/tokens/retrieve-token-info.md b/docs/tutorials/tokens/retrieve-token-info.md index 38b3af04e..34d95b59c 100644 --- a/docs/tutorials/tokens/retrieve-token-info.md +++ b/docs/tutorials/tokens/retrieve-token-info.md @@ -72,6 +72,57 @@ try { } ``` +```{raw} html +
+
+ Run this example on testnet + +
+
+
+ + +
+
+ + +
+
+
+
+ + +
+
+ + +
+
+
+ + +
+
+ View browser code +
+
+
+
Run the query to inspect token information and balances.
+
+
+``` + ## What's Happening After connecting to the client, we derive the token ID from the contract ID and token position with `sdk.tokens.calculateId()`. We then query several pieces of information: diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 000000000..8ac94ae06 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,519 @@ +{ + "name": "dashpay-docs-platform", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "dashpay-docs-platform", + "dependencies": { + "@dashevo/evo-sdk": "4.1.1" + }, + "devDependencies": { + "esbuild": "0.25.9" + } + }, + "node_modules/@dashevo/evo-sdk": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/@dashevo/evo-sdk/-/evo-sdk-4.1.1.tgz", + "integrity": "sha512-DsfHXlLByyhvAerDknozg0HT4KHNsYP6rEy949aj/KGLLydb9bwJBHOWyn5oDsNR016tvr3oeT/jCkjMA4qoCA==", + "dependencies": { + "@dashevo/wasm-sdk": "4.1.1" + }, + "engines": { + "node": ">=18.18" + } + }, + "node_modules/@dashevo/wasm-sdk": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/@dashevo/wasm-sdk/-/wasm-sdk-4.1.1.tgz", + "integrity": "sha512-/be0D7qohZc9fHgYjbtzOg8KOajarzo0NtKCNybv4Z1xeV617Wo0CusnsxCzwbV7C/jDgPJiGel0KtcSTzQcfQ==", + "engines": { + "node": ">=18.18" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.9.tgz", + "integrity": "sha512-OaGtL73Jck6pBKjNIe24BnFE6agGl+6KxDtTfHhy1HmhthfKouEcOhqpSL64K4/0WCtbKFLOdzD/44cJ4k9opA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.9.tgz", + "integrity": "sha512-5WNI1DaMtxQ7t7B6xa572XMXpHAaI/9Hnhk8lcxF4zVN4xstUgTlvuGDorBguKEnZO70qwEcLpfifMLoxiPqHQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.9.tgz", + "integrity": "sha512-IDrddSmpSv51ftWslJMvl3Q2ZT98fUSL2/rlUXuVqRXHCs5EUF1/f+jbjF5+NG9UffUDMCiTyh8iec7u8RlTLg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.9.tgz", + "integrity": "sha512-I853iMZ1hWZdNllhVZKm34f4wErd4lMyeV7BLzEExGEIZYsOzqDWDf+y082izYUE8gtJnYHdeDpN/6tUdwvfiw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.9.tgz", + "integrity": "sha512-XIpIDMAjOELi/9PB30vEbVMs3GV1v2zkkPnuyRRURbhqjyzIINwj+nbQATh4H9GxUgH1kFsEyQMxwiLFKUS6Rg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.9.tgz", + "integrity": "sha512-jhHfBzjYTA1IQu8VyrjCX4ApJDnH+ez+IYVEoJHeqJm9VhG9Dh2BYaJritkYK3vMaXrf7Ogr/0MQ8/MeIefsPQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.9.tgz", + "integrity": "sha512-z93DmbnY6fX9+KdD4Ue/H6sYs+bhFQJNCPZsi4XWJoYblUqT06MQUdBCpcSfuiN72AbqeBFu5LVQTjfXDE2A6Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.9.tgz", + "integrity": "sha512-mrKX6H/vOyo5v71YfXWJxLVxgy1kyt1MQaD8wZJgJfG4gq4DpQGpgTB74e5yBeQdyMTbgxp0YtNj7NuHN0PoZg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.9.tgz", + "integrity": "sha512-HBU2Xv78SMgaydBmdor38lg8YDnFKSARg1Q6AT0/y2ezUAKiZvc211RDFHlEZRFNRVhcMamiToo7bDx3VEOYQw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.9.tgz", + "integrity": "sha512-BlB7bIcLT3G26urh5Dmse7fiLmLXnRlopw4s8DalgZ8ef79Jj4aUcYbk90g8iCa2467HX8SAIidbL7gsqXHdRw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.9.tgz", + "integrity": "sha512-e7S3MOJPZGp2QW6AK6+Ly81rC7oOSerQ+P8L0ta4FhVi+/j/v2yZzx5CqqDaWjtPFfYz21Vi1S0auHrap3Ma3A==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.9.tgz", + "integrity": "sha512-Sbe10Bnn0oUAB2AalYztvGcK+o6YFFA/9829PhOCUS9vkJElXGdphz0A3DbMdP8gmKkqPmPcMJmJOrI3VYB1JQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.9.tgz", + "integrity": "sha512-YcM5br0mVyZw2jcQeLIkhWtKPeVfAerES5PvOzaDxVtIyZ2NUBZKNLjC5z3/fUlDgT6w89VsxP2qzNipOaaDyA==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.9.tgz", + "integrity": "sha512-++0HQvasdo20JytyDpFvQtNrEsAgNG2CY1CLMwGXfFTKGBGQT3bOeLSYE2l1fYdvML5KUuwn9Z8L1EWe2tzs1w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.9.tgz", + "integrity": "sha512-uNIBa279Y3fkjV+2cUjx36xkx7eSjb8IvnL01eXUKXez/CBHNRw5ekCGMPM0BcmqBxBcdgUWuUXmVWwm4CH9kg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.9.tgz", + "integrity": "sha512-Mfiphvp3MjC/lctb+7D287Xw1DGzqJPb/J2aHHcHxflUo+8tmN/6d4k6I2yFR7BVo5/g7x2Monq4+Yew0EHRIA==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.9.tgz", + "integrity": "sha512-iSwByxzRe48YVkmpbgoxVzn76BXjlYFXC7NvLYq+b+kDjyyk30J0JY47DIn8z1MO3K0oSl9fZoRmZPQI4Hklzg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.9.tgz", + "integrity": "sha512-9jNJl6FqaUG+COdQMjSCGW4QiMHH88xWbvZ+kRVblZsWrkXlABuGdFJ1E9L7HK+T0Yqd4akKNa/lO0+jDxQD4Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.9.tgz", + "integrity": "sha512-RLLdkflmqRG8KanPGOU7Rpg829ZHu8nFy5Pqdi9U01VYtG9Y0zOG6Vr2z4/S+/3zIyOxiK6cCeYNWOFR9QP87g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.9.tgz", + "integrity": "sha512-YaFBlPGeDasft5IIM+CQAhJAqS3St3nJzDEgsgFixcfZeyGPCd6eJBWzke5piZuZ7CtL656eOSYKk4Ls2C0FRQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.9.tgz", + "integrity": "sha512-1MkgTCuvMGWuqVtAvkpkXFmtL8XhWy+j4jaSO2wxfJtilVCi0ZE37b8uOdMItIHz4I6z1bWWtEX4CJwcKYLcuA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.9.tgz", + "integrity": "sha512-4Xd0xNiMVXKh6Fa7HEJQbrpP3m3DDn43jKxMjxLLRjWnRsfxjORYJlXPO4JNcXtOyfajXorRKY9NkOpTHptErg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.9.tgz", + "integrity": "sha512-WjH4s6hzo00nNezhp3wFIAfmGZ8U7KtrJNlFMRKxiI9mxEK1scOMAaa9i4crUtu+tBr+0IN6JCuAcSBJZfnphw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.9.tgz", + "integrity": "sha512-mGFrVJHmZiRqmP8xFOc6b84/7xa5y5YvR1x8djzXpJBSv/UsNK6aqec+6JDjConTgvvQefdGhFDAs2DLAds6gQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.9.tgz", + "integrity": "sha512-b33gLVU2k11nVx1OhX3C8QQP6UHQK4ZtN56oFWvVXvz2VkDoe6fbG8TOgHFxEvqeqohmRnIHe5A1+HADk4OQww==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.9.tgz", + "integrity": "sha512-PPOl1mi6lpLNQxnGoyAfschAodRFYXJ+9fs6WHXz7CSWKbOqiMZsubC+BQsVKuul+3vKLuwTHsS2c2y9EoKwxQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/esbuild": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.9.tgz", + "integrity": "sha512-CRbODhYyQx3qp7ZEwzxOk4JBqmD/seJrzPa/cGjY1VtIn5E09Oi9/dB4JwctnfZ8Q8iT7rioVv5k/FNT/uf54g==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.9", + "@esbuild/android-arm": "0.25.9", + "@esbuild/android-arm64": "0.25.9", + "@esbuild/android-x64": "0.25.9", + "@esbuild/darwin-arm64": "0.25.9", + "@esbuild/darwin-x64": "0.25.9", + "@esbuild/freebsd-arm64": "0.25.9", + "@esbuild/freebsd-x64": "0.25.9", + "@esbuild/linux-arm": "0.25.9", + "@esbuild/linux-arm64": "0.25.9", + "@esbuild/linux-ia32": "0.25.9", + "@esbuild/linux-loong64": "0.25.9", + "@esbuild/linux-mips64el": "0.25.9", + "@esbuild/linux-ppc64": "0.25.9", + "@esbuild/linux-riscv64": "0.25.9", + "@esbuild/linux-s390x": "0.25.9", + "@esbuild/linux-x64": "0.25.9", + "@esbuild/netbsd-arm64": "0.25.9", + "@esbuild/netbsd-x64": "0.25.9", + "@esbuild/openbsd-arm64": "0.25.9", + "@esbuild/openbsd-x64": "0.25.9", + "@esbuild/openharmony-arm64": "0.25.9", + "@esbuild/sunos-x64": "0.25.9", + "@esbuild/win32-arm64": "0.25.9", + "@esbuild/win32-ia32": "0.25.9", + "@esbuild/win32-x64": "0.25.9" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 000000000..b141f70b2 --- /dev/null +++ b/package.json @@ -0,0 +1,13 @@ +{ + "name": "dashpay-docs-platform", + "private": true, + "scripts": { + "build:sdk": "esbuild scripts/evo-sdk-entry.js --bundle --format=esm --platform=browser --target=es2022 --minify --outfile=_static/vendor/evo-sdk.js" + }, + "dependencies": { + "@dashevo/evo-sdk": "4.1.1" + }, + "devDependencies": { + "esbuild": "0.25.9" + } +} diff --git a/scripts/evo-sdk-entry.js b/scripts/evo-sdk-entry.js new file mode 100644 index 000000000..cd2b19943 --- /dev/null +++ b/scripts/evo-sdk-entry.js @@ -0,0 +1,3 @@ +// Keep the browser-facing surface deliberately small. esbuild follows and +// bundles the SDK's transitive imports into the generated static asset. +export { EvoSDK } from '@dashevo/evo-sdk';