Skip to content

Releases: offerrall/FuncToWeb

2.7.6

Choose a tag to compare

@offerrall offerrall released this 29 Sep 19:59

Documentation only: the docs are rewritten as nine short pages, still covering every public name, parameter and route. The code is the same as 2.7.5.

2.7.5

Choose a tag to compare

@offerrall offerrall released this 29 Sep 12:48

Changed

  • Documentation only: the README's Documentation list links each page on the
    documentation site, so readers on GitHub and PyPI land there. The code is the same as 2.7.4.

2.7.4

Choose a tag to compare

@offerrall offerrall released this 29 Sep 08:54

Changed

  • Documentation only: the README becomes a short entrance to the documentation
    site at https://offerrall.github.io/func-to-web/, and docs/overview.md holds
    the introduction and its fuller examples (the API, /doc, FastAPI, sdk.js,
    how it works, the CRUD, OpenForm, files, streaming, capabilities and the
    comparison). The design notes are listed as maintainer pages.
  • The layers and pytypehintstore are described once, in
    docs/architecture.md, and the widget demo once, in docs/types.md; the
    hand-kept line and example counts, the dependency tree, the badges, the
    install commands and the Status and License sections are gone, since the
    site derives them.
  • docs/examples.md lists the users.py, bookings.py and gallery.py
    mini-apps, and states once which examples need an extra library.
  • The design notes state the theme and the stream polling as facts rather than
    plans, the run() banner sample no longer carries a stale version, and links
    to pytypehint, pytypehintweb and pytypehintstore point to their documentation
    sites.
  • The changelog headings use one format, and seven dates are corrected to the
    PyPI upload dates (0.1.0, 0.4.4, 0.5.0, 0.9.8, 1.0.1, 2.5.0 and 2.6.1).
  • pyproject.toml gains the Documentation URL, and Source is renamed
    Repository.

The code is the same as 2.7.3.

2.7.3

Choose a tag to compare

@offerrall offerrall released this 29 Sep 06:44

Changed

  • Documentation only: the design notes are listed one by one in the README's
    Documentation section, and docs/design/index.md, which only listed them, is
    removed. The code is the same as 2.7.2.

2.7.2

Choose a tag to compare

@offerrall offerrall released this 29 Sep 06:37

Changed

  • Documentation only: the documentation index moves from docs/index.md into
    the README's Documentation section, and the README no longer repeats the
    version in its title and status line. The code is the same as 2.7.1.
  • The READMEs inside examples/ become one page, docs/examples.md, listed
    with the rest of the documentation.

Fixed

  • The test suite runs again with anyio 4.15, whose deprecation of
    anyio.abc.BlockingPortal is raised inside Starlette's TestClient; that one
    warning is ignored, every other warning is still an error.

2.7.1

Choose a tag to compare

@offerrall offerrall released this 24 Sep 05:57

[2.7.1] - 2026-09-24

Fixed

  • openModal() no longer opens at full height and then shrinks. With
    autoHeight the panel stays invisible until the page reports its height;
    the overlay appears at once. If no height arrives within 1.5 s the panel is
    shown anyway. autoHeight: false is unchanged.

2.6.2

Choose a tag to compare

@offerrall offerrall released this 13 Sep 00:13

[2.6.2] - 2026-09-13

Added

  • Per-opening hide_submit for Python page_of() and HTTP function pages,
    exposed as hideSubmit in SDK pageUrl(), embed() and openModal().
    Hides the Submit button without reserving space or enabling autorun.
    The default remains false. Combine with autorun for result-only previews;
    existing validation, uploads and result rendering remain in use.

2.6.1

Choose a tag to compare

@offerrall offerrall released this 12 Sep 23:18

[2.6.1] - 2026-09-13

FuncToWeb 2.6.1 is stable, used daily and actively maintained.

Added

  • Automatic content height for embed() and openModal(), including growth
    and shrinkage after form edits and results. Modal height remains bounded by
    the configured height and viewport. autoHeight: false keeps fixed sizing.
  • Per-opening hide_title and hide_description options for function pages,
    available through the URL and page_of(). The SDK exposes them as hideTitle
    and hideDescription in pageUrl(), embed() and openModal(). Hiding both
    removes the visible header and its spacing.

Fixed

  • An overlong file reference in prefill now returns 400 when the filesystem
    rejects its name during the existence check, instead of an internal 500.
    Existing stored names remain readable beyond the upload reference limit.

2.6.0

Choose a tag to compare

@offerrall offerrall released this 14 Aug 21:07

[2.6.0] - 2026-08-13

FuncToWeb moves onto pytypehintweb 1.1.0, and with it onto pytypehint 1.0.0.
The annotation that marks a file parameter is now FileHint, and the division of
labour around files is redrawn: the core stopped touching the filesystem, so
everything that was a promise about the real file on disk — that it exists,
that it is a file and not a directory, that its bytes fall between min_size and
max_size — is now stated only where it is actually enforced. For FuncToWeb that
is its own storage layer, which already did that work; nothing about uploads,
references or the pending/promoted lifecycle is weaker than it was in 2.5.0.

Changed

  • Breaking: IsPathFile is replaced by FileHint, re-exported from
    func_to_web with the same three fields (extensions, min_size,
    max_size). This is a clean cut: there is no alias, no deprecation warning and
    no support for pytypehint 0.x. Rename the import and the call —
    Annotated[str, IsPathFile(extensions=(".png",))] becomes
    Annotated[str, FileHint(extensions=(".png",))] — and nothing else changes.
  • Breaking (behaviour): Signature.build() no longer checks that a file
    value exists, that it is a file, or how large it is. In 2.5.0 an invocation
    carrying a path to a missing, oversized or undersized file was refused by the
    layer below; in 2.6.0 the core only checks the extension of the value it is
    given, and builds the arguments.
  • Breaking (behaviour): FileHint.min_size / FileHint.max_size no longer
    have a server-side second check during invocation. The browser applies them to
    a file it has just picked, because there it holds the bytes and knows
    file.size; a stored reference carries no bytes, the browser cannot weigh it
    and the core no longer stats it. Do not expect a 422 from /invoke for byte
    bounds on a reference. An application that needs an authoritative guarantee
    about stored content owns that policy itself — FuncToWeb does not invent one
    silently, and max_upload_bytes remains the endpoint policy it always was.
  • A file default written by the author as a server path is still refused unless
    it belongs to the storage directory, and is still published as a reference and
    never as a path. What changed is only what backs it: in 2.5.0 the core also
    refused a default that did not exist, so a stale default failed at build time.
    In 2.6.0 that default becomes a reference, and the resolver refuses it at
    invocation instead, when it fails to resolve.

Fixed

  • OpenForm now forwards a union-valued prefill in the same transport a browser
    submit sends, so an opening whose target has a parameter of two or more union
    branches works. It answered 400 before, in every version that had the
    feature: the opening published the plan's own way of writing a default —
    {"branch": …, "value": …}, which names the control the page opens on — and
    the destination reads a prefill as a submit, where a branch is named by the
    value itself or not at all. Depending on the union the answer was
    expected int | str, got dict or ambiguous value: wrap it as $type. The
    conversion is the plan's own answer rather than a rule restated in FuncToWeb:
    each branch already carries the mode pytypehintweb chose for it, and the
    three modes emit what form.js emits — bare for plain,
    {"$type": …, "$value": …} for wrapped, {"$type": …, …fields} for
    inline. X | None was never affected and is unchanged: one real branch
    compiles no choice at all, so the two spellings already agreed.

Unchanged, and now the only thing that says so

  • Reference syntax, separators, .., control characters, reserved names and
    markers, and length limits stay in segment_of() / stored_of().
  • Confinement to the uploads directory, in both directions: stored_of()
    requires the resolved parent to be the storage root, and reference_of()
    refuses a path whose round trip through storage does not land back on itself,
    which is what keeps a symbolic link from escaping.
  • Existence. stored_file() promotes a pending upload or raises
    FileNotFoundError, so a reference that does not resolve never reaches the
    function. /invoke answers 422; /invoke-stream answers 200 and carries
    the same error in its result event, as it does for every rejection once the
    stream is open. Either way the call is not made, and that refusal is a
    FuncToWeb storage rule — after 2.6.0, the only one on this path.
  • max_upload_bytes is authoritative and unchanged: it may reject early on
    Content-Length, but it never trusts it alone — the bytes are counted as they
    arrive, the transfer is cut the moment the limit is passed, the partial file is
    deleted and the answer is 413.
  • The pending/promoted lifecycle, the sweeper, expiry and the reuse of a promoted
    reference without a second upload.
  • OpenForm still hands a file on to the next function as a reference, hidden
    and not re-uploaded; the local path never travels.

Dependencies

  • pytypehintweb==0.0.5 → pytypehintweb==1.1.0, which brings pytypehint 1.0.0 transitively. FuncToWeb still declares no direct dependency on
    pytypehint: it arrives through pytypehintweb, as it has since 2.3.0.
  • 1.1.0 delegates the portable decoding to the core inside its own decode():
    it calls schema.decode() and then walks the decoded tree applying the
    file_resolver to each file node. The simplification happens in
    pytypehintweb; FuncToWeb implements none of it and its call sites are
    unchanged.
  • The mypy exception untyped_calls_exclude = ["pytypehintweb"] is removed:
    1.1.0 annotates decode(), so the package passes strict again with no
    exemption.
  • starlette==0.49.3 and uvicorn==0.38.0 are unchanged.

2.5.0

Choose a tag to compare

@offerrall offerrall released this 09 Aug 23:26

[2.5.0] - 2026-08-08

FuncToWeb now depends directly on Starlette instead of FastAPI. app_of()
returns a mountable Starlette/ASGI application, and run() serves that same
application with Uvicorn. FastAPI remains a naturally compatible host through
app.mount(...), but is no longer a runtime dependency. There is no 2.4.0: the
numbering goes from 2.3.0 straight to 2.5.0.

Changed

  • Breaking: router_of() is replaced by app_of() and
    include_router(..., prefix=...) becomes mount(prefix, app_of(...)).
  • Breaking: fastapi_kwargs is removed from run(); uvicorn_kwargs
    remains unchanged.
  • Breaking: the per-router dependencies of
    include_router(..., dependencies=[Depends(...)]) have nowhere to go, because
    a mount is an application inside another and not a router: mount() takes no
    dependencies and raises TypeError for it. A host that authenticated the
    space that way applies the same check as middleware around the mount, wrapping
    app_of(...) before mounting it. Dropping the argument to get past the
    TypeError leaves the space mounted and unauthenticated.
  • A POST carrying Content-Type: text/plain with a valid JSON body now
    answers 200. FastAPI read the declared content type first and took the body
    for a string, which was a 422; the body is parsed as JSON whatever the
    header says.
  • A route that answers GET now answers HEAD as well, which is the behaviour
    of starlette.routing.Route; with FastAPI's own router HEAD was a 405.
  • Invalid or non-object JSON keeps status 422 but now uses a small
    FuncToWeb transport error instead of FastAPI/Pydantic's validation payload.
  • The application index at / is part of app_of() as well as standalone
    run().

Dependencies

  • Replaced fastapi==0.121.1 with starlette==0.49.3 at runtime.
  • FastAPI is retained only in the test extra to verify host compatibility.