Changelog

All versions and downloads Jump to Added | Changed | Fixed | Security

Unreleased [Not released yet]

Part 7 of 11. Previous part

Fixed (464)

  • The gate step rt all-features clippy runs clippy on wisp-web-rt with --all-features. The clippy step did not turn on og-png and img.
  • <img blur>, priority and data-wisp-raw leave no extra space in the served tag. The build deletes the white space before the attribute too.
  • The examples/nojs upload page and the forms docs say that a RateLimit runs after Wisp reads the upload body, so it counts requests and not bytes.
  • Examples: examples/jobs POST /ping counts dead pings in its cap, and the nightly cron puts them back for another try. examples/nojs takes member names of letters, digits, - and _ only. Its /upload page needs a signed-in member and keeps at most 20 files for each member. Its recovery link goes in a flash cookie, not in the address. examples/api GET /healthz answers the plain text ok, and the key dev-key exists only when the server listens on a loopback address.
  • Examples: examples/jobs POST / refuses a greeting with 503 when 1000 wait. examples/nojs /upload takes 15 page requests per minute per client, about 3 per upload (the form, the post, the redirect). examples/demo /join checks plan (free or pro) and each of days. The examples/websocket page sends only on an open socket. The examples/kit-app /cdn page loads confetti.browser.js with an integrity hash and crossorigin="anonymous".
  • In CI (CI set, not empty and not false), a check that would skip makes the gate fail. These checks are: no docs checkout, no VS Code for the grammar test, no wisp for the Prettier plugin test. The wasm clippy step runs in wasm mode too. wisp-gate changelog --tag without a value is an error. The stamps check reads a one-line <Measured> block, ignores case without shifting offsets, and skips only the exact changelog page and exact file names. The VS Code test uses a private temp folder, and the tree-sitter coverage test matches whole tokens. The release job sets WISP_SKIP_VSCODE=1, the explicit skip of the VS Code scope checks, because its runner has no VS Code. The CI job check clones wisp-docs into a folder per run, sets WISP_DOCS_DIR and WISP_SKIP_VSCODE=1, and deletes the clone at the end. The cli test prettier_plugin_keeps_every_form runs the Prettier plugin test. With no node, no npm or no network, it skips on a local machine and fails in CI.
  • Build: a client block shows a number that has no JSON form (0xFF, 1_000, 1e21) as the browser paints it, not as {}. A #[remote] fn named pre or r#name fails at build. The Rust scan reads '[' in an attribute, = inside <> in a const type, raw identifiers, and an error that a use of the app binds. The .live() listen call survives a // comment and escapes the table name. The build id fails when src/ or static/ cannot be listed. .wisp/route-types keeps files that the build did not write. A path in a generated // note cannot end the comment. Class fields of a script are found in one pass.
  • In an app with src/locales, only a browser module that says t(…) loads i18n.js. Before, every module loaded it.
  • A transition whose duration is no number (NaN, a string) plays for 0 ms. Before, a custom one had no frames and the Web Animations API threw on it.
  • #[validate(pattern)] with \0 before a digit (\01) stops the build. The browser's v flag refuses it, and the server read it as \0 and a digit.
  • <html dir> follows a script subtag: sd-Deva, ug-Latn and ks-Deva are left to right, az-Arab is right to left. ks alone is right to left, and so are prs, azb, pnb, arz, ary and other ISO 639-3 codes of right-to-left languages. A subtag after a singleton (ar-u-nu-latn) is no script.
  • A literal #[validate(pattern)] is now also the pattern of the field that <form fields> writes, with a \ before each sign in a class. The browser's v flag refuses [a-z0-9-] and other unescaped signs in a class and then skips the whole check. Now it reads the same sets as the server. && or -- in a class, or a lone ] or }, stops the build.
  • The benchmark scripts in bench/rust-fastest fail on bad data. ir.py fails a rep when the body length changes. sc.sh skips the row when sc.py fails. io-ab.sh counts online CPUs, deletes old out/io-ab/*.tsv at start, writes the perf counters per request, and stops a hung server with kill -9 after 10 s. The golden live-page test stops Node after 120 s and fails with a message.
  • The script passes (scopes, runes, minify) run in linear time. Before, a big module with many statements in one block or many a => b => c arrows took time in the square of its size.
  • A type nested more than 200 levels deep in TypeScript is a build error. Before, it could overflow the stack of the build.
  • extra.js and the other runtime parts go out when only a lib file or a layout load names them. Before, a lib file that made a persisted store imported a file that the app did not serve.
  • A release build minifies a lib file once. wisp build --analyze minifies wisp.js and live.js once, not once per route.
  • wisp.d.ts declares store, persisted, derived and portal.
  • wisp check --types reads let x: fn(u8) -> T and finds the let of the name itself, not a word that ends in let.
  • The prerender errors have no stray spaces in them.
  • A kept +layout.js load that fails after invalidate() or invalidateAll() runs again on the next navigation. Before, the next navigation with the same params and URL gave its old data.
  • A release build without source maps could move a kept if (browser) body out of its outer if, else or loop (for example if (x) if (browser) (g)();), so it ran every time. The build now writes a kept or removed body as one block.
  • With public_env = true, a prerendered page (PRERENDER) held the PUBLIC_* values of the build machine. The server now writes its own values from its start into each prerendered page, once at its start.
  • The build id (version, version_poll, <meta name="wisp-build">) skipped src/main.rs, modules in subfolders, static/ and Cargo.lock. It is now a hash of each file in src/ and static/, of build.rs, Cargo.toml and the nearest Cargo.lock (up to 8 parent folders, with the Cargo.toml beside it).
  • The docs of beforeNavigate named only a form's redirect as goto. A server redirect from invalidate, invalidateAll or a refresh is goto too, and cancel() stops it.
  • invalidate(key) no longer throws when the page has a kept instance with no +page.js load.
  • With a base path (WISP_BASE), each #[remote] call got a 404: the server read the request path without the base and compared it with the path with it. Now the server compares paths without the base.
  • A static export (wisp build --static, PRERENDER) wrote a random CSP nonce (cx.nonce()) and a clock-based <form guard> token into the files, so two builds of one commit differed. The export now empties both in the page bytes.
  • wisp ui Select: the hint is outside the label, as in Input and Textarea, so it no longer joins the accessible name.
  • wisp ui Tooltip: without script, the wrapper (role="group") carries aria-describedby. With script, the first focusable child carries it as before.
  • wisp ui Menu: ArrowUp or End on a closed menu keeps focus on the item the key chose when the menu opens.
  • #[validate]: a rule with an empty value (min =) stops the build with "min needs a value: min = 1", as a rule with no = does.
  • #[model] makes an Account only when email or name is text (String, &str or Email) and a plain hash is a String. Before, name: Vec<u8> gave an error in code the macro wrote.
  • {@element}: a prop named i, c, v or put stops the build (W0105). Before, it took the place of the element's own state.
  • src/fonts.txt: a weight outside 1 to 1000, a range from high to low, a family with no letter or digit, or two families with one file name (Open_Sans, Open-Sans) stops the build.
  • wisp::dir and <html dir>: ku (Kurmanji, Latin script) is left to right. ckb and a locale with the Arab script (ku-Arab, pa-Arab) are right to left.
  • #[validate(pattern)]: \s is the browser's white space, with U+00A0, U+FEFF, U+2000 to U+200A and the other Unicode spaces. Before, the server took only ASCII white space.
  • #[validate(pattern)]: a count such as a{+5} stops the build, as does an escape that the browser refuses (\- outside a class, \,). Before, the server read them.
  • #[validate(pattern)]: the build reads a pattern with a Rust escape (\u{..}, \x.., \0) too, and stops on one that it cannot read. Before, it skipped the check.
  • The TypeScript client of wisp openapi parses again. Six lines of its fetch helper held Rust text (pub(super) const), so each generated client was a TypeScript syntax error. A test now parses the client with Node.
  • Zed: the extension fetches the tree-sitter grammar from https://code.wispweb.dev/wispdev/wisp, where its pinned commit is. The gate checks the grammar pins against the first-parent history of main, so a pin to a merge commit passes.
  • Build output from cargo: a message longer than 64 KiB is no longer split into pieces that lose the error. The line limit is 1 MiB and a longer line is cut, not split.
  • wisp service install on an installed service upgrades it: systemd restarts the unit, launchd unloads the old job before it loads the new one, and Windows stops the task before it copies the build. A Windows task command over 261 characters is an error with that reason.
  • wisp preview checks the port and waits for the app on HOST, not always 127.0.0.1. --port 0 is an error.
  • wisp dev --port 0 works again: the system picks a free port. Only wisp preview rejects port 0.
  • An export (wisp build --static, a prerender) does not count against RATE_LIMIT or cx.limit. Before, a later export in the same process could skip a page with RATE_LIMIT as a 429.
  • wisp::browser! and wisp::test::browser_at work with a snap Chromium. They read the DevTools port from the "DevTools listening on" line of stderr when the profile file is not visible. A snap Chromium has its own /tmp.
  • wisp dev --https: a certificate time from 2050 is a GeneralizedTime. A write error ends the whole TLS session at once.
  • wisp route add <path> crud writes nothing when its routes would match the URLs of another route, as wisp route add does for one file. It adds struct Post to src/db.rs unless the file declares it: a comment or a string with the text does not count.
  • wisp lsp: a bad header or a bad Content-Length gets a JSON-RPC error (-32700) and the server reads on from the next frame. Before, it ended. A file:///c%3A/x URI is a drive path only on Windows.
  • The TypeScript types from a Rust raw identifier (r#type) name the field type.
  • A page fetched ahead and not followed leaves the map of wisp.js after 10s, and the code cancels its stream, also one that was not read ahead. Before, each link hovered stayed in the map.
  • A thrown value whose String() throws (Object.create(null)) is reported once. A throw in a batch no longer stops later batches from running.
  • t('toString') and other Object.prototype names are no message.
  • animate:flip times and the frames of a custom transition stop at 10s. Before, a huge duration made a huge frame list and the tab ran out of memory.
  • crossfade forgets a place after 20s. Before, its maps grew with every new key.
  • Bench tooling: a failed --routes run of bench/native-rank/run.mjs keeps the earlier result file. run.sh all goes on after a failed build or pass and exits non-zero at the end. The native-rank report gives the reason for an unranked cell per route and lists an unranked ^ cell. The bench/rank report gives a noisy Wisp cell a losses row, as for Hono. merge-reruns.mjs accepts an original cell with no runs and ignores a null p99. The build bench keeps the artifact of a contender with no build step. bench/rust-fastest/rank.py marks a workload INVALID when a contender has no row or the run counts differ. The bench/site-speed proxy uses one gzip size rule on both paths, keeps the Vary header of the app and replies 400 to a bad percent escape.
  • Benchmark harness: a run is INVALID, never a wrong number, in more cases. The TFB Express and Hono entries write the randomnumber field of /updates, clamp the query count to 1..500 and answer 500 on an error. The TFB database tables mark a cell with steal over the bar, too few runs, no p99 or errors. io-ab.sh warms up each route and counts answers that are not 2xx. site-speed rejects a failed Lighthouse run, a non-2xx page, a stopped app and a busy port. quiet/summarize.mjs treats non200, bad and load_bad above 0 as a failure. bootstrap-target.sh checks for the bench box in every mode. bench/app /compress has the same rows and layout as the other contenders, and Express and Fastify render it per request. The native-rank /list escapes each item per request in Wisp, Actix and Axum. Fastify validates /echo without type coercion. wisp-load reports the top of a latency bucket, not its bottom.
  • A 304 for an embedded text file of 256 bytes or more says vary: accept-encoding, as its 200 does (RFC 9110 15.4.5). Before, the 304 had no vary. This holds on the edge too.
  • Error::redirect(304, ..), (305, ..) and (306, ..) send a 303 with the location. Before, they sent a status that no browser follows. The statuses 300 to 303, 307 and 308 stay.
  • if-none-match and if-match read a comma inside a quoted ETag as part of it (RFC 9110 8.8.3). Before, an ETag such as "a,b" matched nothing, so a 304 never came for it.
  • A template with many { and no }} checks and formats in linear time. Before, each {{ looked for its }} to the end of the file, so the editor waited seconds on a file of 200 KB.
  • wisp dev ends the CSS watcher and all processes that it started, also when WISP_SASS names a .bat shim on Windows. Before, only cmd.exe ended.
  • wisp lsp reads the body of a message as the bytes arrive. Before, a header with a big Content-Length took up to 64 MiB of memory at once.
  • A #[json(skip)] field with the name of a stamped field (created_at) is no longer read from the JSON. On the edge target, a parked Cx gives back the room that a big request grew in its headers.
  • wisp test --coverage reads a rustflags = "-D warnings" line of a Cargo config file that has a comment after it. Before, it dropped the flags and said the form was not read.
  • wisp test --e2e starts the app again on a new port when another program takes the port first. Before, the test failed.
  • wisp client --out refuses a symbolic link. Before, it wrote through the link.
  • wisp dev lists the files of a folder once when a symbolic link also names the folder. Before, a folder and a link to it gave each file twice.
  • wisp lsp remembers a hung rustfmt format for each document. Before, a format of another document made it forget the hung one.
  • In wisp lsp, a name that ends in xfn is no longer a declaration of an exported function.
  • A page field with a name that starts with pub, such as published, public or pub_date, keeps its name. Before, the scan cut pub from it, so published became lished and the build failed in the generated code.
  • A <form ACTION="?/del" GUARD> in capital letters, and the other form attributes fields, confirm, once, method and class, work as in small letters. Before, the build read them in small letters only, so a guard form named a wrong action.
  • An attribute value such as value="Next &rarr;" shows the arrow. Before, the build wrote &amp;rarr;. The build now reads the Latin-1 names (&copy;, &eacute;), &mdash;, &ndash;, &hellip;, the quote marks, the arrows and &hearts;, and a number with more than 10 characters (&#0000000065;). A string of & with no ; is read in linear time. The same decoder serves the props of a component and the live attributes. The build does not read the other HTML names.
  • A {:@render} of a snippet with a block that it does not open or close is a build error ("closes a block it does not open"). Before, the build stopped with a Rust panic. A snippet that a zero-parameter snippet defines no longer stays defined after the render, so a second {:@render} of it no longer fails with "already defined". A snippet that ends with {~/snippet} or {/snippet~}, or holds {~#snippet}, is found in a client block and below its use.
  • :global(.a, .b) p in a <style> keeps its comma list in one selector, :is(.a, .b) p. Before, the comma cut the list, and .a lost its scope.
  • :global(a::before, b) is a build error. A :is() list ignores a pseudo-element, so the list would match nothing.
  • A C raw string (cr"...", cr#"..."#) in a page block is skipped as a literal, as br"..." is.
  • <a HREF="/x" ACTIVE> in capital letters marks the current page as <a href="/x" active> does.
  • With WISP_BASE, a link gets the base in an unquoted value (href=/about) and with blanks around = (href = "/about"). It also gets the base after a hole in the same tag. A hole that cuts a <script> or <style> text leaves the text as it is, so the CSP hash stays true. A link that has the base and then any blank (href=/app and a newline) is not changed again. Before, those links kept the path without the base.
  • {:else}, {:then v} and {:catch e} end at the first } as on the server, and a {:expr} hole ends past a JavaScript string, template or comment. A snippet that closes a block and opens another of the same kind is an error ("closes a block it does not open").
  • A raw string with a quote inside (r#"say "hi"#) in a page block no longer hides a later mod server or let. Before, the endpoints of the page had no route.
  • TypeScript in a client script: the erased type, interface, declare, import type, overload and as clauses no longer join the lines around them. Before, console.log then a type line then (async () => {})() ran as console.log(async ...). A line break before as ends the statement, as in TypeScript.
  • {:...} holes end at the end of the JavaScript. Before, a string such as 'a}', a template with }, or a /* in the code ended the hole too soon.
  • A non-ASCII character before an attribute name no longer stops the build when WISP_BASE is set. A <script> or <style> text keeps its bytes under a base path, so the CSP hash of an inline script stays right.
  • wisp::og(.., "auto") writes the picture path under WISP_BASE. Before, the og:image of every page of an app under a base path was a 404. The server serves static/ under the base.
  • A headers rule that names a header of a kept or baked page replaces it. Before, the 200 had the header twice (for example two cache-control lines). A cors rule also added a second access-control-allow-origin beside the route's.

Next part (8 of 11)

Source: CHANGELOG.md