Unreleased [Not released yet]
Part 9 of 11. Previous part
Fixed (464)
- A
{:expr}hole reads a/afterreturn,typeof,instanceof,in,void,delete,case,throw,doorelseas the start of a regular expression, and skips a//comment to the end of its line. Before, a}in such a regular expression or comment ended the hole. /_imganswers 503 withretry-after: 1when the blocking pool stops before a resize ends. Before, it answered 400not an image Wisp can resize, as for a bad file.- A list call of an
fsstore reads the folder once and keeps at most 1001 keys of the scan in memory. Before, each call read every key of the store into memory and sorted all of them. - On Linux with io_uring, the driver answers a request itself only for a connection that is open. Before, a closed connection whose entry was free still counted as a connection that waits, so the driver may try to answer for it.
wisp new genandwisp new gen --libreject the namegen, which edition 2024 reserves. Before, the new package did not compile. The build,wisp lspandwisp newuse one list of Rust keywords.wisp new --libnames the folder that it could not create in its error. Before, the error named the library root.- The test client's
websockettakes theBODY_LIMITof the route, as the server does. Before, a test socket took messages of up to 1 MB on every route. kvandblobswith thememorydriver give no cursor on a page of live keys that only expired keys follow. Before, the cursor led to an empty page.kvandblobsread back an empty metadata or content type asNoneon every store. Before,memorygaveSome("")andfsgaveNone.#[action]gives an action itscxwhen the body usescxonly after..(0..cx.n). Before, the macro read..cxas a field access, and the action did not compile.- The
azuretarget keeps all of the text of aset-cookiewith no=, as the value of a cookie with an empty name. Before, the name lost its last character. - The inlined critical CSS of
wisp build --staticchecksstyle-src-elemfirst, thenstyle-src, thendefault-src, as CSP3 does for a<style>element. It reads the directive names in any ASCII case. Before, it skippedstyle-src-elemand read only names in small letters. cx.form()rejects a multipart boundary with a quote at one end only (boundary="q), as the streamed upload reader does. Before, the form took off the one quote and read the body.WISP_DATA=OFF(any ASCII case) keepskvandblobsin memory, as it does the saved tables. Before, onlyoffin small letters did, andOFFwrote the stores toOFF/storage.wisp devbuilds the template again when only the action of a kept field or of a kept<select>changes. Before, the skeleton of the edit matched the compiled one, so the swap kept the old action.- The
node,bunanddenotargets use the default 65 s for aWISP_KEEP_ALIVE_TIMEOUTthat is not a number above 0. Before, the server took a negative value as the time. - An
fsstore deletes the file of an expired key when a get or a list finds it expired. A put of the same key at that time keeps its file. One process owns a data folder. Its first store call locks.wisp-store.lockin the folder. A second process waits forWISP_STOP_DELAY+WISP_DRAIN+ 5 s. Then its call fails with a 503 (fs:<dir>: another process uses it). Before, the file stayed until a put or a delete of the key, so the folder only grew. - With
WISP_ALLOWED_HOSTSset, the bridge answers theif-none-matchGET of a kept page with the kept 304. It makes no call into the wasm. Before, the bridge asked for that 304 with the hostwisp.invalidand got a 421. So each such GET went to the wasm. The test runs thenodebridge only. - A
headersrule with a condition adds its name to a blankvary, or to one with an empty member, and leaves no empty member. Before, a blankvarybecamevary: , cookie. The rules and thevary: accept, x-wispof a page that shares its path with an endpoint use one merge. Avarywith a*member stays as it is. - The
afterhook inllms/AGENTS.mdnames the fields of the reply:reply.status(a u16) andreply.headers(a Vec of (name, value) pairs).reply.header("name")reads one header, in any case. The sample adds a header with a push, and the build compiles it. Before, the reference did not name the fields. - Docs site:
cargo run --example playground -- todos apizips only these examples, andstatic/examples/apps.jsonkeeps its old Wisp commit. Only a run that zips every example inapps.jsonwrites the new commit. A part run fails whenapps.jsonhas no Wisp commit. Before, each run wrote the new commit, also when the other zips came from an older one. - A
use:action or an{@attach}that moves its element in its parent no longer changes the order of a{:#each}in the browser. To measure an element that is not laid out, gsap moves it to<html>and then before its next element. That place can be after the end of the item. Then new items went in before the last item. Now the browser code moves the element again, to the place it had before the action or the{@attach}ran. - io_uring: after the kernel drops a completion, the worker cancels each operation that may wait for it. When a cancel fails with an error other than
ENOENTorEALREADY, the worker keeps the slot marked lost and cancels it again after 2 s. Once per worker, it logswisp: io_uring cancel of a lost operation failed (<OS error>). Slots marked lost: <count>. The worker cancels them again. Before, when the lost completion was the one of that operation, the slot stayed in use for good, with no new cancel. - In the Rust of a page, the build reads a
<or>in an array length ([u8; 1 << 3]) as a shift or a comparison. Before,fn load(buf: [u8; 1 << 3]) -> Datafailed withloadreturns nothing, but the template reads aData. A struct with such a field, or with#[json(default = a > b)]on a field, lost its other fields. - In
{@props}, an=right after the>that closes a type starts the default:xs: Vec<u8>= vec![]. Before, the build read it as>=. - The build reads a non-ASCII letter as a part of a name in the Rust of a page and of a template. Before, it cut the name at that letter. It also read the
asorenumat the end of such a name as a keyword. - The auto-imports read the names in a raw format string, such as
TITLEinprintln!(r"{TITLE}"). Before, they skipped raw strings and did not import such a name. - A shebang line (
#!/usr/bin/env ...) on line 1 of a page file is not an error. The build does not copy it into the module, and the auto-imports skip its words. Before, it failed with the error that//!docs go at the top of the file. A/**/or////comment before the//!docs gave the same error. #[action]on an item that is not a function, such as#[action] struct A;, fails the build with its line. Before, the build accepted it, and the attribute did nothing.wisp_shared::rust::skip_raw_strreturns the end of input when no raw string starts at the index. Before, it panicked.- A log line of the io_uring worker ("wisp: io_uring under memory pressure" and "wisp: io_uring cancel of a lost operation failed"), of
css_varsandstylein a dev build, and of a data folder with no file lock does not panic when stderr is a closed pipe (EPIPE). Before,eprintln!panicked. - The output of
wisp build --static, of the prerender ofwisp buildand ofwisp check --types(the app binary) does not panic when stdout is a closed pipe (EPIPE). Before,println!andprint!panicked. Thewrote,removed,warnanderrorlines ignore a write error, and the build goes on. - The
wispcommand does not panic when stdout or stderr is a closed pipe (EPIPE). Before,println!,print!,eprintln!andeprint!panicked, and the exit code was 101. Now these macros ignore a write error, and the command ends with its own exit code.wisp mcpandwisp lspstill end when they cannot write a reply.wisp devstill stops when its output is closed, and its exit code is 1. - io_uring: a worker drops the refused
PROVIDE_BUFFERSbuffers at the 16th refusal in a row, as its log line says (wisp: io: the kernel refused receive buffers 16 times in a row). Before, it dropped them at the 17th refusal. - Gate:
WISP_SKIP_STEandWISP_SKIP_GRAMMARskip their step only with the value1, as the docs say. Before, any value skipped it, also0. Thegrammar generatestep takes the localtree-sittercommand only when its version is exactly 0.25.10. Before, 0.25.100 passed too. - Gate: the
stampsstep counts a fenced line inside a shown<Measured at="...">block in the hash of its suite. Before, a number in such a line could change with no new stamp. The step also reads.wispfiles undersrc/routesof the docs site, andstamps --stalelists a suite whose summary file is missing. - Gate: the
textstep fails on a tracked path that is not valid UTF-8. Before, it skipped the path. An emptyWISP_DOCS_DIRmeans the default docs checkout in the gate, intests/gate/ste.pyand in the coverage tests ofwisp-web. Before, the gate and the coverage tests read it as an explicit path. - Gate: the speed budgets count a script file once when the page names it in a
modulepreloadlink and in a<script src>. They read an attribute with blanks around=, such ashref = "x". - A hole whose
Displayreturns an error no longer panics instyle:,style=${..}, a URL part or a custom property (--name). The code uses the text written before the error, as a text hole does. Before,to_stringpanicked and the request failed. - A style value keeps
scroll-behaviorandoverscroll-behavior(transition: scroll-behavior 1s). Each otherbehaviorstays refused. Before, any value with the textbehaviorwas refused. - A spread of a
HashMapwrites its attributes in the order of the keys. Before, the order changed from one render to the next. - A component call with
--namevalues that all fail the check writes no emptystyle=""on the root element and no<wisp-css>wrapper. Before, it wrote them. - The test
docs_cover_the_surfacereads a flag, a knob and an attribute as a word of its own, and--[no-]xas--xand--no-x. Before,-pmatched inside--port. - The test
docs_cover_the_surfacereads the methods ofResponseinresponse.rsandws.rs. Before, it looked forimpl Responseinlib.rs, found none and checked no method. - The test
docs_cover_the_surfacefails for a listedimplheader that is not in its file, a source file that is missing or empty, and anENVentry that no read uses. It also checks the types, constants and macros at the root ofwisp::. - The build of the CLI shows a read error of the docs site pages as a warning (
Could not read the docs for llms-full.txt). Before, an unreadable pages folder gave a shorterllms-full.txtwith no warning, and a read error saidCould not write. llms-full.txttakes a<Measured>block for a block withatonly when the attribute isat=". Before, an attribute that ends inat, such asformat="x", counted. A page with an empty front matter, or a closing---at the end of the file, loses its front matter inllms-full.txt.- Gate:
python3 tests/gate/ste.py --updatecreates no docs baseline file. Before, it wrote an empty one, and--seed-docsthen refused to run. WISP_NO_CRAWLandWISP_DEAD_LINKScome from the process, else from.env, as every other variable. Before, the crawl of the export read the process only, and a value in.envdid nothing.- The scrub of an export reads the name of a tag as the crawl does: it ends at white space,
/or>.<style-guide>is no<style>. Before, the scrub read it as<style>and skipped to the next</style, sononcevalues stayed in the file and the files changed on each build. - A character reference with a sign (
&#+5;) is no number, and�is U+FFFD. Before,&#+5;decoded to U+0005 and�to a NUL character in a link or an id of the crawl. - With
WISP_LISTEN, a socket file that gives another error than refused or not found when the server connects (for examplePermissionDenied) keeps that error. Before, the message said that another server was listening on it. - The name of a tag in the crawl of an export ends at ASCII white space,
/or>, as in the HTML rule. Before, the crawl ended it at any Unicode white space. - A redirect rule with a
has:ormissing:condition on a cookie, a header or the host adds the matchingvaryto its redirect, as a header rule does. Before, a cache could keep the 308 and send it to a visitor who does not match the rule. - Critical CSS reads
<SCRIPT>,</SCRIPT>,<STYLE>,</STYLE>andSTYLE=in any case, as HTML does. Before, a page with</SCRIPT>stopped the scan of its markup, and Wisp cut the rules of later classes from the inline style. - On HTTP/1.1 the metrics (
METRICS_KEY), the JSON log and the traces record the 500 that Wisp sends for a response status that it cannot send (0 or 1xx). Before, they recorded the invalid status. - The docs of
Table::updateandTable::add_unlesssay that the closure runs with the table locked. The docs ofBrowser::gotosay that the path goes to the server as written. WISP_DATAin.envworks like the same variable in the process when the server starts as root withWISP_USER: it gives that folder to the user. Before, this step read only the process, and it did not give the folder of.envto the user.WISP_LAMBDA_STREAM=1in.envturns streaming on, as in the process. Before, Wisp read only the process.wisp build --target pagesreadsSITE_URLfor the cron Worker from the process, else from the.envfiles. Before, it read only the process and wrote an emptySITE_URLwhen the value was in.env.- A
Cargo.tomlthat Wisp cannot read (for example a file that is not UTF-8) is a build error that names the file and the cause. A missingCargo.tomlstill means no rules. This holds for the rulesredirects,rewrites,headers,corsandimages, and for the rulesscript,strict,typecheckandlintof[package.metadata.wisp]. Before, Wisp read an unreadableCargo.tomlas empty, and these rules were silently missing. - A
Cargo.tomlthat Wisp cannot read (for example a folder namedCargo.toml) is an error that names the file and the cause. A file that is not UTF-8 text gets the hint to save it as UTF-8. A missingCargo.tomlstill means no keys. This holds for the keysauto,i18n,use,extends,build_plugins,prefixes,scripts,public_envandversion_pollof[package.metadata.wisp]. It also holds for the tablesdev.proxy,dev.runandvercel, and forwisp checkandwisp package. Before, Wisp read an unreadableCargo.tomlas empty there, and these keys were silently missing. - A
.envfile that Wisp cannot read (for example a folder named.env) is an error that names the file and the cause. A file that is not UTF-8 text gets the hint to save it as UTF-8. A missing.envfile still means no variables. This holds for thePUBLIC_*variables of a build and forSITE_URLof the cron Worker ofwisp build --target pages. Before, Wisp read an unreadable.envfile as missing, and these variables were silently missing. - A value of
AWS_LAMBDA_RUNTIME_APIin.envno longer changes the folder ofkvandblobs. Only Lambda sets this variable, and Wisp reads it from the process only. Before, a value in.envmoved the folder to/tmp/wisp-data/storage, while the saved tables kept their folder. - A path that ends in
/goes to the route of the same path without it, also beside an optional last segment. Before, withblog/newandblog/[slug]/[[part]],/blog/new/went toblog/[slug]/[[part]]withslug=newundertrailing_slash(Always)andtrailing_slash(Ignore). Now it goes toblog/new. The static export and the prerender crawl ask for the form with/underAlways, so they got the wrong page too.
Security (99)
/_imgdoes not serve a file ofstatic/when it cannot resolve the path of the file (a 404). Before, a failed check of a link to a hidden folder served the file.- A file in
static/with a dot segment in its path (.env,.git/config,a/.hidden/x) is not embedded, not served, not copied bywisp build --static,--spaor--target, and not served bywisp dev. Before,GET /.envsent the file. A symlink to such a path is hidden too./_imgand<img src>skip these files. The folderstatic/.well-knownstays public (/.well-known/security.txt). A dot segment below it is still hidden. The build prints one warning for each hidden entry (the first hidden name of a path, such as the folder.git), with its full path, in the formWisp does not serve <path>: its path has a dot segment.wisp build --staticshows it twice: the build prints it and the copy ofstatic/prints it. Wisp writes.nojekyllitself, so there is no knob. An earlierwisp build --staticmay have left a file such asdist/.env: it stays. Deletedist/once. - A page's own
SIGNED_IN,MIDDLEWARE,RATE_LIMITorCORSnow gates every method of the endpoints of its route (+server.rsormod server), also the member route/[id]. Before, it gated only aget, or apostof a page with actions, so aput, apatch, adelete, apostof a page with no action and each method of/[id]ran without the page's guard. An upload on such a route is also refused before the server reads the body. An endpoint that must stay public goes to another route.CORSruns once and before every guard of the route: the endpoint's, else the page's. Before, a page and an endpoint with aCORSeach sent anaccess-control-allow-originheader, and a refusal by a page guard had noCORSheaders. The OpenAPI operations of an endpoint under aSIGNED_INof its page or a layout name the session and the 401. The member route takes its guard from the+page.wispof its folder, also when a.mdpage sits beside it or the page is a rest page ([...p]). Aconst CORSinsrc/hooks.rsand one in a route stop the build, because both would send the header. A keptCACHE_PUBLICGET of an endpoint with aCORSsends no CDN header. Only routes with such a page guard run more code. - A page under
SIGNED_INorMIDDLEWARE(on the page or a layout) is not in/sitemap.xmlor/feed.xml(with itsentries()). It is not in the"offline": "all"service worker list. It is not in the files ofwisp build --staticandPRERENDER. Before, the sitemap and the feed named its address, title and description. The worker could keep it. A guard that lets the export in (an IP check on loopback) wrote the page to a public file. The static export prints a warning for the page. It skips a link to the page, with no dead-link error. Before, a public link to a guarded page could write the page to a file. A guard you write in code (abeforehook or aloadthat redirects) does not count: useSIGNED_INorMIDDLEWARE.