rules editor: TypeScript support with in-browser language service - #1202
Open
evgeny-boger wants to merge 17 commits into
Open
rules editor: TypeScript support with in-browser language service#1202evgeny-boger wants to merge 17 commits into
evgeny-boger wants to merge 17 commits into
Conversation
.ts rule files get full editor support: - Syntax: @codemirror/lang-javascript in typescript mode for .ts files; rules-store accepts/preserves the .ts extension (create, rename, copy). - Validation in the editor: a browser-side TypeScript language service (typescript + @typescript/vfs + @valtown/codemirror-ts) checks as you type - squiggles, hover type info, type-aware completions - seeded with the vendored wb-rules.d.ts builtin declarations. Same settings as the engine-side tsgo check (esnext libs, non-strict), so editor and controller agree. - Cost: everything heavy lives in a lazy chunk (~1 MB gzip: typescript 975 kB + lib.*.d.ts texts 74 kB) loaded only when a .ts file is opened; the rule page chunk stays at 16 kB and .js-only users download nothing new. - Static completions for .js files are generated from wb-rules.d.ts by scripts/generate-wb-rules-completions.mjs (npm run generate:completions). Engine side (wirenboard/wb-rules#221) additionally publishes tsgo check results as retained JSON on /wbrules/ts-check/<file> for future integration and logs them to the rules console. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AoTGMFABCCENSPaHThxAru
Up to standards ✅🟢 Issues
|
| Metric | Results |
|---|---|
| Complexity | 83 |
| Duplication | 0 |
NEW Get contextual insights on your PRs based on Codacy's metrics, along with PR and Jira context, without leaving GitHub. Enable AI reviewer
TIP This summary will be updated as you push new changes.
- Show the controller-side type-check verdict: subscribe to the retained /wbrules/ts-check/<file> topic while editing a .ts file and render each diagnostic as a page alert (danger/warn by severity, i18n en+ru). The in-editor language service stays the live check; this banner is the authoritative post-save result from the controller's own tsgo. Review findings fixed (adversarially verified multi-agent review): - language-service cache: reseed when the same file is reopened with different content (tsSync only tracks in-editor edits), and drop the cached promise on load failure instead of poisoning TS support forever - completion: mergeSources awaits each source, so static completions still answer when the async TS source resolves to null - unsaved .ts rules use a stable placeholder path - title keystrokes no longer rebuild the language service per key Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AoTGMFABCCENSPaHThxAru
React escapes on render; i18next's own interpolation escaping showed ' instead of quotes in the banner. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AoTGMFABCCENSPaHThxAru
The controller's tsgo diagnostics (retained /wbrules/ts-check JSON) now render as regular lint entries at the reported lines - squiggles and gutter markers merged with the local language service's own - with the tooltip labeled 'controller (tsgo)' so the two checks stay distinguishable. A mobx autorun re-triggers linting when new MQTT data arrives. The page-top banner (and its i18n strings) is gone. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AoTGMFABCCENSPaHThxAru
…heck Both checkers usually flag the same line with the same message, which doubled every squiggle. Controller entries matching a local language service diagnostic (same line, same message) are now dropped; only skew-only findings - the controller's unique value - render separately. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AoTGMFABCCENSPaHThxAru
…s from Editor.GetTypes Replaces the retained /wbrules/ts-check MQTT subscription (topic removed engine-side; it had unsolvable retained-state lifecycle issues). The controller verdict is now pulled on .ts file open and after each save. The language service is seeded with the CONTROLLER's installed wb-rules.d.ts (Editor.GetTypes) when reachable, so the editor validates against the API of the engine it is actually talking to; the vendored copy remains as offline fallback. This eliminates declaration version skew between UI and engine releases. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AoTGMFABCCENSPaHThxAru
User-reported bug: fix an error in the editor and the controller's old squiggle for that line stayed until the next save. Mechanism: while the error existed the controller entry was hidden behind the identical local one; fixing the line removed the local diagnostic and the stale controller entry (describing the last-saved file) surfaced. The verdict now carries the editor content it was computed for and renders only while the document still matches it - the local language service owns the screen while typing, and saving triggers a fresh verdict. Covered by controllerDiagsForDoc tests. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AoTGMFABCCENSPaHThxAru
- Check replies 'pending' while the controller's background check runs: poll briefly (700ms x 15), with a token so a newer check supersedes an in-flight poll loop. 'not-ts' (e.g. disabled files) and 'unsupported' clear the verdict instead of masquerading as clean. - De-dup by message prefix: the controller carries only the head line of chained diagnostics while the local service flattens the whole chain, so equality matching let every elaborated error double-squiggle. - Skip diagnostics that belong to another file (import/reference) - the reply now identifies them; anchoring them in the open file was wrong. - Fix stale transport comment (retained MQTT -> Check RPC). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AoTGMFABCCENSPaHThxAru
- H1/H2: the vendored wb-rules.d.ts and the completion list generated from it are excluded from eslint and tsconfig - the ambient engine globals (dev, log, require, ...) no longer leak into the whole app's type space (a typo'd log() call typechecked before), and the full-repo lint is clean again (0 errors, 0 warnings). - H3: an empty completion result no longer shadows later sources - the TS service answers with zero entries inside dev["..."], where the device-list source has the real completions; merged sources now fall through past empty results. - M1: snippets and generated globals are one static source (snippet variants first) - the generated signatures were unreachable behind the snippet source's catch-all match. - M2: the poll-timeout clear is token-guarded (a stale loop could wipe a fresh verdict). - M3: editor extensions are memoized on [isTypeScript, tsSupport] - rebuilding them per keystroke reconfigured CodeMirror and re-ran every lint source synchronously per character. - M4: the completions generator emits repo-style single quotes; regeneration is byte-idempotent against the committed file. - L1: controller types race a 3s deadline so firmware without Editor.GetTypes does not stall TS support for the 60s RPC timeout. - L2: post-save checks capture the exact saved content. - L3: leaving the page cancels the verdict poll loop. - L5: ControllerVerdict lives in types.ts per repo convention. - L7: new deps exact-pinned; typescript moved to runtime dependencies (dynamically imported in production); renaming a .ts rule to an extensionless title keeps .ts instead of silently becoming .js. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AoTGMFABCCENSPaHThxAru
Plain-JS rule files now get the same in-browser language service as .ts (allowJs): completions and hover reflect the controller's installed API via Editor.GetTypes instead of the build-time snapshot, plus live syntax checking. checkJs stays off, so wild ES5 gets no type-error noise. The generated static list remains as fallback when the service is unavailable (offline, pre-GetTypes firmware). Cost: .js-only users now lazy-load the language-service chunk on first editor open. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AoTGMFABCCENSPaHThxAru
The first environment build parses every bundled lib.*.d.ts - ~2s locally but 7s in a loaded sbuild chroot, past vitest's 5s default (seen on Jenkins PR-1202 #15). 30s budget for the two cold-start candidates. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AoTGMFABCCENSPaHThxAru
(The PR pipeline's version-bump check requires it; also gives the experimental debs a proper feature version.) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AoTGMFABCCENSPaHThxAru
- H1: type-aware completions actually surface the wb-rules API now. valtown's completion filter drops every ambient global (sortText "15") not on its hardcoded standard-JS whitelist, so defineRule & co never appeared once the language service loaded - and its non-empty answer shadowed the static sources, a regression for .js files too. Pass keepLegacyLimitationForAutocompletionSymbols: false and merge the snippet templates into the service's answers (snippets replace the plain entry of the same label; member accesses stay service-only). - M1: device/topic completions read the devices store at completion time instead of being snapshotted when the (memoized) extension array is built - devices arriving over MQTT after page load now show up in dev["..."], getDevice(...), publish(...) lists. - M2: the rename uniqueness pre-check now tests the path the rename will actually target (a .ts rule keeps .ts; only a fresh save defaults to .js) - renaming foo.ts to an occupied extensionless name no longer slips past the check and silently fails in the engine. - M3: typing the title of an unsaved rule no longer re-runs the language-service effect per keystroke (each run cost an Editor.GetTypes RPC); the effect is keyed on the stable service path. - L1: controller verdict diagnostics survive CRLF rule files - compare the checked content LF-normalized, matching CodeMirror's own normalization on ingest. - stale comments updated (the service runs for .js files too). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AoTGMFABCCENSPaHThxAru
…nd, delay(), nextMqtt() Sync the vendored wb-rules declarations with the engine's new promise-native library: spawn()/runShellCommand() return Promise<SpawnResult> (nonzero exit resolves; rejection only when the process cannot start), delay(ms) is the async setTimeout, and nextMqtt(topic[, timeoutMs]) resolves with the next live MQTT message (MqttMessage gains retained/qos, matching what trackMqtt callbacks always received). Completions regenerated. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AoTGMFABCCENSPaHThxAru
Matches the engine-side rename; completions regenerated. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AoTGMFABCCENSPaHThxAru
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AoTGMFABCCENSPaHThxAru
Bump the wb-mqtt-homeui Recommends floor from wb-rules 2.37.0 to 2.47.0~quickjs3 - the engine generation this editor's Editor.Check / Editor.GetTypes integration and .ts file support target. Recommends (not Depends) stays correct: with an older engine the editor degrades gracefully (vendored type fallback, bounded verdict polling), and old homeui keeps working against the new engine. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AoTGMFABCCENSPaHThxAru
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.
TypeScript support in the rules editor
Companion to wirenboard/wb-rules#221 (engine-side TypeScript rule support). With that engine installed,
.tsfiles in/etc/wb-rules/transpile and run natively — this PR makes the web editor a first-class place to write them.What the user gets
.tsrule files can be created, renamed, copied and edited (extension preserved everywhere; syntax highlighting switches to TypeScript mode).wb-rules.d.tsbuiltin declarations, sodefineRule,dev,PersistentStorage, timers etc. all carry real signatures.esnextlibs, non-strict), so what the editor flags is what the engine logs. The engine additionally publishes its check results as retained JSON on/wbrules/ts-check/<file>for future in-UI display of the authoritative post-save verdict..jsusers: the language service (typescript ≈975 kB gzip + lib texts ≈74 kB gzip) lives in a lazy chunk loaded only when a.tsfile is opened. The rule-page chunk stays at 16 kB..jsfiles is now generated fromwb-rules.d.ts(npm run generate:completions), so the two stay in sync.Implementation notes
@valtown/codemirror-ts+@typescript/vfsdrive CodeMirror integration; the virtual FS bundleslib.es*.d.ts/lib.decorators*.d.tsviaimport.meta.glob(no CDN — controllers are offline).ts-language-service.ts);tsSync()tracks edits after the initial seed.tsc --noEmitclean, eslint clean, 2418 vitest tests green (2 new for the language service, 3 for.tsfile handling in the rules store).🤖 Generated with Claude Code
https://claude.ai/code/session_01AoTGMFABCCENSPaHThxAru