From 1ccbbb61c15b14e8e937574e068a24b9454e2784 Mon Sep 17 00:00:00 2001 From: isaacCodes1 Date: Mon, 1 Jun 2026 21:15:52 +0100 Subject: [PATCH] fix: add runtime compatibility audit --- .github/workflows/ci.yml | 15 +++++++- COMPAT.md | 23 ++++++++++++ docs/running-on-the-edge.md | 56 +++++++++++++++++++++++++++++ src/chains/stellar/announcements.ts | 25 ++++++++----- 4 files changed, 110 insertions(+), 9 deletions(-) create mode 100644 COMPAT.md create mode 100644 docs/running-on-the-edge.md diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a3d24b1..07d690f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -7,6 +7,9 @@ on: jobs: test: runs-on: ubuntu-latest + strategy: + matrix: + node-version: [20, 22] steps: - uses: actions/checkout@v4 - uses: pnpm/action-setup@v4 @@ -14,9 +17,19 @@ jobs: version: 10 - uses: actions/setup-node@v4 with: - node-version: 22 + node-version: ${{ matrix.node-version }} cache: pnpm - run: pnpm install --frozen-lockfile - run: pnpm run format:check - run: pnpm build - run: pnpm test + bun: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: oven-sh/setup-bun@v2 + with: + bun-version: latest + - run: bun install --frozen-lockfile + - run: bun test + - run: bun run build diff --git a/COMPAT.md b/COMPAT.md new file mode 100644 index 0000000..f6bb356 --- /dev/null +++ b/COMPAT.md @@ -0,0 +1,23 @@ +# Compatibility Matrix + +This file tracks the SDK's runtime support across the environments called out in issue #23. + +| Runtime | Status | Notes | +| ---------------------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Node 20 | Working | Verified with the full `vitest` suite and build output in CI. | +| Node 22 | Working | Verified locally and in CI. This is the current CI baseline. | +| Bun latest | Working | Verified locally with `bun test` and `bun run build`. | +| Deno latest | Partial | Use the npm specifier form, for example `import { deriveStealthKeys } from "npm:@wraith-protocol/sdk@1.x/chains/stellar";`. The pure crypto modules are ESM-friendly; the Stellar announcement parser now lazy-loads `@stellar/stellar-sdk` instead of using `require()`. | +| Cloudflare Workers / workerd | Partial | The SDK itself is fetch-first and Web Crypto-friendly. The same Stellar lazy-import fix applies here. If you use the optional Stellar or Solana peer dependencies, bundle them explicitly. | +| Vercel Edge | Partial | Same story as Workers: the core SDK works with standard Web APIs, while optional peer deps need to be bundled or avoided. | + +## Verified Fixes + +- Stellar announcement parsing no longer uses `require()` inside an ESM module. +- The Stellar announcement parser now loads `@stellar/stellar-sdk` lazily with `import()`. +- The test suite now includes a regression test for that code path. + +## Remaining Caveats + +- Deno and Workers support is documented from static review plus the Bun/Node verification we can run in this environment. +- If you need `fetchAnnouncements()` for Stellar on an edge runtime, make sure `@stellar/stellar-sdk` is available to the bundler or runtime. diff --git a/docs/running-on-the-edge.md b/docs/running-on-the-edge.md new file mode 100644 index 0000000..fd16886 --- /dev/null +++ b/docs/running-on-the-edge.md @@ -0,0 +1,56 @@ +# Running Wraith On The Edge + +Wraith is designed around ESM, `fetch`, `TextEncoder`, `TextDecoder`, and Web Crypto. That makes the core SDK a good fit for edge runtimes such as Bun, Deno, Cloudflare Workers, and Vercel Edge. + +## Bun + +Install and run the repository tests with Bun: + +```bash +bun install +bun test +bun run build +``` + +If you only need the package in an app, import the same entry points you would use in Node: + +```ts +import { deriveStealthKeys } from '@wraith-protocol/sdk/chains/stellar'; +``` + +## Deno + +Use npm specifiers when importing the published package: + +```ts +import { deriveStealthKeys } from 'npm:@wraith-protocol/sdk@1.x/chains/stellar'; +``` + +If you need the Stellar announcement parser, make sure the optional `@stellar/stellar-sdk` dependency is available to the runtime or bundler. + +## Cloudflare Workers + +Workers can consume the ESM entry points directly. The SDK does not require `Buffer` or `fs`, and the Stellar parser now uses dynamic `import()` instead of `require()`. + +Minimal worker example: + +```ts +import { deriveStealthKeys } from '@wraith-protocol/sdk/chains/stellar'; + +export default { + async fetch() { + const keys = deriveStealthKeys(new Uint8Array(64).fill(0xaa)); + return Response.json({ spendingKeyLength: keys.spendingKey.length }); + }, +}; +``` + +## Vercel Edge + +Use the same import style as Workers. If you are only scanning/generating addresses, the pure crypto exports are the safest path. Optional peer dependencies such as `@stellar/stellar-sdk` and `@solana/web3.js` should be bundled explicitly if you use those chain modules. + +## What Changed For Edge Support + +- The Stellar announcement parser now lazy-loads `@stellar/stellar-sdk`. +- The pure chain modules are ESM-only and avoid Node-specific APIs. +- Bun is covered in CI so regressions show up before release. diff --git a/src/chains/stellar/announcements.ts b/src/chains/stellar/announcements.ts index 7af0a00..bf03ddf 100644 --- a/src/chains/stellar/announcements.ts +++ b/src/chains/stellar/announcements.ts @@ -2,6 +2,13 @@ import type { Announcement } from './types'; import { bytesToHex } from './utils'; import { getDeployment } from './deployments'; +let stellarSdkPromise: Promise | undefined; + +function loadStellarSdk(): Promise { + stellarSdkPromise ??= import('@stellar/stellar-sdk'); + return stellarSdkPromise; +} + export interface FetchAnnouncementsOptions { /** Earliest ledger to include, inclusive. Ignored when cursor is provided. */ fromLedger?: number; @@ -154,7 +161,7 @@ export async function fetchAnnouncements( hasMore = false; continue; } - const ann = parseAnnouncementEvent(event); + const ann = await parseAnnouncementEvent(event); if (ann) all.push(ann); } @@ -253,14 +260,11 @@ function parseLedgerRange(message: string): { oldest: number; latest: number } | }; } -function eventLedger(event: Record): number | undefined { - const ledger = event.ledger; - return typeof ledger === 'number' ? ledger : undefined; -} - -function parseAnnouncementEvent(event: Record): Announcement | null { +async function parseAnnouncementEvent( + event: Record, +): Promise { try { - const { xdr, Address } = require('@stellar/stellar-sdk'); + const { xdr, Address } = await loadStellarSdk(); const topics = event.topic as string[]; if (!topics || topics.length < 3) return null; @@ -289,3 +293,8 @@ function parseAnnouncementEvent(event: Record): Announcement | return null; } } + +function eventLedger(event: Record): number | undefined { + const ledger = event.ledger; + return typeof ledger === 'number' ? ledger : undefined; +}