mirror of
https://github.com/Studio-Saelix/sencho.git
synced 2026-08-13 04:06:59 +00:00
c47b8eb8e9
* perf(frontend): overlap stack list and status hydration requests Fire /stacks and /stacks/statuses concurrently while committing the list first, so list_visible stays progressive and statuses overlap the list fetch instead of serializing behind it. The visible list now renders as soon as it commits (isLoading clears at list commit, not at status completion), and an atomic HydrationEvidence record derived at render time fails closed on node switches, list changes, and partial status payloads: lifecycle actions across the sidebar menu, editor toolbar, mobile editor, bulk bar, and deferred dialogs recheck a ref-backed readiness predicate immediately before any mutation. Status payloads are validated (bulk object format with typed fields, legacy string maps with recognized values); the legacy per-stack fallback targets the captured node and tracks coverage, so a total fallback failure escalates to a hard error instead of masquerading as ok. Pending, error, stale, and incomplete hydration states render distinctly in rows, filter chips, and the mobile masthead. Measured on the SEN-531 workload contract (local node, 6 stacks, Dev Mode ON): hydration gap 52.5ms median to 0ms, full hydration ~146ms to ~84-100ms (~42% improvement). 2590 frontend tests, visual regression 12/12, backend tsc clean. * fix(frontend): recheck hydration readiness in deferred lifecycle executors Deferred continuations (pre-deploy advisory proceed, external-network continue, update-readiness proceed, service-update proceed) dispatched against the node captured when the dialog opened, without rechecking readiness at the actual mutation boundary. A dialog opened on node A could therefore mutate node A after the operator switched to node B. Each executor now rechecks the ref-backed readiness predicate before starting any operation, releasing the pending deploy guard and surfacing a toast when blocked. Plain Save stays independent of status readiness (mobile editor and diff-preview plain-save mode are no longer gated); only Save & Deploy / Save & Reapply requires authoritative runtime evidence. Adds two-node delayed-response tests proving late prior-node list and status results cannot replace the current node's files, statuses, evidence, or loading ownership, plus deferred readiness-loss regression tests for the update and service-update dialogs. * fix(frontend): bind deferred executor readiness to the captured node The previous readiness rechecks validated the CURRENT node, but deferred continuations (pre-deploy advisory proceed, external-network continue, update-readiness proceed, service-update proceed) still dispatched with the node captured when the dialog opened. A dialog opened on node A could mutate node A after the operator switched to node B and B finished hydrating. The executor-boundary predicate now also requires the captured operation node to equal the current active node (effect-updated ref), so a fully-hydrated node switch still blocks the stale continuation. Adds ready-after-switch regression tests for the deploy continuation, stack update, and service update (switch to node B, hydrate, invoke the stored proceed, assert no operation session starts), plus focused plain Save vs Save & Deploy tests for the mobile editor and the diff preview. Restores the unrelated frontend lockfile metadata drift. * fix(frontend): guard external-network creation and reactive retry by node ownership The missing-external-networks dialog's createAndContinue posted network creation requests to the captured node before any ownership check, and the reactive 409 retry callback started a deploy directly without the executor guard. A dialog opened on node A could create Docker networks or deploy on node A after the operator switched to node B. Both paths now recheck hydrationReadyForNode before any mutation: the proactive dialog closes with the pending guard released and a toast when blocked, and the reactive retry refuses to start an operation session. Adds ready-after-switch regression tests for both flows asserting no /system/networks POST and no operation session after a switch to a fully hydrated node B. * fix(frontend): bind policy bypass, delete, and take-down confirmations to their opening node Three deferred confirmation paths remained unsafe across node switches: the policy-bypass retry validated current readiness but dispatched to the policy block's captured node; Delete and Take Down dialogs stored only a stack name and derived the operation node live at confirmation time, so a dialog opened on node A would mutate node B's same-named stack after a switch. Delete and Take Down targets now carry the opening node id in the overlay state (deleteTarget / takeDownTarget), and their confirm handlers verify hydrationReadyForNode against that id before mutating. The policy-bypass retry uses the same predicate against the block's captured node. The reactive 409 regression test now reaches the reactive path: the proactive preflight reports no missing networks, the deploy POST returns the 409, and the reactive refetch opens the dialog, after which an ownership change during network creation blocks the retried deploy. Also restores the unrelated frontend lockfile drift again. * fix(frontend): validate legacy fallback payloads and target stale refreshes at the current node The legacy per-stack fallback counted any successfully decoded JSON body as authoritative coverage, including a 200 non-array body or an array with malformed entries, which could authorize lifecycle actions on non-authoritative evidence. The fallback now requires a container array whose entries satisfy the minimal container shape; malformed responses fail closed and contribute zero coverage. A refreshStacks callback captured on node A and invoked after the operator switched to node B mixed a live list request (localStorage target) with node A statuses and evidence. refreshStacks now reads the current node from its render-synchronous ref, and the list request is targeted explicitly so both requests always share one authoritative node. Adds malformed-fallback regressions (non-array, malformed-array, partial-invalid) and a stale-callback-after-switch test asserting both requests target the current node and its state stays authoritative.
React + TypeScript + Vite
This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.
Currently, two official plugins are available:
- @vitejs/plugin-react uses Babel (or oxc when used in rolldown-vite) for Fast Refresh
- @vitejs/plugin-react-swc uses SWC for Fast Refresh
React Compiler
The React Compiler is not enabled on this template because of its impact on dev & build performances. To add it, see this documentation.
Expanding the ESLint configuration
If you are developing a production application, we recommend updating the configuration to enable type-aware lint rules:
export default defineConfig([
globalIgnores(['dist']),
{
files: ['**/*.{ts,tsx}'],
extends: [
// Other configs...
// Remove tseslint.configs.recommended and replace with this
tseslint.configs.recommendedTypeChecked,
// Alternatively, use this for stricter rules
tseslint.configs.strictTypeChecked,
// Optionally, add this for stylistic rules
tseslint.configs.stylisticTypeChecked,
// Other configs...
],
languageOptions: {
parserOptions: {
project: ['./tsconfig.node.json', './tsconfig.app.json'],
tsconfigRootDir: import.meta.dirname,
},
// other options...
},
},
])
You can also install eslint-plugin-react-x and eslint-plugin-react-dom for React-specific lint rules:
// eslint.config.js
import reactX from 'eslint-plugin-react-x'
import reactDom from 'eslint-plugin-react-dom'
export default defineConfig([
globalIgnores(['dist']),
{
files: ['**/*.{ts,tsx}'],
extends: [
// Other configs...
// Enable lint rules for React
reactX.configs['recommended-typescript'],
// Enable lint rules for React DOM
reactDom.configs.recommended,
],
languageOptions: {
parserOptions: {
project: ['./tsconfig.node.json', './tsconfig.app.json'],
tsconfigRootDir: import.meta.dirname,
},
// other options...
},
},
])