Pswap Tracking [WIP] - #3
Open
VAIBHAVJINDAL3012 wants to merge 77 commits into
Open
Conversation
…den#14) * docs: import planning docs from miden-client (web SDK ones) Per @SantiagoPittella's review on miden-client #1992: 5 of the 13 stray planning .md files at the miden-client repo root are web-SDK- relevant — they belong here, not in the Rust client repo. Importing under docs/planning/ with an index README that calls out their snapshot-not-maintained status. AGENTS.md Api.Rework.Impl.md REACT_SDK_PLAN.md SimplifiedAPI.md SimplifiedAPI.sdk.review.md The miden-client side (#1992 commit e20e6261a) deletes all 13 stray docs; the other 8 are either already-tracked issues, stale, or non- client concerns and don't need to be ported. * docs: keep only AGENTS.md from imported planning docs * ci: replace paths-ignore with intra-job filter so docs-only PRs aren't blocked by required checks that never run
…iden#19) * chore: add .nvmrc + lefthook + lint-staged for local dev hygiene * chore: fix lefthook config — drop broken per-file tsc, use pnpm exec, guard prepare
…iden#18) * chore(web-client): swap chai expect for @playwright/test expect * chore(web-client): swap puppeteer Page type for @playwright/test * chore(web-client): drop mocha/chai/puppeteer/esm/ts-node devDeps + .mocharc.json The legacy mocha-based Playwright wiring has been fully replaced by @playwright/test (assertions) and vitest (unit tests). Remove the dead test infrastructure: - crates/web-client/.mocharc.json (deleted) - crates/web-client/tsconfig.json: drop the "ts-node" block - crates/web-client/package.json devDependencies removed: - mocha - chai - puppeteer - esm - ts-node - pnpm-lock.yaml regenerated Verified: pnpm --filter @miden-sdk/miden-sdk run test:unit passes (295 tests across 13 files).
* chore(deps): bump miden-client to 0.14.5
Bumps the workspace miden-client dependency on `main` from 0.14.0 (locked
at 0.14.4) to 0.14.5, the latest 0.14.x patch on crates.io. No API
changes required on the web-sdk side — same minor line, just patch-level
updates from upstream miden-client.
The `next` branch separately tracks miden-client `next` (0.15.0
unreleased) and is unaffected by this bump.
* docs: clarify lazy entry — SSR / controlled WASM init, not 'most users skip crypto'
The previous README framing of `@miden-sdk/miden-sdk/lazy` as for 'apps
where most users never touch crypto' is misleading. The actual point of
the lazy entry is to allow usage in environments that hang on top-level
await (Next.js / SSR, Capacitor WKWebView) or where the caller wants to
explicitly control when the WASM-init cost is paid.
Also adds the missing `await MidenClient.ready()` contract: until you
call it, every wasm-bindgen type imported from the lazy entry is just a
stub that throws on construction. Async SDK methods await internally
and are exempt.
* docs: add React-side lazy guidance — gate UI on isReady from useMiden()
Apps using @miden-sdk/react with the lazy entry don't call
MidenClient.ready() directly; MidenProvider does it for them and
exposes the readiness state through useMiden() as { isReady,
isInitializing, error }. Adds the contract + a minimal example,
plus pointers to the loadingComponent / errorComponent provider
props for the zero-glue case.
* chore(ci): add publint + arethetypeswrong gates for published packages
Adds publint and @arethetypeswrong/cli as root devDeps and wires three
new scripts:
- check:publint - runs publint per published workspace package
- check:attw - runs attw --pack . per published workspace package
- check:publish - builds web-client, react-sdk, vite-plugin then runs
both gates
Filters target the three publishable packages by name
(@miden-sdk/miden-sdk, @miden-sdk/react, @miden-sdk/vite-plugin), which
skips the private web_store workspace member that has no exports map
worth checking.
A new workflow (.github/workflows/check-publish.yml) runs check:publish
on PRs and pushes to main/next. It mirrors the WASM build setup from
test.yml's build-web-client-dist-folder job (Rust toolchain, sccache,
binaryen, Swatinem/rust-cache) and uses MIDEN_FAST_BUILD on the WASM
build since publint/attw inspect package shape and types, not the
optimization level of the WASM blob.
Mode: STRICT. Per the task rule (document only if both tools find > ~3
issues per package; otherwise leave strict), only @miden-sdk/miden-sdk
clearly clears that bar - the other two packages have <= 1 attw issue
and 1 publint warning each, so a doc-and-tolerate path would be
overkill. The CI gate will be red on first PR; the author should decide
whether to fix the underlying export map issues or merge red.
Local results today (built with MIDEN_FAST_BUILD=true):
publint (exit 0 - warnings/suggestions only):
- @miden-sdk/miden-sdk: 1 suggestion (pkg.browser refactor)
- @miden-sdk/react: 1 warning + 2 suggestions
(types ambiguous under "import" condition;
missing "type" field; repository.url shape)
- @miden-sdk/vite-plugin: 1 warning + 2 suggestions (same shape as react)
attw (exit 1 - real failures):
- @miden-sdk/miden-sdk: CJSResolvesToESM, InternalResolutionError
across multiple matrix cells, NoResolution
on the /lazy subpath under node10
- @miden-sdk/react: FalseCJS (masquerading-as-CJS) under node16
from-ESM for both . and /lazy; NoResolution
on /lazy under node10
- @miden-sdk/vite-plugin: FalseCJS under node16 from-ESM
Per task scope, no source or build-config changes were made to address
these - the gate ships first, the fixes are followup work.
* chore(miden-sdk): fix exports map for attw + publint compliance
- Split the `exports` conditions into explicit `import` blocks with
`types` listed first, eliminating the ambiguous-types warning publint
reported. Drop the implicit `default` fallthrough that caused
attw to flag CJSResolvesToESM under the node16-cjs profile.
- Add an `.attw.json` selecting the `esm-only` profile. The package is
`"type": "module"` and ships only ESM artifacts (rollup output +
WASM glue); `require()` consumers must already use a dynamic import.
The `esm-only` profile communicates this intent to attw and makes
the node10 / node16-cjs columns informational only.
- Add a post-build step (`scripts/post-build.js`) that:
1. Rewrites extensionless relative specifiers in the published
`dist/*.d.ts` files (e.g. `from "./api-types"` ->
`from "./api-types.js"`). Without explicit extensions, Node16
type resolution flags an `InternalResolutionError` on every
relative import inside `dist/index.d.ts` and `dist/api-types.d.ts`.
2. Emits a `lazy/package.json` shim at the package root pointing at
`dist/index.{js,d.ts}` so node10 resolution (which doesn't read
`exports`) can still locate `@miden-sdk/miden-sdk/lazy`.
- Add `lazy` to the `files` array so the shim ships in the tarball.
- Expose `./package.json` from `exports` to keep tooling that reads
the manifest at runtime working under strict resolution.
Public import paths (`@miden-sdk/miden-sdk` and
`@miden-sdk/miden-sdk/lazy`) are unchanged. Verified that the
existing vitest unit suite (295 tests) still passes against the new
build output.
* chore(react-sdk): fix exports map for attw + publint compliance
- Split each `exports` subpath into explicit `import` / `require`
conditions with `types` listed first. publint flagged the previous
shape because `types: "./dist/index.d.ts"` resolved as CJS under the
`import` condition (FalseCJS / ambiguous-types). The new shape uses
the `.d.mts` declaration tsup already emits for the ESM build, so
TypeScript sees an `.mts` declaration when resolving via `import`
and a `.d.ts` declaration when resolving via `require`.
- Add `"type": "commonjs"` to silence publint's package-type-detection
suggestion (tsup emits `.js` as CJS and `.mjs` as ESM, matching).
- Add a `lazy/package.json` shim at the package root that points at
`dist/lazy.{js,mjs,d.ts}`. node10 resolution doesn't read the
`exports` map, so `@miden-sdk/react/lazy` previously failed to
resolve under that column; the shim is the standard fix.
- Expose `./package.json` from `exports` and add `lazy` to the `files`
array so the shim ships in the tarball.
Public import paths (`@miden-sdk/react` and `@miden-sdk/react/lazy`)
are unchanged. Verified that the existing vitest unit suite (719
tests) still passes.
* chore(vite-plugin): fix exports map for attw + publint compliance
- Split the `exports["."]` block into explicit `import` / `require`
conditions with `types` listed first. publint flagged the previous
shape because `types: "./dist/index.d.ts"` was interpreted as CJS
when resolving via the `import` condition (FalseCJS / ambiguous
types under `import`). The new shape uses the `.d.mts` declaration
tsup already emits for the ESM build, so TypeScript sees an
`.mts` declaration when resolving via `import` and a `.d.ts`
declaration when resolving via `require`.
- Add `"type": "commonjs"` to silence publint's package-type-detection
suggestion (tsup emits `.js` as CJS and `.mjs` as ESM, matching).
Public import path (`@miden-sdk/vite-plugin`) is unchanged. Verified
the existing vitest suite (27 tests) still passes.
* chore(react-sdk): drop CJS output, ship ESM-only
* chore(web-client): deduplicate lazy/package.json shim
* chore(ci): prettier-format post-build.js
* chore(react-sdk): rename CJS configs to .cjs after ESM-only flip
Switching @miden-sdk/react to "type": "module" makes .js files ESM by
default, which breaks two CJS-syntax files:
- packages/react-sdk/eslint.config.js (uses module.exports + require())
- packages/react-sdk/test/serve-tests.js (uses require())
Renamed both to .cjs. Updated playwright.config.ts:34 to reference the
new filename. ESLint auto-discovers either extension; no other consumer
references the renamed paths.
* chore: add vitest workspace + root test script * chore(vitest): migrate from defineWorkspace to defineConfig projects (vitest 3 modern shape) * chore(ci): exclude root vitest.config.ts from eslint typed-linting
…n#17) * chore: add knip for unused-exports/deps detection Lands knip 6.7.0 plus a baseline knip.jsonc config covering the four TS-bearing workspaces (`packages/react-sdk`, `packages/vite-plugin`, `crates/web-client`, `crates/idxdb-store/src`) and a root scripts entry. Mode: warning-only. The script is `knip --no-exit-code` so CI does not go red on day-one findings; promoting to strict mode is a follow-up once the baseline backlog is cleared. Baseline findings (pnpm run check:knip, exit 0 in --no-exit-code): Unused files 4 (3 are .d.ts ambient declarations in crates/web-client/js/types/, plus packages/react-sdk/src/__tests__/utils/test-utils.tsx) Unused dependencies 3 (root prettier; web-client @rollup/plugin-typescript, dexie) Unused devDeps 5 (root and idxdb-store @typescript-eslint/eslint-plugin; web-client http-server + mocha; react-sdk http-server) Unlisted dependency 1 (web-client test references @aspect-build/aspect-rsdoctor) Unlisted binary 1 (vite in .github/workflows/wallet-pages.yml) Unresolved imports 4 (web-client tests import ./eager.js / ./index.js relative paths — likely dist-time URLs) Unused exports 23 (all in packages/react-sdk test mocks) Unused exported types 9 (react-sdk SignerContext + types/index.ts re-exports) Duplicate exports 3 (playwright.global.setup.ts; react-jsx-runtime.js; vite-plugin index.ts midenVitePlugin|default) Per-workspace tally: root 1 dep, 1 devDep packages/react-sdk 1 file, 1 devDep, 23 exports, 9 types, 1 dup-export packages/vite-plugin 1 dup-export crates/web-client 3 files, 2 deps, 2 devDeps, 1 unlisted dep, 1 unlisted bin, 4 unresolved imports, 1 dup-export crates/idxdb-store/src 1 devDep No CI workflow added in this commit — that lands separately once the baseline is cleaned up. No source code modified. * chore(web-client): drop dead @aspect-build import in sync_lock test The 'waiters are rejected when sync times out' test destructured acquireSyncLock/releaseSyncLock/releaseSyncLockWithError from a stale @aspect-build/aspect-rsdoctor path that has nothing to do with this codebase, then immediately fell back to nulls and never used the bindings. The only thing actually exercised below is client.syncState(), so the destructuring + bogus import was pure dead code. Flagged by knip as the sole 'unlisted dependency' import in crates/web-client; removing the dead block both fixes the finding and makes the test honest about what it's checking. * chore: drop unused devDeps + deps flagged by knip Removes packages that no source file, build config, lint config, test, or script references. Verified each by grepping the repo for imports / CLI invocations / config references before deletion. - root: @typescript-eslint/eslint-plugin (root eslint.config.js wires parser only; no plugin-rules block, so the plugin pkg sits idle) - crates/idxdb-store/src: @typescript-eslint/eslint-plugin (its eslint.config.mjs uses 'typescript-eslint' meta-package, not the legacy plugin) - packages/react-sdk: http-server (no script or workflow runs it) - crates/web-client: http-server, mocha (rollup + playwright + vitest pipeline does not invoke either; no .mocharc, no http-server script) - crates/web-client deps: @rollup/plugin-typescript (rollup.config.js uses node-resolve + commonjs + wasm-tool only — no TS plugin), dexie (only the idxdb-store crate uses dexie, and it lists its own copy) Lockfile regenerated via pnpm install --no-frozen-lockfile. * chore: drop unused files + dead exports flagged by knip react-sdk: - Delete src/__tests__/utils/test-utils.tsx — exports renderWithProvider, renderHookWithProvider, etc., none of which any test imports. The repo uses renderHook/render directly from @testing-library/react. - Strip 'export' off internal mock helpers and types in __tests__/mocks/ (createMockOutputNote, createMockTransactionRecord, MockNoteFilter and ~17 other Mock* constants, MockWebClientType, createMockSignCallback, createMockAccountStorageMode). They were never imported across module boundaries; making them module-private is correct. - Delete the createMockSdkModule factory along with the Mock* class / enum constants that only existed to populate it. No test in the repo ever called the factory, so it pulled an entire wing of dead code. - Remove unreachable type re-exports: GetKeyCallback / InsertKeyCallback / NoteId / NoteVisibility / StorageMode were re-exported through src/types/index.ts but src/index.ts never re-exported them, so they were not part of the package's public surface. The underlying types stay locally-typed where they're actually used. - Make ClientWithTransactions in src/utils/transactions.ts module-private (the noteFilters.ts copy is the one consumers use). web-client: - Delete crates/web-client/.mocharc.json (only consumer was mocha, which was removed in the previous commit) and the matching ts-node / esm devDeps + the now-orphan ts-node block in tsconfig.json. Public API of @miden-sdk/react is unchanged: every type still exported through src/index.ts continues to be exported. Only types that were never reachable through the package entry have been demoted. * chore(knip): allowlist public-API exports + ambient .d.ts files After deleting actual dead code, the remaining findings are all cases where knip can't statically see the consumer: - crates/web-client/js/types/{index,api-types,docs-entry}.d.ts: shipped as the package's published types via 'cpr js/types dist' in the build script, so they ARE the consumer of themselves at publish time. Registered as entry points with a comment naming the post-build copy step. - ./eager.js, ./index.js: dynamic imports inside page.evaluate() callbacks, executed in the browser against http://localhost:8080 (i.e. the dist/ output served by the test http server). Resolved at test runtime, not relative to the Playwright test file. Allowlisted via ignoreUnresolved with a comment. - ./crates/miden_client_web: wasm-bindgen module emitted into dist/ by the rollup rust plugin; the .d.ts files reference it but it doesn't exist until after build. Same allowlist with a comment. - prettier: invoked from the repo Makefile via 'pnpm exec prettier .'; knip doesn't scan Makefile rules. Allowlisted in ignoreDependencies. - vite: invoked from .github/workflows/wallet-pages.yml via 'pnpm exec vite build' inside the wallet example workspace, which has its own package.json + lockfile not visible to this monorepo's graph. Allowlisted in ignoreBinaries. - Three intentional dual-export sites (vite plugin named+default, the Playwright test fixture's named+default 'test', and the React JSX runtime shim's jsx/jsxs/jsxDEV aliases): rules.duplicates set to 'off' globally with a comment enumerating each case so a future change knows when to revisit it. Also dropped the redundant entry patterns and the empty docs/** ignore patterns that knip flagged as configuration hints. After this, 'pnpm exec knip' exits 0 with zero findings; the 3 remaining 'configuration hints' are about react-sdk's package.json exports pointing at not-yet-built dist/lazy.* files, which is correct and doesn't affect the exit code. * chore(knip): flip check:knip to strict mode All baseline findings have been triaged in the preceding commits, so the warning-only flag is no longer needed. Knip now fails CI on any new unused export / dep / file / unresolved import. * chore(ci): wire knip into lint workflow * chore(ci): restore dexie + prettier-format knip files Knip's static scan flagged dexie as unused, but it's bundled into the web-client test page at runtime via rollup — page.evaluate blocks load the bundle from localhost:8080 which transitively imports dexie. The removal broke 22 integration tests with "Failed to resolve module specifier 'dexie'". Re-added dexie@^4.0.1 to crates/web-client deps and registered it in knip's top-level ignoreDependencies with a comment. Also runs prettier --write on knip.jsonc and two react-sdk source files that were missed in the earlier cleanup pass. * chore(knip): allowlist publint/attw + .cjs serve-tests rename
…den#20) Removes overlapping formatting rules (semi, comma-dangle, eol-last, space-before-blocks, keyword-spacing, no-multiple-empty-lines) from the root eslint.config.js so Prettier 3.x is the single source of truth for style. Logic rules (camelcase, @typescript-eslint/no-unused-vars) are preserved. Adds eslint-config-prettier as the last entry in both flat configs (eslint.config.js and packages/react-sdk/eslint.config.js) to disable any remaining stylistic rules pulled in transitively from rule presets, preventing eslint and prettier from fighting. Extends .prettierignore to mirror the eslint configs' ignore list (dist, target, node_modules, generated .d.ts, docs, idxdb-store codegen) so prettier --check stays scoped to source we actually own. The existing .prettierrc.json (trailingComma: es5) is left as-is. Verified: pnpm --filter @miden-sdk/react run lint shows 0 errors (same 5 pre-existing warnings as main); prettier --check on packages/react-sdk and packages/vite-plugin source passes with no reformatting needed.
…0xMiden#35) The pre-split miden-client release workflow shipped all three packages on a tagged release; the web-sdk port dropped react-sdk and vite-plugin publishing while keeping web-client. End users `npm install @miden-sdk/react` were therefore stuck on the `next` dist-tag (set by publish-web-client-next.yml) instead of getting the latest tagged release. This restores parity with pre-split behaviour: - New scripts/check-react-sdk-version-release.sh and scripts/check-vite-plugin-version-release.sh — same pattern as the existing check-web-client-version-release.sh (compare package.json version against the tagged commit's parent, set should_publish flag). - publish-web-client-release.yml now runs each package's version-bump check and conditionally builds + publishes that package. The three publishes are independent, so a release that bumps only one package republishes only that one. - The web-client build runs whenever EITHER web-client OR react-sdk needs to ship, since react-sdk's build consumes the WASM dist.
Match the secret name to the cargo env var (and to miden-node's convention) so the publish-crates workflow picks up the org-managed secret without an indirection.
…ale AGENTS (0xMiden#38) - Add a top-level CLAUDE.md aimed at AI agents (and humans skimming for build/lint/test/release conventions). Captures pnpm-only rule, the Makefile-driven workflows, runExclusive, the eager/lazy entry contract, the npm-registry-driven publish gate, and cross-repo coordination notes. The README already links to it (line 337). - Refresh packages/react-sdk/CLAUDE.md against the current source: * useNotes returns notes/consumableNotes (not input/consumable) * useAccounts returns accounts/wallets/faucets (no 'all') * Mutation hooks expose action-named callbacks (send, mint, ...) and a 'result' field — not generic { mutate, data } * useSend / useMultiSend take assetId / recipients (not faucetId / outputs) * SendResult has txId (not transactionId) * SignerAccountConfig uses publicKeyCommitment + accountType * Hook reference table reorganized into query/mutation buckets and includes the previously-undocumented hooks (useNoteStream, useSyncControl, useTransactionHistory, useImportNote / Export*, etc.) - Remove docs/planning/AGENTS.md — 7 lines of miden-client leftover that referenced 'yarn prettier' (the repo migrated to pnpm). All the still-useful content is now in the top-level CLAUDE.md.
…iden#41) * ci(changelog): add changelog gate + filtered web-sdk CHANGELOG.md Mirror miden-client's changelog gate. Each PR must touch one of the three changelog files; trivial PRs override with the 'no changelog' label. Files: - CHANGELOG.md (new) — pre-filled with miden-client's [web]-tagged entries up through 0.14.x. Source-of-truth changelog for the WASM client (@miden-sdk/miden-sdk). - packages/vite-plugin/CHANGELOG.md (new, stub) — placeholder so the CI script has a real path to diff against. - packages/react-sdk/CHANGELOG.md (already exists) — unchanged here. - scripts/check-changelog.sh (new) — mirrors miden-client's script, extended with a third path for the vite-plugin. - .github/workflows/changelog.yml (new) — fires on PR open / sync / label changes. * ci(changelog): collapse to single root CHANGELOG.md + auto-populate release notes - Drop packages/react-sdk/CHANGELOG.md and the packages/vite-plugin/ CHANGELOG.md stub. The single root CHANGELOG.md is the source of truth for every published artifact in this repo. - Simplify scripts/check-changelog.sh accordingly: only the root file is checked. The 'no changelog' label still overrides. - Add scripts/extract-changelog-section.sh: takes a version (with or without a leading 'v'), prints the matching '## <version> (...)' section body to stdout. Used by the release-notes workflow. - Add .github/workflows/release-notes.yml: on release:published, looks up the matching CHANGELOG section and replaces the release body via 'gh release edit --notes'. Logs a warning + leaves the body untouched if no matching section is found (no failure — release shouldn't be blocked on a stale changelog; the gate at PR time is where that's enforced).
* ci: migrate heavy jobs to WarpBuild for ~3x speedup * chore: ignore docs/superpowers * chore: remove tracked docs/superpowers (now gitignored)
0xMiden#46) * fix(ci): wait for node-builder/prover gRPC port instead of sleep+pgrep Root-cause fix for the recurring 'Integration tests (ci-shard-N)' flake that intermittently surfaces as 'TypeError: Failed to fetch' from the in-browser gRPC client during ensure_genesis_in_place. The previous startup pattern was: ./bin/testing-node-builder & sleep 4 pgrep -f testing-node-builder || exit 1 which only checked process liveness, not that the gRPC listener had bound to its TCP port. On slower CI runners, the test job would race the listener and fail the very first RPC call (get_block_header_by_number) with a JS-level fetch failure. Replaced with a /dev/tcp probe loop with a 30s timeout. Same fix applied to all three startup sites: - integration-tests-web-client: testing-node-builder on :57291 - integration-tests-remote-prover-web-client: testing-node-builder on :57291 - integration-tests-remote-prover-web-client: testing-remote-prover on :50051 If the port isn't reachable in 30s the step still fails with a clear error (and prints whether the process is running at all). * fix(ci): wait for node-builder/prover stdout readiness signal, not bare TCP Previous fix (b02c1dc) replaced sleep+pgrep with a TCP-probe loop on :57291. The probe consistently passes within 6s on WarpBuild runners, but tests STILL hit 'TypeError: Failed to fetch' from the in-browser gRPC client. Root cause: the node-builder binds its public TCP listener early but doesn't finish wiring up the gRPC services (store, ntx-builder, block- producer, validator) until `NodeBuilder::start().await` returns. A bare TCP probe to :57291 succeeds before the gRPC service is registered — in-browser fetches race in and fail. The reliable readiness signal is the explicit 'Node started successfully with PID' stdout line printed AFTER start().await returns (see miden-client crates/testing/node-builder/src/main.rs:55). This commit: - Pipes node-builder stdout to /tmp/node-builder.log and waits (timeout 60s) for the readiness line to appear before returning from the step. - Same treatment for the remote-prover step ('Remote prover listening on <addr>' from prover/src/main.rs:87). - On timeout, dumps the last 50 lines of the captured log so the failure mode is visible. * fix(ci): force line-buffered stdout via stdbuf -oL on test backends Addendum to 931e36a. The stdout-grep readiness check on the captured log was timing out because Rust's stdout is BLOCK-buffered when connected to a file (`>/tmp/node-builder.log`) instead of line-buffered when connected to a TTY. The 'Created data directory' line happened to flush, but 'Node started successfully' (printed ~80 bytes later) never reached the buffer fill threshold (~8KB) so it stayed in memory and the grep loop timed out at 60s. Wrap both backend launches with `stdbuf -oL` (coreutils) to force line-buffered stdout, which makes println output visible to the log file as soon as the newline is written. Same fix on the remote prover step. * fix(ci): revert readiness probe over-engineering, add Playwright retries: 1 Backing out 931e36a + a31c792. The root-cause hypothesis (gRPC listener bound before service registered) led to a stdout-grep readiness check that consistently TIMED OUT — start().await either hangs or stdout doesn't flush properly when redirected to a file (stdbuf -oL didn't fix it). All 4 integration shards failed solid on the over-engineered version. Restore the original `sleep 4 && pgrep` startup pattern (which was flaky at maybe 1-3% of runs but functional 97% of the time), and add `retries: 1` to playwright.config.ts on CI to mask the residual flake without doubling test wall-clock. The actual root cause of the original 'TypeError: Failed to fetch' flake remains unidentified — not enough signal in failed runs to isolate it. If it becomes a real problem (>5% of runs), revisit with debug instrumentation in the test harness or the node-builder binary itself rather than the workflow YAML.
…iden#48) * ci(publish): consolidate to single workflow + trusted publishing (main) Replaces the two-workflow setup (publish-web-client-release.yml + publish-web-client-next.yml) with a single publish-web-sdk.yml that handles both the GitHub-release event AND the next-channel patch-release PR-merge event in one file. Why one file: npm's trusted publishing has a (package, repo) uniqueness constraint — only ONE workflow filename per package can be the trusted publisher. Probed empirically: registering trust for a second workflow on the same package returns 409 Conflict, including with a different `--env` scope. To use trusted publishing for both flows, both must come from the same workflow file. Migration to trusted publishing: - permissions.id-token: write at workflow + publish-job level → GitHub mints the OIDC token npm exchanges for a short-lived publish credential. - node-version bumped to 24 + force-update of npm CLI to ensure ≥ 11.5 (required for OIDC support). - NPM_WEBCLIENT_TOKEN env vars dropped from every publish step. - --provenance flag added to every `npm publish` / `pnpm publish` for the signed attestation that ships with the package. Branching by event: - release:published → `--tag latest`, gated by check-*-version-release.sh. - pull_request:closed (against next, with patch-release label) → `--tag next`, gated by check-*-version-pr.sh. Inert on main (branch filter excludes main-targeted PRs); kept here for parity so the same trust config works across both long-lived branches. - vite-plugin still ships only on release events (no pr.sh script for it). Tracked as future work. Trust registration follow-up: each of the 6 published packages has its trust pointing at `publish-web-client-release.yml`. After this lands, huna1 (npm org owner) revokes those + re-registers against `publish-web-sdk.yml` for both branches' workflow location. Until that happens the publish job will fail at the publish step (no trust → 401). * fix(ci/publish): guard skipped job + pin npm to ^11.5 Two review-feedback fixes on the consolidated publish workflow: 1. `skipped:` job no longer fires misleadingly when `check-version` itself was skipped. Previously: a PR merged to `next` without the `patch release` label rejected at `check-version`'s `if:`, all `should_publish_*` outputs were empty strings, the `!= 'true'` checks all evaluated true, and `skipped:` ran printing "All publish gates failed" — even though no gating ever happened. Adding `needs.check-version.result == 'success'` to the guard prevents that. 2. Pin the global npm install to `^11.5` instead of `@latest`. A future npm 12 with breaking changes to `--provenance` / OIDC semantics would silently break this workflow under @latest. The ^11.5 floor still gets the OIDC support we need.
…ain) (0xMiden#53) * fix: restore exec bit on check-react-sdk-version-pr.sh * fix(ci): wait for node-builder readiness via pty (real fix) Spawn testing-node-builder + testing-remote-prover via 'script -qfec' (allocates a pty), then poll the captured log for the binary's own 'Node started successfully' / 'Remote prover listening' stdout signal. Why each prior approach failed: - 'sleep 4 && pgrep': only proves the process is alive, not that the port is bound or gRPC handlers are registered. ~10% flake. - TCP probe (/dev/tcp): kernel-level handshake succeeds the moment TcpListener::bind() is called, but tonic's gRPC service dispatcher registers AFTER that. Racy. - 'stdbuf -oL': only modifies libc stdio buffering. Rust's println!() goes through std::io::stdout()'s own BufWriter (~8KB block buffer on non-tty fds) and ignores stdbuf entirely. Captured log stayed empty until the buffer filled, long past the readiness window. The pty is the actual fix: Rust detects the tty fd and constructs stdout as a LineWriter, so println!() flushes on every newline. 'script -qfec' is preinstalled (bsdmainutils) on Ubuntu runners. Centralized into scripts/wait-for-bg-binary.sh so the same probe applies at all 4 spawn sites (3 node-builder + 1 remote-prover). * Revert "fix(ci): wait for node-builder readiness via pty (real fix)" This reverts commit 0ad7700.
…ENT_REF v0.14.4 → v0.14.5 (0xMiden#59) * fix(ci): introduce wait-for-grpc.sh with strict 3-digit HTTP code check (sibling of next) Sibling fix to next-side. PR 0xMiden#57 (the original main-side flake fix) shipped a probe that false-positived on shard-4: reported 'gRPC dispatch responsive after 1 attempt (HTTP 000000)' and tests then ran against a not-yet-ready server, hitting the original TypeError: Failed to fetch. Two issues: 1. The contrived gRPC-web payload `--data-binary "$(printf '\x00\x00...')"` tripped bash's null-byte stripping in command substitution. The payload was always empty after substitution; the printf was useless. 2. The success check `[ "$http_code" != "000" ]` was too loose. On the failing run curl somehow emitted "000000" (a six-digit string, probably from an internal retry concatenation), which 'is not 000' and the loop broke immediately. Fix: drop the fancy POST, use a plain GET (any HTTP response from tonic — even 404/405/415 — proves the dispatcher is up). Tighten the success regex to require an exact 3-digit [1-5][0-9][0-9] code. Failures and timeouts continue to produce '000' which now correctly fails the regex and loops. Note: PR 0xMiden#57 was never merged. This PR brings the script forward plus the test.yml wiring (the 3 spawn site replacements that were in PR 0xMiden#57). * ci(test): wire wait-for-grpc.sh into all 3 spawn sites * ci: bump MIDEN_CLIENT_REF v0.14.4 -> v0.14.5 The HTTP-probe readiness check (introduced earlier in this PR) works on next branch where MIDEN_CLIENT_REF=dab6cf7b... but breaks main on v0.14.4 — every test fails with TypeError: Failed to fetch even though the probe gets HTTP 200. The older testing-node-builder appears to misbehave after handling a non-gRPC HTTP request. Bump to v0.14.5 to pick up the corresponding stable client release; the probe should be compatible with this slightly newer binary.
Required by npm's provenance verifier. Without it, publishes via
trusted publishing fail with:
422 Unprocessable Entity
Failed to validate repository information: package.json:
"repository.url" is "", expected to match "https://github.com/0xMiden/web-sdk"
react-sdk and vite-plugin already had the field; only web-client
was missing. Discovered when 0.15.0-alpha.3 published the platform
packages but rejected the web-client publish on next.
…Miden#62) Observed flake: probe returns HTTP 200 once on the first attempt that clears the connection-refused phase, exits, tests start, ALL tests fail with 'TypeError: Failed to fetch' to the gRPC backend. The single-probe gate isn't strict enough — a one-shot 200 (e.g. tonic-health responding before the rest of the dispatcher is fully wired) currently passes. Upgrade the readiness signal to N consecutive HTTP successes spaced PROBE_INTERVAL apart (defaults: 3 successes, 0.5s apart), so the probe only declares the server ready after ~1s of demonstrably-stable response. Any non-success in the streak resets it to zero and the slow-poll loop resumes — so a momentary blip during init doesn't get counted twice on either side. Tracked occurrences across recent PR runs: web-sdk PR 0xMiden#23 ci-shard-4, PR 0xMiden#29 ci-shard-1 + ci-shard-4, PR 0xMiden#27 multiple shards.
* ci: auto-patch miden-client dep from PR description marker
When a web-sdk PR description contains:
Client PR: #1234
Client PR: 0xMiden/rust-sdk#1234 (cross-repo / fork form)
a new composite action (.github/actions/inject-linked-client-pr) parses
the marker, resolves the linked PR's head ref, and rewrites web-sdk's
miden-client (and miden-client-sqlite-store) dep in place to point at
that branch — only on the runner, never committed. The build then runs
against the unreleased upstream code, while the committed Cargo.toml
diff stays clean. Replaces the manual '[patch]' / branch-edit dance
that this session has been carrying on every migration PR.
Why in-place rewrite instead of [patch.crates-io] / [patch."<url>"]?
Web-sdk's 'next' pins miden-client at git+url=...miden-client.git+
branch=next; pointing the same URL at a different branch via [patch]
errors with 'patches must point to different sources'. In-place
rewrite covers both 'main' (crates.io dep) and 'next' (git dep)
uniformly — the rewritten line is wrapped in marker comments that
preserve the original verbatim so a cleanup step can restore it.
Components:
.github/actions/inject-linked-client-pr/action.yml
Composite action: parse marker, resolve head, validate state
(closed-without-merge fails the run loudly), patch Cargo.toml +
refresh Cargo.lock, post a sticky PR comment summarizing the
patch. Strict 0-or-1 comment invariant: only fires when called
with comment=true (one designated job per workflow run), and
deletes the prior comment if the marker is later removed.
.github/workflows/build.yml + test.yml
Wired the action into the cargo-compiling jobs (build-wasm,
build-web-client-dist-folder, verify-release-build). build-wasm
is the single comment-poster (comment=true).
.github/workflows/check-linked-client-pr.yml
Mergeability gate: keeps a 'linked-client-pr-ready' check on the
PR. Stays pending while the linked client PR isn't merged-and-
reachable from the target branch's canonical refs (miden-client
next for next-targeted PRs, latest miden-client release tag for
main-targeted). Re-evaluates every 15 min so the check goes green
automatically once upstream catches up — no need to re-push.
Configure branch protection to require it.
scripts/dev-with-client-pr.sh
Local-dev mirror: applies the same in-place dep rewrite to your
working tree. Idempotent. '--clear' restores the originals
byte-for-byte from the marker block.
lefthook.yml
Pre-commit guard: refuses any commit while the marker block is
present in Cargo.toml. So a forgotten 'apply' can't accidentally
leak into a commit.
CLAUDE.md
Documents the marker convention, the local script, and the
mergeability gate.
* fix(ci): use POSIX [[:space:]]* instead of \s in marker grep
GNU grep on the runner balked with 'repetition-operator operand invalid'
on the \s* in the regex. \s is a Perl-extension class that grep -E
doesn't recognize in POSIX-extended mode; the * after it ends up
applying to nothing, hence the error. Replace with [[:space:]]* across
the composite action, the dev script, and the readiness gate workflow.
* fix(ci): anchor Client PR marker regex to start-of-line
Without the ^[[:space:]]* prefix, doc examples or table cells in the
PR body that mention 'Client PR:' inline (e.g. inside backticks for a
test-plan checklist) are picked up as if they were real markers — the
action then tries to fetch a fake miden-client PR and fails with
'Failed to fetch'. Anchoring requires the marker to start its own
line, so inline mentions in surrounding prose stay decorative. Doc
examples that use placeholders like #<NUM> instead of #N (a literal
number) are also rejected by the existing #[0-9]+ requirement.
Pairs with the PR-body cleanup that replaces the literal example
'Client PR: #1234' with 'Client PR: #<NUM>' so it's clearly a template.
gh's REST API returns pull-request state as 'open' / 'closed', GraphQL returns 'OPEN' / 'CLOSED'. The closed-PR guardrail in the composite action and the dev script were comparing against the uppercase form, so the closed-without-merge branch always fired even on open PRs — the action exited with 'is closed without merge' on every run with a 'Client PR:' marker. Normalize the API-returned value via tr to lowercase, then compare to 'open'. Verified against miden-client#2059 (state='open' from REST).
…ment (0xMiden#69) The auto-patch action's parse + Cargo.toml-rewrite steps succeed, but the sticky-comment step fails with 'Resource not accessible by integration' because build.yml only declares contents: read at the workflow level. The error short-circuits the composite action and the build job fails before cargo even runs. Two-part fix: build.yml -> grant pull-requests: write to the build-wasm job only. Scoped to that job (rather than at the workflow level) so every other job keeps the read-only default. Principle of least privilege: only the comment-poster needs the write. inject-linked-client-pr/action.yml -> mark the sticky-comment steps (both upsert and delete) as continue-on-error: true. The comment is decorative — if write permissions ever go missing again, or if the sticky-comment marketplace action has a transient blip, we shouldn't abort the rewrite step's gains. The patch is already on disk at the point those steps run.
…iden#71) PR 0xMiden#25's verification run surfaced a coverage gap: my initial wiring in 0xMiden#65 only patched build.yml's build-wasm and test.yml's build-web-client-dist-folder + verify-release-build. But Clippy WASM (in lint.yml) and publint + attw (in check-publish.yml) ALSO compile cargo against the workspace's miden-client dep — without the patch, they hit canonical crates.io `miden-client = "0.14.5"` and fail with `error[E0599]: no variant named `ApplyTransactionAfterSubmitFailed`` when the PR depends on an unreleased upstream variant. Add the action call after checkout in both jobs. Both run with comment=false (the default); build-wasm remains the single sticky- comment poster.
…0xMiden#73) Two issues from PR 0xMiden#25's verification run: 1. The 'linked-client-pr-ready' custom commit status the gate posts never landed. Cause: the workflow's permissions block has 'checks: write' (for check-runs) but not 'statuses: write' (which the POST /repos/.../statuses/$sha endpoint requires). The gh api POST 403'd silently — and #2 below ate the error. 2. set_status() redirected gh's stdout to /dev/null. With no error visible, the silent 403 looked like 'job ran successfully' even though the readiness verdict never reached the commit. This commit: - adds 'statuses: write' to the workflow permissions - drops the >/dev/null redirect so any future API failure is loud
0xMiden#75) Previous form: read -r body base_ref state <<<"$(gh api ... --jq '"\(.body // "")\t\(.base.ref)\t\(.state)"')" PR bodies are multi-line and the marker is itself space-separated. read defaults to whitespace IFS and only consumes through the first newline, so a body starting with 'Client PR: #2059\n...' assigned: body = 'Client' base_ref = 'PR:' state = '#2059' The gate then hit the early-return: if [ "$state" != "open" ] && [ "$state" != "OPEN" ]; then echo "PR #$PR_NUM is $state — skipping." exit 0 fi …and exited with the visible-in-the-job 'PR 0xMiden#25 is #2059 — skipping.', never posting the linked-client-pr-ready custom status. The job logged 'success' because exit 0 is what the early-return does, but the readiness verdict never reached the commit. Fix: - body = separate gh api call (multi-line tolerated naturally) - base_ref + state packed into a tab-separated single-line, read with IFS=$'\t' so a hypothetical future whitespace in either field wouldn't repeat the bug - Same IFS=$'\t' added to the second read of (merged, merge_commit_sha) for consistency, even though neither field can contain whitespace today
…arker (0xMiden#77) The README's Contributing section was a stub. Move it into a proper CONTRIBUTING.md (GitHub auto-detects this filename and surfaces it on new-issue / new-PR pages) and expand it with the cross-repo workflow: - The 'Client PR: #N' marker convention (and the cross-repo / fork form 'Client PR: 0xMiden/miden-client#N') - What CI does with the marker (auto-patch action, sticky PR comment, readiness gate) - Branch-protection guidance (require linked-client-pr-ready, NOT the gate matrix job) - Local-dev parity (scripts/dev-with-client-pr.sh) - The three situations where you still want to hand-edit Cargo.toml instead of using the marker README's Contributing section is now a one-line pointer.
…DK API changes (0xMiden#127) * docs(CLAUDE.md): document required surfaces for MidenClient + React-SDK API changes Adds a 'Documenting public-API changes' section to the repo CLAUDE.md that maps every doc surface a contributor has to touch when changing the public API of either the MidenClient resource layer (web-client) or the React SDK. Replaces the vague 'update relevant per-package CLAUDE.md' bullet in the Contributing checklist with a precise mapping table. Surfaces enumerated for each side: TS types (api-types.d.ts), resource JSDoc, README narrative, per-package CLAUDE.md, root CHANGELOG, and the typedoc curation file when applicable. Includes triggers (when to touch each), conventions (terse tone, no speculative docs, cross-link PRs, one source of truth per fact), and a doc-only-PR escape hatch. Triggered by web-sdk#31, where the new transactions.batch API needed docs in five different places — easy to miss without an explicit map. * docs(CLAUDE.md): add the published-docs-site surfaces to the doc-process map The first pass missed three load-bearing surfaces of the public-docs pipeline: 1. The canonical user-facing site at https://docs.miden.xyz, hosted from 0xMiden/miden-docs, ingesting upstream repos via deploy-docs.yml's docs/external/src/* → docs/builder/<repo>/ copy step. 2. The typedoc-generated API reference at docs/typedoc/web-client/, which is regenerated from docs-entry.d.ts and verified by CI via git diff --exit-code (drift fails the build). 3. docs/external/src/ as the canonical narrative source the deploy-docs workflow ingests. After the web/WASM split this directory needs to exist in this repo (currently a gap — miden-client's docs/external/src no longer carries web/React content), and pages under it need to be added when shipping new public capabilities. Adds a 'Where the docs are published' table at the top of the doc-process section, plus a typedoc-regen workflow snippet, plus README ⇄ Docusaurus parity rule. Expands both the MidenClient and React SDK surface tables to call out docs/external/src/ and docs/typedoc/web-client/ explicitly, and updates the contributing-checklist item 3 to enumerate them. * fix(clippy): drop redundant & in export.rs format! arg Newer nightly clippy fires `useless_borrows_in_formatting` on the pre-existing &account_id.to_string() pattern. The lint started firing between origin/main's last green CI run (2026-04-30) and today — nightly clippy rolls forward unpinned in this repo's Makefile. Same fix as web-sdk#31's commit 7f9cf07. Including it here so the CLAUDE.md doc-process change isn't blocked by an unrelated baseline lint regression on the target branch. * fix(fmt): collapse format! to single line per nightly fmt After dropping the redundant & in commit 2b27e16, the call fits on one line and cargo +nightly fmt --check fails until we collapse the multi-line layout. Same pattern as the next-targeted PR (which already had it on one line).
…en#129) The first pass of the doc-process section (0xMiden#126/0xMiden#127) prescribed 'regenerate typedoc and commit the diff' for any change to the curated public surface. That guidance was wrong: the in-repo 'Check that web client documentation is up-to-date' CI step runs typedoc and then a git diff --exit-code over the regenerated tree, but the directory hasn't been tracked since the web/WASM split — so the diff is empty and the step is a warning-only smoke test, not a gate. typedoc output is build artifact, not source. Updates four spots in the doc-process section: - 'Typedoc — keep in sync with the public API surface' → 'Typedoc — regenerated by CI, don't commit' - The MidenClient surface table row for docs/typedoc/web-client/ now reads 'Don't commit. Regenerated by CI...' - The Conventions bullet 'Regenerate typedoc' became 'Don't commit typedoc' - Contributing checklist item 3 mirrors that Triggered by web-sdk#31 audit feedback: noticed the autogen tree shouldn't have been committed there either. Companion .gitignore addition lands separately on web-sdk#31.
…erences it on next)
…ght project The test asserts on idxdb-store's Js* classes and the wasm-bindgen WebClient copied onto window by the browser global setup — none of which exist under the napi binding. main had no nodejs project, so the merge made it run there for the first time.
The test predates the main/next split and guards both directions of the entry contract: dist/st/eager.js must have WASM initialized at import (sync constructors work immediately), and dist/st/index.js must NOT TLA-init WASM (sync constructor throws before getWasmOrThrow resolves). next's js_export/napi test-suite rewrite (0xMiden#13) deleted it, leaving the package's default entry (main/browser → dist/st/eager.js) untested; the merge inherited that deletion and the knip ignoreUnresolved entry for ./eager.js went stale — restoring the test un-stales it. Excluded from the nodejs Playwright project (page fixture + browser-only dynamic imports). Verified green locally against the merged dist.
- ci: eager_entry.test.ts added to ci-shard-4's testMatch — main carried it in shard-4, the merge dropped the entry, and CI only invokes the shard projects, so the restored test was never executing in CI - react-sdk: the variant rewrite in tsup.config.ts keyed off the /lazy specifier, but next's source tree imports the bare @miden-sdk/miden-sdk — the rewrite never matched and every variant (/lazy, /mt, /mt/lazy) silently bundled the eager-ST SDK. The rewrite now keys off the exact quoted bare specifier and THROWS when nothing matches, so a future specifier change cannot silently ship wrong variants again. Verified: all four dist bundles emit their correct per-variant subpath - node: waitForIdle resolves immediately (the napi binding has no detached call chain to drain) and lastAuthError returns null (signing goes through FilesystemKeyStore — a JS sign callback can never run), so the cross-platform MidenClient type surface holds on Node instead of throwing TypeError - docs: lastAuthError docstrings state the useWorker:false requirement (the worker shim's keystore lives in the worker WASM instance, so the main-thread accessor reads null there) - test: with_inner_web_client_reentrancy first case comment matched main's serializing proxy; next's proxy binds fallback reads directly, so the case guards the borrow-check invariant, not chain re-entrancy
…roject Same class as no_wasm_reentry_via_tojson: the tests came from main (which had no nodejs Playwright project) and exercise the browser worker-shim chain mechanics — _serializeWasmCall, the _withInnerLockDepth counter, chain release on rejection. The napi client serializes in Rust and has no JS call chain, so all four cases TypeError'd under nodejs. They were masked in the previous run because no_wasm_reentry aborted the suite earlier.
Bumps @miden-sdk/miden-sdk, @miden-sdk/react (+ peer), and the three node-sdk platform packages to 0.15.0-alpha.5. First alpha published from the unified branch (PR 0xMiden#177): restores useWorker, newCallbackProver, _withInnerWebClient, MidenClient.ready/waitForIdle/lastAuthError, BlockHeader.feeFaucetId, the miden-mobile-prover crate, and MT proving via the /mt + /mt/lazy subpaths on the 0.15 protocol surface. Release notes must flag: nightly toolchain required for the MT build; react-sdk is now esm-only; mobile-prover wire format requires a matching 0.15 SDK (0.14-built native binaries do not interoperate).
…ort for granularity (0xMiden#30)
…resolves the 0xMiden#180 'prove hang' (0xMiden#181) * fix(web): initialize the rayon pool in the mock worker (INIT_MOCK) Every prove executes inside the method worker's own WASM instance, and rayon's global pool is per-instance — but the INIT_MOCK action never called initThreadPool there, unlike the real INIT path beside it. Mock proving (incl. the integration suite) therefore ran single-threaded on the /mt build regardless of page COI or main-thread pool init: a mock mint prove measured 23.3s vs 26.4s on the ST build (and 47.8s on 0.14.11 — the gap predates 0.15). numThreads now rides INIT_MOCK exactly like INIT; the same prove completes in 5.2s on 10 threads. Also ships two testing-gated MT diagnostics (mtProbeSync/mtProbeAsync) that report which pool threads execute a par_iter from sync vs async dispatch — these were the instruments that separated healthy pool mechanics from the missing per-instance init while investigating the 'prove hang' in 0xMiden#180 (which this resolves: the perceived hang was the sequential prove blowing consumer timeouts, plus an experiment that rayon::spawn'ed into the uninitialized mock-worker registry, where jobs wait forever for workers that don't exist). * docs: changelog entry for the INIT_MOCK thread-pool fix
…render (0xMiden#179) * fix(react): resolve the default prover after WASM init, not at first render MidenProvider resolved its prover config in a render-time useMemo — resolveTransactionProver constructs WASM objects (TransactionProver), which crashes with `__wbindgen_malloc` undefined on the /lazy entries, where no top-level await precedes first render. The whole consumer tree went down with it (Miden Wallet's extension popup error boundary: 'Something went wrong while booting a wallet'). The bug was latent on every published 0.15 alpha before .5: the react /lazy build accidentally bundled the EAGER SDK (the inert variant rewrite fixed on the unification branch), whose import-time TLA initialized WASM before any render. Correct lazy resolution exposed it. The prover is now resolved as state gated on `isReady` — by then the WebClient exists, so the WASM module is necessarily initialized. Hook prove paths were already safe (they resolve at prove time). * docs: changelog entry for the lazy-entry prover resolution fix * fix(react): drop eslint-disable for unconfigured react-hooks/exhaustive-deps rule The react-sdk ESLint config registers only @typescript-eslint, so the react-hooks/exhaustive-deps disable directive references an undefined rule, which ESLint reports as an error (Run lint exits 2). The rule isn't active, so the deps array needs no suppression — remove the directive.
) Two fixes for the wallet leg of 0xMiden#180: - proveTransaction(result, prover) with an explicit prover proves directly, without requiring an initialized client. Proving with a supplied prover is pure computation over the TransactionResult; the inner-client gate was incidental and broke prover-only hosts (e.g. a chrome.offscreen document proving on its own rayon pool). The explicit-prover path also no longer holds the inner-client lock for the duration of the prove. - A failed method-worker INIT now rejects the wrapper's `ready` promise. INIT errors carry no requestId, matched no pending request, and were dropped — leaving `ready` forever pending and every worker-forwarded method call silently hanging on `await this.ready`. Callers now get the underlying error (e.g. the eager genesis fetch failing against an unreachable or version-mismatched RPC endpoint).
…t merge (0xMiden#184) The 0xMiden#177 unification dropped the _serializeWasmCall chain from the direct (no-worker) path everywhere it existed on main: - createClientProxy bound fallback methods (getAccount, getAccounts, getTransactions, ...) raw to the WASM client instead of routing them through the chain (SYNC_METHODS stay raw-bound by design); - the five transaction methods and three sync methods called the raw client directly in their !this.worker branches. Every such call holds the WASM client's internal RefCell across its awaits, so two overlapping calls panic with 'RefCell already borrowed' and poison the instance — later calls throw 'Unreachable code should not be executed' and in-flight promises can stay pending forever. useWorker:false consumers (Capacitor/WKWebView, callback-prover hosts) hit this deterministically; miden-wallet's mobile build panicked at wallet creation when front-end balance polling overlapped the initial syncs (the mobile leg of 0xMiden#180). All direct-path calls now route through the chain exactly as on main and 0.14.x. The sync methods keep their coalescing lock as the outer lock (sync lock → chain, in that order, on every path — no deadlock). Covered by a burst-concurrency regression test that hangs/panics on the unfixed build and completes in ~3s on the fixed one.
…s when tx_script is missing (0xMiden#24)
…t::note::NoteType via compile-time assertions (0xMiden#22)
* chore: update miden dependencies * fix(tests): repair test/impl mismatches left by the next merge - restore the NoteAttachment instance argument to createP2IDNote call sites in notes, note_transport and import_export integration tests. The WASM signature requires the instance and the merge had replaced it with undefined - useMultiSend.test: the hook passes an empty attachment, so expect an instance rather than undefined - utils.test: the network storage mode no longer exists in the WASM surface (removed in miden-client #2214). Assert it is rejected instead of mocking AccountStorageMode.network, and drop the unused network mock from accounts.test - idxdb-store: add the detailsCommitment field (now the OutputNotes primary key) to output-note fixtures in sync.test and to all upsertOutputNote calls in notes.test * docs: fill in PR links for the 0.15.0 changelog entries carried by 0xMiden#171 * chore: bump to client crates.io dep * fix(ci): migrate test infra from node-builder to miden-client's start-test-node.sh miden-client v0.15.0 removed the testing node-builder and testing-remote-prover crates. Test infra is now provisioned the same way miden-client's own CI does it: scripts/start-test-node.sh cargo-installs the standalone node executables (miden-validator, miden-node, miden-ntx-builder, miden-remote-prover) at the node rev pinned in its Cargo.lock and bootstraps a fresh genesis from the test-node-genesis crate. - replace build-node-builder with build-test-node (install + gen-genesis prebuild, cached on node rev / client ref, uploaded as test-node-artifact) - drop build-remote-prover: the tx prover ships with the test node on 50051 - consumer jobs check out miden-client, download the binaries, and run start-test-node.sh --background (waits for RPC on 57291 itself) - drop scripts/wait-for-grpc.sh (no remaining callers) --------- Co-authored-by: igamigo <ignacio.amigo@lambdaclass.com>
chore(release): merge `next` into `main` for v0.15.0
VAIBHAVJINDAL3012
force-pushed
the
feat/pswap-tracking
branch
from
June 16, 2026 15:42
4f6bff0 to
3a8b309
Compare
Persist a lineage per partially-fillable swap order — the chain of remainder notes a PSWAP leaves behind as it is filled round by round — keyed by a stable orderId. Adds a `pswap` resource on MidenClient (lineages / lineagesFor / lineage / cancelByOrder) and four React hooks (usePswapLineages, usePswapLineagesFor, usePswapLineage, usePswapCancelByOrder). - pswap.rs: lineage reads + build_pswap_cancel_by_order with a terminal-state guard (only Active lineages can be cancelled), covering the raw binding, the pswap.js resource, and the React hook through the one shared layer. - PswapLineageRecord model exposes remainingOffered/remainingRequested as FungibleAsset (faucet + amount), depth, tip, state, and block numbers. - applyTransaction routes through the high-level apply so registered tx observers (PSWAP tracking) fire. - Tracks inicio-labs/miden-client vaibhav/pswap until the PSWAP API releases.
VAIBHAVJINDAL3012
force-pushed
the
feat/pswap-tracking
branch
from
June 16, 2026 16:05
3a8b309 to
43a6a25
Compare
…itative The cancelByOrder resource duplicated the FullyFilled/Reclaimed check that buildPswapCancelByOrder already enforces in Rust, and its comment described the pre-guard behavior (request reaching the kernel). Remove the JS state block so the binding is the single guard, matching usePswapCancelByOrder; keep the lineage fetch for the creator account.
* chore(deps): upgrade miden-client to 0.15.2 Bumps miden-client + miden-client-sqlite-store 0.15.0 -> 0.15.2 (pulls miden-note-transport-proto-build 0.4.1; miden-protocol resolves to 0.15.3). Updates the CI node-builder ref (MIDEN_CLIENT_REF) to the v0.15.2 tag commit so the node's miden-protocol matches the bundled client. 0.15.2 deprecates the no-hint Client::send_private_note; the web-client keeps the no-hint path (allow(deprecated)) since the JS API does not expose a block hint yet. * release: 0.15.1
0.15.2 ships the PSWAP API on crates.io, so pin the version and drop the inicio-labs/vaibhav/pswap git dependency. Adapt to 0.15.2's PswapLineageRecord: remainingOffered/remainingRequested are now plain amounts (the record no longer carries the faucet — it's recovered from the original note when needed), and the created/updated block accessors are gone (fields dropped upstream). Suppress the new send_private_note deprecation; the block-hint variant can be wired later.
The "Publish Rust Crates on Release" job has failed since v0.15.0: - web-client depends on js-export-macro via a bare path dep (no version), so cargo publish refuses it, and js-export-macro was never in the publish list. - The Rust crates share the workspace version, which releases don't bump (only the npm versions move), so cargo publish hits "already exists". Fixes: - Bump the workspace version + internal dep version refs to 0.15.1. - Add js-export-macro as a workspace dep (path + version); web-client uses it via workspace = true so the published manifest carries a version. - Publish in dependency order (js-export-macro, idxdb-store, web-client, mobile-prover) and add the missing js-export-macro step. - web-client publishes with --no-verify: it's a wasm cdylib that can't build for the host target; CI verifies it for wasm32 instead. - Document the dual npm/Rust version bump in CONTRIBUTING. Verified: js-export-macro/idxdb-store/mobile-prover dry-run-publish cleanly at 0.15.1; web-client's manifest now resolves (its deps publish first in the workflow).
Add a workflow_dispatch trigger (with an optional ref input) to publish-crates-release.yml so the crates can be (re-)published off a chosen ref — e.g. to catch crates.io up to a fix that landed after a release tag, or to recover when a release's publish failed partway. The checkout ref falls back from the release tag to the dispatched ref.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds PSWAP lineage tracking to the web SDK: the client now persists the lifecycle of partially-fillable swap (PSWAP) notes across fill rounds and exposes that state to JavaScript and React consumers. This is the browser-side counterpart to the Rust client tracking work.
Client PR: 0xMiden/rust-sdk#2231
This builds on the PSWAP transactions feature already in the base branch (
feat/migrate-client-2214); this PR adds the tracking + read surface on top of it.What's included
idxdb-store(persistence)pswapLineagesobject store (schema v2) tracking each order's original note, current tip, depth, remaining offered/requested amounts, lifecycle state, and create/update block heights.NoteTagSource::Subscriptionso the asset-pair subscription tag is registered on lineage creation and dropped when a lineage reaches a terminal state.Store-trait pswap methods (upsert/get/list/apply_pswap_round) implemented to match the SQLite backend: monotonic-depth guard, unknown-order rejection, terminal tip-freeze, and atomic round application that reconstructs payback + remainder notes into the input-note set (INSERT-OR-IGNORE), making private paybacks consumable.web-client(WASM/JS API)PswapLineageRecordmodel and fourWebClientmethods:getPswapLineages,getPswapLineagesFor(account),getPswapLineage(orderId), andbuildPswapCancelByOrder(orderId).api-types.d.tsand resource wiring.react-sdk(hooks)usePswapLineages,usePswapLineagesFor,usePswapLineage, andusePswapCancelByOrder, with exported types.