From 7d8c75315b4c3789785061e38681662dc33deae9 Mon Sep 17 00:00:00 2001 From: Dheepak Krishnamurthy <1813121+kdheepak@users.noreply.github.com> Date: Sun, 27 Sep 2026 02:46:00 -0400 Subject: [PATCH 1/5] feat: add datastar_url helper --- src/datastar_py/__init__.py | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/src/datastar_py/__init__.py b/src/datastar_py/__init__.py index c041c24..98212f0 100644 --- a/src/datastar_py/__init__.py +++ b/src/datastar_py/__init__.py @@ -1,13 +1,37 @@ from __future__ import annotations import json +import re from collections.abc import Mapping from typing import Any from .attributes import action_generator, attribute_generator from .sse import SSE_HEADERS, ServerSentEventGenerator -__all__ = ["SSE_HEADERS", "ServerSentEventGenerator", "action_generator", "attribute_generator"] +__all__ = [ + "SSE_HEADERS", + "ServerSentEventGenerator", + "action_generator", + "attribute_generator", + "datastar_url", +] + + +def datastar_url(version: str | None = None) -> str: + """Return the jsDelivr URL for the Datastar JavaScript module. + + Release versions accept an optional ``v`` prefix, e.g. '1.0.4' or 'v1'. + """ + ref = "" + if version is not None: + if not isinstance(version, str): + raise TypeError("version must be a string or None") + if not re.fullmatch( + r"v?[0-9]+\.[0-9]+\.[0-9]+(?:-[0-9A-Za-z]+(?:[.-][0-9A-Za-z]+)*)?", version + ): + raise ValueError("version must be a release such as '1.0.4' or 'v1.0.4'") + ref = f"@v{version.removeprefix('v')}" + return f"https://cdn.jsdelivr.net/gh/starfederation/datastar{ref}/bundles/datastar.js" def _read_signals( From 329c4f5e0e37120d23352f829e75db4f90f9ace0 Mon Sep 17 00:00:00 2001 From: Dheepak Krishnamurthy <1813121+kdheepak@users.noreply.github.com> Date: Sun, 27 Sep 2026 02:56:17 -0400 Subject: [PATCH 2/5] feat(version): accept major and minor values in datastar_url --- src/datastar_py/__init__.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/datastar_py/__init__.py b/src/datastar_py/__init__.py index 98212f0..f27c9b8 100644 --- a/src/datastar_py/__init__.py +++ b/src/datastar_py/__init__.py @@ -20,16 +20,20 @@ def datastar_url(version: str | None = None) -> str: """Return the jsDelivr URL for the Datastar JavaScript module. - Release versions accept an optional ``v`` prefix, e.g. '1.0.4' or 'v1'. + Accepts major, minor, or exact versions with an optional ``v`` prefix. + Major, minor, and omitted versions can resolve to the latest release. """ ref = "" if version is not None: if not isinstance(version, str): raise TypeError("version must be a string or None") if not re.fullmatch( - r"v?[0-9]+\.[0-9]+\.[0-9]+(?:-[0-9A-Za-z]+(?:[.-][0-9A-Za-z]+)*)?", version + r"v?[0-9]+(?:\.[0-9]+(?:\.[0-9]+(?:-[0-9A-Za-z]+(?:[.-][0-9A-Za-z]+)*)?)?)?", + version, ): - raise ValueError("version must be a release such as '1.0.4' or 'v1.0.4'") + raise ValueError( + "version must be a valid jsdelivr compatible string, such as 'v1', '1.0', or '1.0.4'" + ) ref = f"@v{version.removeprefix('v')}" return f"https://cdn.jsdelivr.net/gh/starfederation/datastar{ref}/bundles/datastar.js" From 59062c7ea3e5170e57cd50c5146a3f0951573f1f Mon Sep 17 00:00:00 2001 From: Dheepak Krishnamurthy <1813121+kdheepak@users.noreply.github.com> Date: Sun, 27 Sep 2026 03:01:32 -0400 Subject: [PATCH 3/5] test(datastar-url): add coverage for version parsing and validation --- src/datastar_py/__init__.py | 12 ++++---- tests/test_datastar_url.py | 57 +++++++++++++++++++++++++++++++++++++ 2 files changed, 63 insertions(+), 6 deletions(-) create mode 100644 tests/test_datastar_url.py diff --git a/src/datastar_py/__init__.py b/src/datastar_py/__init__.py index f27c9b8..997fdb5 100644 --- a/src/datastar_py/__init__.py +++ b/src/datastar_py/__init__.py @@ -17,24 +17,24 @@ ] -def datastar_url(version: str | None = None) -> str: +def datastar_url(version: str = "") -> str: """Return the jsDelivr URL for the Datastar JavaScript module. Accepts major, minor, or exact versions with an optional ``v`` prefix. Major, minor, and omitted versions can resolve to the latest release. """ + if not isinstance(version, str): + raise TypeError("version must be a string or None") ref = "" - if version is not None: - if not isinstance(version, str): - raise TypeError("version must be a string or None") + if version != "": if not re.fullmatch( r"v?[0-9]+(?:\.[0-9]+(?:\.[0-9]+(?:-[0-9A-Za-z]+(?:[.-][0-9A-Za-z]+)*)?)?)?", - version, + version.strip(), ): raise ValueError( "version must be a valid jsdelivr compatible string, such as 'v1', '1.0', or '1.0.4'" ) - ref = f"@v{version.removeprefix('v')}" + ref = f"@v{version.strip().removeprefix('v')}" return f"https://cdn.jsdelivr.net/gh/starfederation/datastar{ref}/bundles/datastar.js" diff --git a/tests/test_datastar_url.py b/tests/test_datastar_url.py new file mode 100644 index 0000000..1d9dcfa --- /dev/null +++ b/tests/test_datastar_url.py @@ -0,0 +1,57 @@ +import pytest + +import datastar_py + + +@pytest.mark.parametrize( + ("version", "ref"), + ( + ("", ""), + ("1", "@v1"), + ("v1", "@v1"), + ("1.0", "@v1.0"), + ("v1.0", "@v1.0"), + ("1.0.4", "@v1.0.4"), + ("v1.0.4", "@v1.0.4"), + ("1.0.0-RC.7", "@v1.0.0-RC.7"), + ("v1.0.0-RC.7", "@v1.0.0-RC.7"), + ), +) +def test_datastar_url_versions(version, ref): + assert datastar_py.datastar_url(version) == ( + f"https://cdn.jsdelivr.net/gh/starfederation/datastar{ref}/bundles/datastar.js" + ) + + +def test_datastar_url_default(): + assert datastar_py.datastar_url() == ( + "https://cdn.jsdelivr.net/gh/starfederation/datastar/bundles/datastar.js" + ) + + +@pytest.mark.parametrize( + "version", + ( + "v", + "v1.", + "1..0", + "1.0.4.1", + "1-RC.7", + "1.0-RC.7", + "1.0.4-", + "1.0.4-RC.", + "latest", + "v1/something", + "v1?x=1", + "v1#fragment", + ), +) +def test_datastar_url_rejects_invalid_versions(version): + with pytest.raises(ValueError, match="version"): + datastar_py.datastar_url(version) + + +@pytest.mark.parametrize("version", (1, 1.0, True, b"v1", [], {})) +def test_datastar_url_rejects_invalid_types(version): + with pytest.raises(TypeError, match="version must be a string or None"): + datastar_py.datastar_url(version) From 689f3e6ce0b0ab068ece309c78bc37eb49cf10ab Mon Sep 17 00:00:00 2001 From: Dheepak Krishnamurthy <1813121+kdheepak@users.noreply.github.com> Date: Sun, 27 Sep 2026 03:07:19 -0400 Subject: [PATCH 4/5] refactor(api): rename datastar_url to url --- src/datastar_py/__init__.py | 4 ++-- tests/{test_datastar_url.py => test_url.py} | 16 ++++++++-------- 2 files changed, 10 insertions(+), 10 deletions(-) rename tests/{test_datastar_url.py => test_url.py} (73%) diff --git a/src/datastar_py/__init__.py b/src/datastar_py/__init__.py index 997fdb5..f1c4f58 100644 --- a/src/datastar_py/__init__.py +++ b/src/datastar_py/__init__.py @@ -13,11 +13,11 @@ "ServerSentEventGenerator", "action_generator", "attribute_generator", - "datastar_url", + "url", ] -def datastar_url(version: str = "") -> str: +def url(version: str = "") -> str: """Return the jsDelivr URL for the Datastar JavaScript module. Accepts major, minor, or exact versions with an optional ``v`` prefix. diff --git a/tests/test_datastar_url.py b/tests/test_url.py similarity index 73% rename from tests/test_datastar_url.py rename to tests/test_url.py index 1d9dcfa..9d9686f 100644 --- a/tests/test_datastar_url.py +++ b/tests/test_url.py @@ -17,14 +17,14 @@ ("v1.0.0-RC.7", "@v1.0.0-RC.7"), ), ) -def test_datastar_url_versions(version, ref): - assert datastar_py.datastar_url(version) == ( +def test_url_versions(version, ref): + assert datastar_py.url(version) == ( f"https://cdn.jsdelivr.net/gh/starfederation/datastar{ref}/bundles/datastar.js" ) -def test_datastar_url_default(): - assert datastar_py.datastar_url() == ( +def test_url_default(): + assert datastar_py.url() == ( "https://cdn.jsdelivr.net/gh/starfederation/datastar/bundles/datastar.js" ) @@ -46,12 +46,12 @@ def test_datastar_url_default(): "v1#fragment", ), ) -def test_datastar_url_rejects_invalid_versions(version): +def test_url_rejects_invalid_versions(version): with pytest.raises(ValueError, match="version"): - datastar_py.datastar_url(version) + datastar_py.url(version) @pytest.mark.parametrize("version", (1, 1.0, True, b"v1", [], {})) -def test_datastar_url_rejects_invalid_types(version): +def test_url_rejects_invalid_types(version): with pytest.raises(TypeError, match="version must be a string or None"): - datastar_py.datastar_url(version) + datastar_py.url(version) From d508a77018334f871eb98c8864f31f0bd36fe1d7 Mon Sep 17 00:00:00 2001 From: Dheepak Krishnamurthy <1813121+kdheepak@users.noreply.github.com> Date: Sun, 27 Sep 2026 03:09:17 -0400 Subject: [PATCH 5/5] docs(readme): use datastar_py url helper in script src --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 9d9ac3c..d4d572e 100644 --- a/README.md +++ b/README.md @@ -31,6 +31,7 @@ available in this package. import asyncio from datetime import datetime +import datastar_py from datastar_py import ServerSentEventGenerator as SSE, attribute_generator as data from datastar_py.quart import datastar_response, read_signals from quart import Quart @@ -43,7 +44,7 @@ def index(): return f"""
- +