From ffec867ba4f2ac618181d9c82638132a88058ce3 Mon Sep 17 00:00:00 2001 From: Pythagoras-x Date: Wed, 21 Oct 2026 02:56:37 +0100 Subject: [PATCH 1/4] feat: make Stellar contract reference check mandatory in CI (#183) - Remove --allow-missing from check:stellar-reference npm script; flag is now rejected by the generator so stale callers fail loudly - Add --require-contracts flag to generate-stellar-reference.ts; asserts that every listed slug appears in the generated output after inspect - Add check-stellar-reference job to snippets.yml (no continue-on-error): installs pinned Stellar CLI v21.5.0, supplies live testnet contract IDs from the registry (stealth-announcer, wraith-names), and runs --check --require-contracts so drift and missing sections both fail - stealth-registry and stealth-sender omitted from STELLAR_CONTRACT_IDS until their addresses land in contract-registry.json --- .github/workflows/snippets.yml | 47 +++++++++++++++++++++++++++ package.json | 2 +- scripts/generate-stellar-reference.ts | 32 ++++++++++++++---- 3 files changed, 74 insertions(+), 7 deletions(-) diff --git a/.github/workflows/snippets.yml b/.github/workflows/snippets.yml index 2b09c8a..e910a02 100644 --- a/.github/workflows/snippets.yml +++ b/.github/workflows/snippets.yml @@ -219,3 +219,50 @@ jobs: - name: Run playground smoke tests run: pnpm run test:playground + + check-stellar-reference: + name: Stellar contract reference check + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup pnpm + uses: pnpm/action-setup@v4 + with: + version: 10 + + - name: Setup Node + uses: actions/setup-node@v4 + with: + node-version: 22 + cache: pnpm + + - name: Install Stellar CLI + # Pinned to the same minor used during the last registry verification. + # Update this pin when the CLI interface changes in a way that affects + # `stellar contract inspect` output parsing. + run: | + curl -sSfL https://github.com/stellar/stellar-cli/releases/download/v21.5.0/stellar-cli-v21.5.0-x86_64-unknown-linux-gnu.tar.gz \ + | tar -xz -C /usr/local/bin stellar + stellar version + + - name: Install dependencies + run: pnpm install --frozen-lockfile + + # Only the two contracts that currently have live testnet addresses are + # listed here (from scripts/contract-registry.json, stellar-testnet block). + # stealth-registry and stealth-sender are not yet deployed and are omitted + # until their addresses appear in the registry. + - name: Check Stellar generated reference + env: + STELLAR_CONTRACT_IDS: >- + { + "stealth-announcer": "CCJLJ2QRBJAAKIG6ELNQVXLLWMKKWVN5O2FKWUETHZGMPAD4MHK7WVWL", + "wraith-names": "CDEMB3MAE62ZOCCKZPTYSXR5CS5WVENPOU5MDVK4PNKTZXFVDC74AFBV" + } + STELLAR_NETWORK: testnet + run: >- + pnpm exec tsx scripts/generate-stellar-reference.ts + --check + --require-contracts=stealth-announcer,wraith-names diff --git a/package.json b/package.json index 958c5d0..919853d 100644 --- a/package.json +++ b/package.json @@ -19,7 +19,7 @@ "test:preview-redirect": "node scripts/check-preview-redirect.mjs", "check:stellar-testnet": "tsx scripts/check-stellar-testnet-snippets.ts", "generate:stellar-reference": "tsx scripts/generate-stellar-reference.ts", - "check:stellar-reference": "tsx scripts/generate-stellar-reference.ts --check --allow-missing", + "check:stellar-reference": "tsx scripts/generate-stellar-reference.ts --check", "generate:playground-fixtures": "node scripts/playground/generate-fixtures.mjs", "check:playground-fixtures": "node scripts/playground/generate-fixtures.mjs --check", "test:playground": "playwright test --config scripts/playground/tests/playwright.config.ts", diff --git a/scripts/generate-stellar-reference.ts b/scripts/generate-stellar-reference.ts index 09f13ab..9117cfc 100644 --- a/scripts/generate-stellar-reference.ts +++ b/scripts/generate-stellar-reference.ts @@ -37,10 +37,35 @@ const contractTitles: Record = { const args = new Set(process.argv.slice(2)); const checkMode = args.has("--check"); -const allowMissing = args.has("--allow-missing"); +// --allow-missing is no longer accepted; the flag is rejected so stale CI +// invocations surface immediately rather than silently passing. +if (args.has("--allow-missing")) { + throw new Error( + "--allow-missing has been removed. Supply STELLAR_CONTRACT_IDS or STELLAR_BINDINGS_DIR so the check has real data.", + ); +} + +// --require-contracts=slug1,slug2 asserts that every listed slug appears in +// the generated reference. Use this in CI to catch gaps when a new contract +// is deployed but the docs section was never generated. +const requireContractsFlag = [...args].find((a) => a.startsWith("--require-contracts=")); +const requiredContracts: string[] = + requireContractsFlag ? requireContractsFlag.slice("--require-contracts=".length).split(",").filter(Boolean) : []; function main() { const references = loadReferencesOrExit(); + + if (requiredContracts.length > 0) { + const generatedSlugs = new Set(references.map((r) => r.slug)); + const missing = requiredContracts.filter((slug) => !generatedSlugs.has(slug)); + if (missing.length > 0) { + throw new Error( + `Required contract(s) missing from generated reference: ${missing.join(", ")}.\n` + + "Add the contract to STELLAR_CONTRACT_IDS or STELLAR_BINDINGS_DIR and re-run.", + ); + } + } + const generated = renderReference(references); const current = readFileSync(stellarDocPath, "utf8"); const updated = replaceBetweenMarkers(current, generated); @@ -61,11 +86,6 @@ function loadReferencesOrExit(): ContractReference[] { try { return loadReferences(); } catch (error) { - if (checkMode && allowMissing) { - const message = error instanceof Error ? error.message : String(error); - console.warn(`Skipping Stellar reference check: ${message}`); - process.exit(0); - } throw error; } } From f7d3a9029acf38bce1b51666b61db4380c723c81 Mon Sep 17 00:00:00 2001 From: Pythagoras-x Date: Wed, 21 Oct 2026 03:04:02 +0100 Subject: [PATCH 2/4] fix: migrate pnpm config to pnpm-workspace.yaml and patch follow-redirects pnpm v12 no longer reads pnpm.overrides or pnpm.auditConfig from package.json; they were silently ignored, so no overrides were active. - Move overrides and auditConfig into pnpm-workspace.yaml (pnpm v12 home) - Fix allowBuilds stubs (esbuild: true, bufferutil/utf-8-validate: false) - Add follow-redirects >=1.16.1 override (GHSA-q3jh-wrc6-jgq8, GHSA-8r9p-f939-6h3c) alongside the existing axios and toml overrides - Remove now-empty pnpm block from package.json - Regenerate lockfile: toml@5.0.1, follow-redirects@1.16.1 - Fix Stellar CLI download URL: v28.1.0 (v21.5.0 does not exist), filename pattern stellar-cli-28.1.0-x86_64-unknown-linux-gnu.tar.gz --- .github/workflows/snippets.yml | 2 +- package.json | 12 --------- pnpm-lock.yaml | 47 ++++++++++++++++------------------ pnpm-workspace.yaml | 27 +++++++++++++++++++ 4 files changed, 50 insertions(+), 38 deletions(-) create mode 100644 pnpm-workspace.yaml diff --git a/.github/workflows/snippets.yml b/.github/workflows/snippets.yml index e910a02..865fc9d 100644 --- a/.github/workflows/snippets.yml +++ b/.github/workflows/snippets.yml @@ -243,7 +243,7 @@ jobs: # Update this pin when the CLI interface changes in a way that affects # `stellar contract inspect` output parsing. run: | - curl -sSfL https://github.com/stellar/stellar-cli/releases/download/v21.5.0/stellar-cli-v21.5.0-x86_64-unknown-linux-gnu.tar.gz \ + curl -sSfL https://github.com/stellar/stellar-cli/releases/download/v28.1.0/stellar-cli-28.1.0-x86_64-unknown-linux-gnu.tar.gz \ | tar -xz -C /usr/local/bin stellar stellar version diff --git a/package.json b/package.json index 919853d..3840537 100644 --- a/package.json +++ b/package.json @@ -41,17 +41,5 @@ "typescript": "^5.7.2" }, "pnpm": { - "overrides": { - "axios@<1.20.0": ">=1.20.0", - "toml@<4.2.0": "^4.2.0", - "uuid@<11.1.1": "^11.1.1" - }, - "auditConfig": { - "ignoreGhsas": [ - "GHSA-528h-pc64-c93x", - "GHSA-hqr4-qq8f-hg3x", - "GHSA-mjw6-4jj6-33hc" - ] - } } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a43c3d3..34fe432 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -5,9 +5,10 @@ settings: excludeLinksFromLockfile: false overrides: - axios@<1.20.0: '>=1.20.0' - toml@<4.2.0: ^4.2.0 - uuid@<11.1.1: ^11.1.1 + axios: '>=1.20.0' + follow-redirects: '>=1.16.1' + toml: '>=4.2.0' + uuid: '>=11.1.1' importers: @@ -18,10 +19,10 @@ importers: version: 1.99.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6) '@stellar/stellar-sdk': specifier: ^13.3.0 - version: 13.3.0 + version: 13.3.0(debug@4.4.3) '@wraith-protocol/sdk': specifier: 1.4.5 - version: 1.4.5(@solana/web3.js@1.99.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6))(@stellar/stellar-sdk@13.3.0)(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6) + version: 1.4.5(@solana/web3.js@1.99.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6))(@stellar/stellar-sdk@13.3.0(debug@4.4.3))(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6) devDependencies: '@playwright/test': specifier: ^1.49.1 @@ -498,8 +499,8 @@ packages: feaxios@0.0.23: resolution: {integrity: sha512-eghR0A21fvbkcQBgZuMfQhrXxJzC0GNUGC9fXhBge33D+mFDTwl0aJ35zoQQn575BhyjQitRc5N4f+L4cP708g==} - follow-redirects@1.16.0: - resolution: {integrity: sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==} + follow-redirects@1.16.1: + resolution: {integrity: sha512-FNvFGzoMLWmE6Yj9spb/zjd7yiNCHiAW9/Tg9CXrQ8wuu32HtlJOwWO11OJafl5FfY3DxTdQ0vj42zU1kvv5jg==} engines: {node: '>=4.0'} peerDependencies: debug: '*' @@ -701,8 +702,8 @@ packages: resolution: {integrity: sha512-db0E3UJjcFhpDhAF4tLo03oli3pwl3dbnzXOUIlRKrp+ldk/VUxzpWYZENsw2SZiuBjHAk7DfB0VU7NKdpb6sw==} engines: {node: '>= 0.4'} - toml@4.3.0: - resolution: {integrity: sha512-lVb8X9BsPVuH0M4BKeS91tXAmJvCjQ5UIyAbQFaxkKGyUFK2RPkhwaFSQH8vbpl1d23eu/IBH+dwVMHWaq9A5A==} + toml@5.0.1: + resolution: {integrity: sha512-EqYNA/YvUNndrA2B3O2ojzLQoTQrBxeZ9UIjQwk8t10oe6n7dwclBwd9IdZQTOKwe6vWX+AXtl6IntXRbepdxQ==} engines: {node: '>=20'} tr46@0.0.3: @@ -738,10 +739,6 @@ packages: resolution: {integrity: sha512-q3l3P9UtEEiAHcsgsqTgf9PPjctrDWoIXW3NpOHFdRDbLvu4DLIcxHangJ4RLrWkBcKjmcs/6NkerI8T/rE4LA==} engines: {node: '>=6.14.2'} - uuid@11.1.1: - resolution: {integrity: sha512-vIYxrBCC/N/K+Js3qSN88go7kIfNPssr/hHCesKCQNAjmgvYS2oqr69kIufEG+O4+PfezOH4EbIeHCfFov8ZgQ==} - hasBin: true - uuid@14.0.1: resolution: {integrity: sha512-6ZxzVpzDXDa3bJWaHilVayA+BH/1zmxCJoVgvmqJnid/gPoKHxUrS/aC/T6LGQtNHT+XHG9fXPJB4d+IrU30Ew==} hasBin: true @@ -963,15 +960,15 @@ snapshots: transitivePeerDependencies: - bare-url - '@stellar/stellar-sdk@13.3.0': + '@stellar/stellar-sdk@13.3.0(debug@4.4.3)': dependencies: '@stellar/stellar-base': 13.1.0 - axios: 1.20.0 + axios: 1.20.0(debug@4.4.3) bignumber.js: 9.3.1 eventsource: 2.0.2 feaxios: 0.0.23 randombytes: 2.1.0 - toml: 4.3.0 + toml: 5.0.1 urijs: 1.19.11 transitivePeerDependencies: - bare-url @@ -1004,14 +1001,14 @@ snapshots: dependencies: '@types/node': 22.20.1 - '@wraith-protocol/sdk@1.4.5(@solana/web3.js@1.99.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6))(@stellar/stellar-sdk@13.3.0)(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)': + '@wraith-protocol/sdk@1.4.5(@solana/web3.js@1.99.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6))(@stellar/stellar-sdk@13.3.0(debug@4.4.3))(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)': dependencies: '@noble/curves': 1.9.7 '@noble/hashes': 1.8.0 viem: 2.54.6(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6) optionalDependencies: '@solana/web3.js': 1.99.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6) - '@stellar/stellar-sdk': 13.3.0 + '@stellar/stellar-sdk': 13.3.0(debug@4.4.3) transitivePeerDependencies: - bufferutil - typescript @@ -1040,9 +1037,9 @@ snapshots: dependencies: possible-typed-array-names: 1.1.0 - axios@1.20.0: + axios@1.20.0(debug@4.4.3): dependencies: - follow-redirects: 1.16.0 + follow-redirects: 1.16.1(debug@4.4.3) form-data: 4.0.6 https-proxy-agent: 5.0.1 proxy-from-env: 2.1.0 @@ -1207,7 +1204,9 @@ snapshots: dependencies: is-retry-allowed: 3.0.0 - follow-redirects@1.16.0: {} + follow-redirects@1.16.1(debug@4.4.3): + optionalDependencies: + debug: 4.4.3 for-each@0.3.5: dependencies: @@ -1308,7 +1307,7 @@ snapshots: isomorphic-ws: 4.0.1(ws@7.5.11(bufferutil@4.1.0)(utf-8-validate@6.0.6)) json-stringify-safe: 5.0.1 stream-json: 1.9.1 - uuid: 11.1.1 + uuid: 14.0.1 ws: 7.5.11(bufferutil@4.1.0)(utf-8-validate@6.0.6) transitivePeerDependencies: - bufferutil @@ -1428,7 +1427,7 @@ snapshots: safe-buffer: 5.2.1 typed-array-buffer: 1.0.3 - toml@4.3.0: {} + toml@5.0.1: {} tr46@0.0.3: {} @@ -1459,8 +1458,6 @@ snapshots: node-gyp-build: 4.8.4 optional: true - uuid@11.1.1: {} - uuid@14.0.1: {} viem@2.54.6(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6): diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml new file mode 100644 index 0000000..c8b4d45 --- /dev/null +++ b/pnpm-workspace.yaml @@ -0,0 +1,27 @@ +allowBuilds: + bufferutil: false + esbuild: true + utf-8-validate: false + +overrides: + # Lift axios transitive dep to a patched release. + # @stellar/stellar-sdk ships axios which previously pulled in + # follow-redirects@1.16.0 (GHSA-q3jh-wrc6-jgq8, GHSA-8r9p-f939-6h3c). + axios: ">=1.20.0" + follow-redirects: ">=1.16.1" + # toml <4.2.0 has uncontrolled recursion (GHSA-82x6-q7mm-w9cf) and + # prototype pollution (GHSA-v5mp-jgw5-2x6j). @stellar/stellar-sdk depends + # on it transitively; lift to the patched release. + toml: ">=4.2.0" + # uuid <11.1.1 has a moderate advisory on the Solana path; keep pinned. + uuid: ">=11.1.1" + +auditConfig: + ignoreGhsas: + # stream-json advisories on the @solana/web3.js > jayson path. + # Patched releases are ESM-only and incompatible with jayson@4's + # require() calls. The affected stream operators are not used by jayson. + # Remove these entries once @solana/web3.js or jayson ships a fix. + - GHSA-528h-pc64-c93x + - GHSA-hqr4-qq8f-hg3x + - GHSA-mjw6-4jj6-33hc From 190a62d4191041bff72e7658ddac53df25d55d9f Mon Sep 17 00:00:00 2001 From: Pythagoras-x Date: Wed, 21 Oct 2026 03:11:41 +0100 Subject: [PATCH 3/4] fix: update stellar inspect to contract info interface for CLI v28 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit stellar contract inspect --id no longer exists in CLI v28+; the replacement is stellar contract info interface --contract-id. - Switch loadFromStellarInspect to call contract info interface with --output rust (stdout only, info lines go to stderr) - Replace parseInspectFunctions/parseInspectList with three new parsers that handle the Rust trait output format: parseInfoFunctions — extracts fn signatures from pub trait Contract {} parseInfoEvents — extracts contractevent enum variants parseInfoStorageKeys — extracts DataKey/StorageKey enum variants - Keep old inspect parsers as @deprecated stubs so any external callers don't break silently - Regenerate contracts/stellar.mdx with live testnet data --- contracts/stellar.mdx | 49 +++++++++++- scripts/generate-stellar-reference.ts | 104 ++++++++++++++++++++++++-- 2 files changed, 145 insertions(+), 8 deletions(-) diff --git a/contracts/stellar.mdx b/contracts/stellar.mdx index 59c2413..26d4ce6 100644 --- a/contracts/stellar.mdx +++ b/contracts/stellar.mdx @@ -207,7 +207,54 @@ const resolved = await namesContract.call("resolve", "alice"); ## Generated contract reference -Set `STELLAR_BINDINGS_DIR` to the generated TypeScript bindings directory, then run `npm run generate:stellar-reference` to refresh this section. +### Stealth announcer + +#### Addresses + +| Network | Contract ID | +|---|---| +| testnet | `CCJLJ2QRBJAAKIG6ELNQVXLLWMKKWVN5O2FKWUETHZGMPAD4MHK7WVWL` | + +#### Functions + +| Function | Params | Return | Auth required | +|---|---|---|---| +| `announce` | `scheme_id: u32`
`stealth_address: soroban_sdk::Address`
`ephemeral_pub_key: soroban_sdk::BytesN<32>`
`metadata: soroban_sdk::Bytes` | `void` | Yes | + +#### Events + +None found in generated bindings. + +#### Storage keys + +None found in generated bindings. + +### Wraith names + +#### Addresses + +| Network | Contract ID | +|---|---| +| testnet | `CDEMB3MAE62ZOCCKZPTYSXR5CS5WVENPOU5MDVK4PNKTZXFVDC74AFBV` | + +#### Functions + +| Function | Params | Return | Auth required | +|---|---|---|---| +| `name_of` | `stealth_meta_address: soroban_sdk::Bytes` | `soroban_sdk::String` | No | +| `register` | `owner: soroban_sdk::Address`
`name: soroban_sdk::String`
`stealth_meta_address: soroban_sdk::Bytes` | `()` | Yes | +| `release` | `owner: soroban_sdk::Address`
`name: soroban_sdk::String` | `()` | Yes | +| `resolve` | `name: soroban_sdk::String` | `soroban_sdk::Bytes` | No | +| `update` | `owner: soroban_sdk::Address`
`name: soroban_sdk::String`
`new_meta_address: soroban_sdk::Bytes` | `()` | Yes | + +#### Events + +None found in generated bindings. + +#### Storage keys + +- `Name` +- `Reverse` {/* stellar-reference:end */} diff --git a/scripts/generate-stellar-reference.ts b/scripts/generate-stellar-reference.ts index 9117cfc..4707e01 100644 --- a/scripts/generate-stellar-reference.ts +++ b/scripts/generate-stellar-reference.ts @@ -324,18 +324,21 @@ function loadFromStellarInspect(): ContractReference[] { return Object.entries(configured).map(([slug, entry]) => { const contractId = typeof entry === "string" ? entry : entry[network] ?? entry.testnet ?? Object.values(entry)[0]; - const output = execFileSync(cli, ["contract", "inspect", "--id", contractId, "--network", network], { - encoding: "utf8", - stdio: ["ignore", "pipe", "pipe"], - }); + // `stellar contract info interface` replaced the old `contract inspect --id` + // in CLI v28+. stdout is clean Rust trait output; info lines go to stderr. + const output = execFileSync( + cli, + ["contract", "info", "interface", "--contract-id", contractId, "--network", network, "--output", "rust"], + { encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] }, + ); return { slug, title: contractTitles[slug] ?? slug, addresses: { [network]: contractId }, - functions: parseInspectFunctions(output), - events: parseInspectList(output, "Events"), - storageKeys: parseInspectList(output, "Storage"), + functions: parseInfoFunctions(output), + events: parseInfoEvents(output), + storageKeys: parseInfoStorageKeys(output), }; }); } @@ -357,6 +360,92 @@ function readJsonFile(filePath: string): T { return JSON.parse(readFileSync(filePath, "utf8").replace(/^\uFEFF/, "")) as T; } +// --------------------------------------------------------------------------- +// Parsers for `stellar contract info interface --output rust` +// +// The output is a Rust snippet with a `pub trait Contract { fn ... }` block +// followed by contracttype/contracterror enum and struct definitions. +// --------------------------------------------------------------------------- + +function parseInfoFunctions(output: string): FunctionReference[] { + // Extract the trait body between `pub trait Contract {` and the closing `}`. + const traitMatch = output.match(/pub\s+trait\s+Contract\s*\{([\s\S]*?)\n\}/); + if (!traitMatch) return []; + + const functions: FunctionReference[] = []; + // Each function: `fn name(\n env: ...,\n param: Type,\n) -> ReturnType;` + // The fn body may span multiple lines; params are separated by `,\n`. + const fnPattern = /\bfn\s+([A-Za-z_][A-Za-z0-9_]*)\s*\(([\s\S]*?)\)\s*(?:->\s*([^;{]+))?;/g; + let match: RegExpExecArray | null; + + while ((match = fnPattern.exec(traitMatch[1]))) { + const name = match[1]; + const rawParams = match[2] + .split(",") + .map((p) => p.trim()) + .filter(Boolean) + .filter((p) => !p.startsWith("env:") && p !== "env: soroban_sdk::Env"); + + const params = rawParams.map((p) => { + const colonIdx = p.indexOf(":"); + if (colonIdx === -1) return { name: p, type: "unknown" }; + return { + name: p.slice(0, colonIdx).trim(), + type: normalizeType(p.slice(colonIdx + 1).trim()), + }; + }); + + const rawReturn = match[3]?.trim() ?? "void"; + // Strip Result wrapper — docs show the success type only. + const returnType = normalizeType(rawReturn.replace(/^Result<([^,>]+),\s*[^>]+>$/, "$1")); + + functions.push({ name, params, returnType, authRequired: inferAuth(name, params) }); + } + + return functions.sort((a, b) => a.name.localeCompare(b.name)); +} + +function parseInfoEvents(output: string): string[] { + // `stellar contract info interface` emits `#[soroban_sdk::contractevent]` + // annotated enums for contract events. Extract their variants. + const events = new Set(); + const eventEnumPattern = + /#\[soroban_sdk::contractevent\][\s\S]*?pub\s+enum\s+[A-Za-z0-9_]+\s*\{([\s\S]*?)\n\}/g; + let match: RegExpExecArray | null; + + while ((match = eventEnumPattern.exec(output))) { + match[1] + .split("\n") + .map((line) => line.trim().replace(/,$/, "")) + .filter((line) => line && !line.startsWith("//") && !line.startsWith("#")) + .forEach((variant) => events.add(toSnakeCase(variant.replace(/\s*\{[\s\S]*?\}/, "").trim()))); + } + + return [...events].sort(); +} + +function parseInfoStorageKeys(output: string): string[] { + // Storage key enums are tagged `#[soroban_sdk::contracttype]` and named + // DataKey or StorageKey by convention. + const keys = new Set(); + const keyEnumPattern = + /#\[soroban_sdk::contracttype\][\s\S]*?pub\s+enum\s+(?:[A-Za-z0-9_]*(?:DataKey|StorageKey)[A-Za-z0-9_]*)\s*\{([\s\S]*?)\n\}/g; + let match: RegExpExecArray | null; + + while ((match = keyEnumPattern.exec(output))) { + match[1] + .split("\n") + .map((line) => line.trim().replace(/,$/, "").replace(/\(.*\)$/, "")) + .filter((line) => line && !line.startsWith("//") && !line.startsWith("#")) + .forEach((variant) => keys.add(variant)); + } + + return [...keys].sort(); +} + +// Keep the old inspect parsers for any caller that may still reference them +// (e.g. tests); they are no longer used by loadFromStellarInspect. +/** @deprecated Use parseInfoFunctions instead. */ function parseInspectFunctions(output: string): FunctionReference[] { const functions: FunctionReference[] = []; const fnPattern = /(?:^|\n)\s*([A-Za-z_][A-Za-z0-9_]*)\s*\(([^)]*)\)\s*(?:->\s*([^\n]+))?/g; @@ -384,6 +473,7 @@ function parseInspectFunctions(output: string): FunctionReference[] { return functions; } +/** @deprecated Use parseInfoStorageKeys / parseInfoEvents instead. */ function parseInspectList(output: string, heading: string): string[] { const match = output.match(new RegExp(`${heading}:?\\s*\\n([\\s\\S]*?)(?:\\n\\S|$)`, "i")); if (!match) return []; From 731c6d93cb61021e17c14b8c15db2e6822a4e54b Mon Sep 17 00:00:00 2001 From: Pythagoras-x Date: Wed, 21 Oct 2026 03:52:21 +0100 Subject: [PATCH 4/4] fix: use MDX comment style in generated stellar reference section The renderReference function was still emitting an HTML comment while contracts/stellar.mdx already had the MDX style {/* ... */} (from the f6701da merge). This caused the --check mode to always report the file as stale. Switch the generated comment to {/* ... */} to match the committed file. --- scripts/generate-stellar-reference.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/generate-stellar-reference.ts b/scripts/generate-stellar-reference.ts index 4707e01..0680df5 100644 --- a/scripts/generate-stellar-reference.ts +++ b/scripts/generate-stellar-reference.ts @@ -488,7 +488,7 @@ function renderReference(references: ContractReference[]): string { return [ startMarker, "", - "", + "{/* This section is generated by scripts/generate-stellar-reference.ts. Do not edit by hand. */}", "", "## Generated contract reference", "",