Skip to content

feat(cli): type headless scratch scripts - #549

Draft
pgherveou wants to merge 5 commits into
mainfrom
feat/headless-script-types
Draft

feat(cli): type headless scratch scripts#549
pgherveou wants to merge 5 commits into
mainfrom
feat/headless-script-types

Conversation

@pgherveou

@pgherveou pgherveou commented Aug 28, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • generate a standalone declaration for the truapi, host, and assert
    globals injected into headless host scripts
  • copy the matching declaration beside each managed scratch script and reference
    it with a portable relative path
  • ship and verify the declaration in source packages and prebuilt CLI archives

Verification

  • cargo test -p truapi-host-cli
  • make cli-runner
  • cargo +nightly fmt --check --package truapi-host-cli
  • cargo +nightly clippy -p truapi-host-cli --all-targets --all-features -- -D warnings
  • regenerated truapi-dts.ts and script-types.d.ts with no diff
  • make e2e-cli-update
  • live Cargo-installed /script flow with Neovim diagnostics returning no errors

@illegalcall

Copy link
Copy Markdown

@lore-bot-app review

@lore-bot-app

lore-bot-app Bot commented Sep 5, 2026

Copy link
Copy Markdown

Review

Summary

This adds editor types for headless /script scratch files. scripts/bundle-truapi-dts.mjs gains a second output: a standalone rust/crates/truapi-host-cli/js/script-types.d.ts (5,390 lines / 197 KB, committed) assembled from the built dist/*.d.ts plus a hand-written block declaring the truapi, host, and assert globals. create_scratch_script copies that bundle beside every new scratch file and seeds the template with a relative /// <reference path> plus export {}. The Makefile copies it into target/dist, cli-dist puts it in the archive, release-cli.yml carries it through the artifact hand-off and asserts both files are present, e2e-cli-update.mjs checks it lands in the install dir, and CI adds a tsc -p rust/crates/truapi-host-cli/js/tsconfig.json step type-checking a new runner-types.fixture.ts. README/SPEC updated.

Five files were withheld from the diff I was handed (script-types.d.ts, js/tsconfig.json, src/script_runner.rs, scripts/bundle-truapi-dts.mjs, scripts/e2e-cli-update.mjs). I read them from the checkout at this branch instead, so they are covered — but from the working tree, not from the diff text.

What the record says

No prompt-injection or instructions addressed to a reviewer in the diff.

Concerns

  1. script-types.d.ts is committed and now load-bearing for cargo test, in a repo that is concurrently untracking generated output. bundle-truapi-dts.mjs:214 writes it, ci.yml:129 requires it committed, and script_runner.rs:122 ?s on reading it. The existing test scratch_script_matches_the_public_example calls create_scratch_script, which in a test binary falls back to CARGO_MANIFEST_DIR/js/runner.ts — so cargo test -p truapi-host-cli now fails on a clean tree that hasn't run codegen. Under chore: untrack generated Rust and iOS outputs, generate on demand #551's model (generated files untracked, produced by make codegen) that is a broken default. Decide the policy with chore: untrack generated Rust and iOS outputs, generate on demand #551 before merging, not after.

  2. Script authors get completion but cannot name a single type. script-types.d.ts has top-level exports (export interface HostContext at the tail, export declare function createMessagePortProvider at :4995), so the whole file is a module. declare namespace T at :429, TrUApiClient, HexString, and the inlined neverthrow Result/Ok/Err are therefore module-local. Only the three globals in declare global escape. An author cannot write let a: ProductAccountId, cannot type a helper that takes a Result, and cannot import from @parity/truapi because the whole point is that it isn't installed. HostContext is exported from a file nobody imports, so it is unreachable too. The fixture works around this (const accountProductId: string = account.dotNsIdentifier, runner-types.fixture.ts:13) rather than exposing it. The playground bundle solves the same problem with declare module "@parity/truapi"; doing that here as well would cost nothing. README.md:36 and SPEC.md:836 both read as if authors get the full typed surface.

  3. 197 KB copied per scratch file, unbounded. script_runner.rs:142-160 writes a full copy of the bundle next to each uniquely-named scratch script in the host's scripts/ directory. Ten /script presses is 2 MB; there is no pruning path in the diff or SPEC. SPEC.md:836-842 justifies the copy by durability across version removal and session promotion — a single shared script-types.d.ts per scripts directory keeps that property for every case except a scratch file moved out of its directory, at 1/N the cost.

  4. tsc is invoked at a hardcoded root path that the repo elsewhere treats as unreliable. Makefile:107 and ci.yml:135 both run node_modules/.bin/tsc, but root package.json has no typescript dependency — it only appears at js/packages/truapi/package.json:79 (^6.0) and reaches the root via workspace hoisting. The repo already knows this is not guaranteed: Makefile:61 and js/packages/truapi/scripts/ensure-generated.sh:49-52 both probe root and js/packages/truapi/node_modules. The two new call sites skip that and fail with a bare "No such file or directory". Either add typescript to root devDependencies or reuse the existing fallback.

  5. The any scrubbing in bundle-truapi-dts.mjs:156-161 is two literal-string regexes against a third-party .d.ts. A neverthrow version bump that reformats A extends readonly any[] silently no-ops both replacements and reintroduces any into a committed file. Nothing catches it: .prettierignore:11 excludes the file, npm run typecheck does not cover it, strict: true does not flag explicit any. Separately, the rewrite is not type-preserving — Fn extends (...args: readonly any[]) => any becomes (...args: never[]) => unknown, so fromThrowable/fromPromise type differently in the editor than in the package the runner actually executes against.

Smaller, same-area:

  • script-types.d.ts:4989,4995 ship createWindowProvider / createMessagePortProvider referencing Window and MessagePort to authors writing Bun scripts. js/tsconfig.json gets DOM implicitly (target: ES2022, no lib), so CI is green — but the file now requires a DOM lib wherever it lands, and those declarations are unusable from a headless script.
  • script_runner.rs:153-156: the non-AlreadyExists error branch returns without removing the .ts file created at :134, leaving an orphaned empty scratch script. The AlreadyExists branch just above does clean up.
  • make cli-dist does not run the type-check that cli-runner now does (Makefile:104-107 vs :110), so a local make cli-dist can package an unverified bundle.

Questions for the author

  • How does this interact with chore: untrack generated Rust and iOS outputs, generate on demand #551? Is script-types.d.ts meant to stay tracked as a deliberate exception, or should it move to make codegen output with the CLI test regenerating or skipping?
  • Why hand-write Encoder/Decoder/Codec/ResultPayload/HexString/CallErrorValue at bundle-truapi-dts.mjs:166-181 instead of inlining scaleBody, which the playground path already reads from the real scale.d.ts? Drift there is caught by the fixture's tsc, but only as an opaque failure in a generated file.
  • Was failing /script outright on a missing declaration bundle (script_runner.rs:122) chosen over falling back to an untyped scratch file? The test at the bottom of the file pins the hard failure but the reasoning ("recreates the original failure") is not in the record.

🤖 Reviewed by Lore (Parity knowledge base) · 27 agent turns · 312.6s

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants