pulse-framework 0.1.48__tar.gz → 0.1.50__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {pulse_framework-0.1.48 → pulse_framework-0.1.50}/PKG-INFO +1 -1
- {pulse_framework-0.1.48 → pulse_framework-0.1.50}/pyproject.toml +1 -1
- {pulse_framework-0.1.48 → pulse_framework-0.1.50}/src/pulse/hooks/runtime.py +4 -2
- {pulse_framework-0.1.48 → pulse_framework-0.1.50}/src/pulse/messages.py +1 -0
- {pulse_framework-0.1.48 → pulse_framework-0.1.50}/src/pulse/proxy.py +2 -2
- {pulse_framework-0.1.48 → pulse_framework-0.1.50}/src/pulse/render_session.py +9 -2
- {pulse_framework-0.1.48 → pulse_framework-0.1.50}/src/pulse/transpiler/imports.py +7 -2
- {pulse_framework-0.1.48 → pulse_framework-0.1.50}/src/pulse/vdom.py +9 -11
- {pulse_framework-0.1.48 → pulse_framework-0.1.50}/README.md +0 -0
- {pulse_framework-0.1.48 → pulse_framework-0.1.50}/src/pulse/__init__.py +0 -0
- {pulse_framework-0.1.48 → pulse_framework-0.1.50}/src/pulse/app.py +0 -0
- {pulse_framework-0.1.48 → pulse_framework-0.1.50}/src/pulse/channel.py +0 -0
- {pulse_framework-0.1.48 → pulse_framework-0.1.50}/src/pulse/cli/__init__.py +0 -0
- {pulse_framework-0.1.48 → pulse_framework-0.1.50}/src/pulse/cli/cmd.py +0 -0
- {pulse_framework-0.1.48 → pulse_framework-0.1.50}/src/pulse/cli/dependencies.py +0 -0
- {pulse_framework-0.1.48 → pulse_framework-0.1.50}/src/pulse/cli/folder_lock.py +0 -0
- {pulse_framework-0.1.48 → pulse_framework-0.1.50}/src/pulse/cli/helpers.py +0 -0
- {pulse_framework-0.1.48 → pulse_framework-0.1.50}/src/pulse/cli/models.py +0 -0
- {pulse_framework-0.1.48 → pulse_framework-0.1.50}/src/pulse/cli/packages.py +0 -0
- {pulse_framework-0.1.48 → pulse_framework-0.1.50}/src/pulse/cli/processes.py +0 -0
- {pulse_framework-0.1.48 → pulse_framework-0.1.50}/src/pulse/cli/secrets.py +0 -0
- {pulse_framework-0.1.48 → pulse_framework-0.1.50}/src/pulse/cli/uvicorn_log_config.py +0 -0
- {pulse_framework-0.1.48 → pulse_framework-0.1.50}/src/pulse/codegen/__init__.py +0 -0
- {pulse_framework-0.1.48 → pulse_framework-0.1.50}/src/pulse/codegen/codegen.py +0 -0
- {pulse_framework-0.1.48 → pulse_framework-0.1.50}/src/pulse/codegen/js.py +0 -0
- {pulse_framework-0.1.48 → pulse_framework-0.1.50}/src/pulse/codegen/templates/__init__.py +0 -0
- {pulse_framework-0.1.48 → pulse_framework-0.1.50}/src/pulse/codegen/templates/layout.py +0 -0
- {pulse_framework-0.1.48 → pulse_framework-0.1.50}/src/pulse/codegen/templates/route.py +0 -0
- {pulse_framework-0.1.48 → pulse_framework-0.1.50}/src/pulse/codegen/templates/routes_ts.py +0 -0
- {pulse_framework-0.1.48 → pulse_framework-0.1.50}/src/pulse/codegen/utils.py +0 -0
- {pulse_framework-0.1.48 → pulse_framework-0.1.50}/src/pulse/components/__init__.py +0 -0
- {pulse_framework-0.1.48 → pulse_framework-0.1.50}/src/pulse/components/for_.py +0 -0
- {pulse_framework-0.1.48 → pulse_framework-0.1.50}/src/pulse/components/if_.py +0 -0
- {pulse_framework-0.1.48 → pulse_framework-0.1.50}/src/pulse/components/react_router.py +0 -0
- {pulse_framework-0.1.48 → pulse_framework-0.1.50}/src/pulse/context.py +0 -0
- {pulse_framework-0.1.48 → pulse_framework-0.1.50}/src/pulse/cookies.py +0 -0
- {pulse_framework-0.1.48 → pulse_framework-0.1.50}/src/pulse/decorators.py +0 -0
- {pulse_framework-0.1.48 → pulse_framework-0.1.50}/src/pulse/env.py +0 -0
- {pulse_framework-0.1.48 → pulse_framework-0.1.50}/src/pulse/form.py +0 -0
- {pulse_framework-0.1.48 → pulse_framework-0.1.50}/src/pulse/helpers.py +0 -0
- {pulse_framework-0.1.48 → pulse_framework-0.1.50}/src/pulse/hooks/__init__.py +0 -0
- {pulse_framework-0.1.48 → pulse_framework-0.1.50}/src/pulse/hooks/core.py +0 -0
- {pulse_framework-0.1.48 → pulse_framework-0.1.50}/src/pulse/hooks/effects.py +0 -0
- {pulse_framework-0.1.48 → pulse_framework-0.1.50}/src/pulse/hooks/init.py +0 -0
- {pulse_framework-0.1.48 → pulse_framework-0.1.50}/src/pulse/hooks/setup.py +0 -0
- {pulse_framework-0.1.48 → pulse_framework-0.1.50}/src/pulse/hooks/stable.py +0 -0
- {pulse_framework-0.1.48 → pulse_framework-0.1.50}/src/pulse/hooks/states.py +0 -0
- {pulse_framework-0.1.48 → pulse_framework-0.1.50}/src/pulse/html/__init__.py +0 -0
- {pulse_framework-0.1.48 → pulse_framework-0.1.50}/src/pulse/html/elements.py +0 -0
- {pulse_framework-0.1.48 → pulse_framework-0.1.50}/src/pulse/html/events.py +0 -0
- {pulse_framework-0.1.48 → pulse_framework-0.1.50}/src/pulse/html/props.py +0 -0
- {pulse_framework-0.1.48 → pulse_framework-0.1.50}/src/pulse/html/svg.py +0 -0
- {pulse_framework-0.1.48 → pulse_framework-0.1.50}/src/pulse/html/tags.py +0 -0
- {pulse_framework-0.1.48 → pulse_framework-0.1.50}/src/pulse/html/tags.pyi +0 -0
- {pulse_framework-0.1.48 → pulse_framework-0.1.50}/src/pulse/js/__init__.py +0 -0
- {pulse_framework-0.1.48 → pulse_framework-0.1.50}/src/pulse/js/__init__.pyi +0 -0
- {pulse_framework-0.1.48 → pulse_framework-0.1.50}/src/pulse/js/_types.py +0 -0
- {pulse_framework-0.1.48 → pulse_framework-0.1.50}/src/pulse/js/array.py +0 -0
- {pulse_framework-0.1.48 → pulse_framework-0.1.50}/src/pulse/js/console.py +0 -0
- {pulse_framework-0.1.48 → pulse_framework-0.1.50}/src/pulse/js/date.py +0 -0
- {pulse_framework-0.1.48 → pulse_framework-0.1.50}/src/pulse/js/document.py +0 -0
- {pulse_framework-0.1.48 → pulse_framework-0.1.50}/src/pulse/js/error.py +0 -0
- {pulse_framework-0.1.48 → pulse_framework-0.1.50}/src/pulse/js/json.py +0 -0
- {pulse_framework-0.1.48 → pulse_framework-0.1.50}/src/pulse/js/map.py +0 -0
- {pulse_framework-0.1.48 → pulse_framework-0.1.50}/src/pulse/js/math.py +0 -0
- {pulse_framework-0.1.48 → pulse_framework-0.1.50}/src/pulse/js/navigator.py +0 -0
- {pulse_framework-0.1.48 → pulse_framework-0.1.50}/src/pulse/js/number.py +0 -0
- {pulse_framework-0.1.48 → pulse_framework-0.1.50}/src/pulse/js/object.py +0 -0
- {pulse_framework-0.1.48 → pulse_framework-0.1.50}/src/pulse/js/promise.py +0 -0
- {pulse_framework-0.1.48 → pulse_framework-0.1.50}/src/pulse/js/regexp.py +0 -0
- {pulse_framework-0.1.48 → pulse_framework-0.1.50}/src/pulse/js/set.py +0 -0
- {pulse_framework-0.1.48 → pulse_framework-0.1.50}/src/pulse/js/string.py +0 -0
- {pulse_framework-0.1.48 → pulse_framework-0.1.50}/src/pulse/js/weakmap.py +0 -0
- {pulse_framework-0.1.48 → pulse_framework-0.1.50}/src/pulse/js/weakset.py +0 -0
- {pulse_framework-0.1.48 → pulse_framework-0.1.50}/src/pulse/js/window.py +0 -0
- {pulse_framework-0.1.48 → pulse_framework-0.1.50}/src/pulse/middleware.py +0 -0
- {pulse_framework-0.1.48 → pulse_framework-0.1.50}/src/pulse/plugin.py +0 -0
- {pulse_framework-0.1.48 → pulse_framework-0.1.50}/src/pulse/py.typed +0 -0
- {pulse_framework-0.1.48 → pulse_framework-0.1.50}/src/pulse/queries/__init__.py +0 -0
- {pulse_framework-0.1.48 → pulse_framework-0.1.50}/src/pulse/queries/client.py +0 -0
- {pulse_framework-0.1.48 → pulse_framework-0.1.50}/src/pulse/queries/common.py +0 -0
- {pulse_framework-0.1.48 → pulse_framework-0.1.50}/src/pulse/queries/effect.py +0 -0
- {pulse_framework-0.1.48 → pulse_framework-0.1.50}/src/pulse/queries/infinite_query.py +0 -0
- {pulse_framework-0.1.48 → pulse_framework-0.1.50}/src/pulse/queries/mutation.py +0 -0
- {pulse_framework-0.1.48 → pulse_framework-0.1.50}/src/pulse/queries/protocol.py +0 -0
- {pulse_framework-0.1.48 → pulse_framework-0.1.50}/src/pulse/queries/query.py +0 -0
- {pulse_framework-0.1.48 → pulse_framework-0.1.50}/src/pulse/queries/store.py +0 -0
- {pulse_framework-0.1.48 → pulse_framework-0.1.50}/src/pulse/react_component.py +0 -0
- {pulse_framework-0.1.48 → pulse_framework-0.1.50}/src/pulse/reactive.py +0 -0
- {pulse_framework-0.1.48 → pulse_framework-0.1.50}/src/pulse/reactive_extensions.py +0 -0
- {pulse_framework-0.1.48 → pulse_framework-0.1.50}/src/pulse/renderer.py +0 -0
- {pulse_framework-0.1.48 → pulse_framework-0.1.50}/src/pulse/request.py +0 -0
- {pulse_framework-0.1.48 → pulse_framework-0.1.50}/src/pulse/routing.py +0 -0
- {pulse_framework-0.1.48 → pulse_framework-0.1.50}/src/pulse/serializer.py +0 -0
- {pulse_framework-0.1.48 → pulse_framework-0.1.50}/src/pulse/state.py +0 -0
- {pulse_framework-0.1.48 → pulse_framework-0.1.50}/src/pulse/transpiler/__init__.py +0 -0
- {pulse_framework-0.1.48 → pulse_framework-0.1.50}/src/pulse/transpiler/builtins.py +0 -0
- {pulse_framework-0.1.48 → pulse_framework-0.1.50}/src/pulse/transpiler/constants.py +0 -0
- {pulse_framework-0.1.48 → pulse_framework-0.1.50}/src/pulse/transpiler/context.py +0 -0
- {pulse_framework-0.1.48 → pulse_framework-0.1.50}/src/pulse/transpiler/errors.py +0 -0
- {pulse_framework-0.1.48 → pulse_framework-0.1.50}/src/pulse/transpiler/function.py +0 -0
- {pulse_framework-0.1.48 → pulse_framework-0.1.50}/src/pulse/transpiler/ids.py +0 -0
- {pulse_framework-0.1.48 → pulse_framework-0.1.50}/src/pulse/transpiler/js_module.py +0 -0
- {pulse_framework-0.1.48 → pulse_framework-0.1.50}/src/pulse/transpiler/modules/__init__.py +0 -0
- {pulse_framework-0.1.48 → pulse_framework-0.1.50}/src/pulse/transpiler/modules/asyncio.py +0 -0
- {pulse_framework-0.1.48 → pulse_framework-0.1.50}/src/pulse/transpiler/modules/json.py +0 -0
- {pulse_framework-0.1.48 → pulse_framework-0.1.50}/src/pulse/transpiler/modules/math.py +0 -0
- {pulse_framework-0.1.48 → pulse_framework-0.1.50}/src/pulse/transpiler/modules/re.py +0 -0
- {pulse_framework-0.1.48 → pulse_framework-0.1.50}/src/pulse/transpiler/modules/tags.py +0 -0
- {pulse_framework-0.1.48 → pulse_framework-0.1.50}/src/pulse/transpiler/modules/typing.py +0 -0
- {pulse_framework-0.1.48 → pulse_framework-0.1.50}/src/pulse/transpiler/nodes.py +0 -0
- {pulse_framework-0.1.48 → pulse_framework-0.1.50}/src/pulse/transpiler/py_module.py +0 -0
- {pulse_framework-0.1.48 → pulse_framework-0.1.50}/src/pulse/transpiler/transpiler.py +0 -0
- {pulse_framework-0.1.48 → pulse_framework-0.1.50}/src/pulse/transpiler/utils.py +0 -0
- {pulse_framework-0.1.48 → pulse_framework-0.1.50}/src/pulse/types/__init__.py +0 -0
- {pulse_framework-0.1.48 → pulse_framework-0.1.50}/src/pulse/types/event_handler.py +0 -0
- {pulse_framework-0.1.48 → pulse_framework-0.1.50}/src/pulse/user_session.py +0 -0
- {pulse_framework-0.1.48 → pulse_framework-0.1.50}/src/pulse/version.py +0 -0
|
@@ -103,11 +103,13 @@ async def set_cookie(
|
|
|
103
103
|
)
|
|
104
104
|
|
|
105
105
|
|
|
106
|
-
def navigate(path: str, *, replace: bool = False) -> None:
|
|
106
|
+
def navigate(path: str, *, replace: bool = False, hard: bool = False) -> None:
|
|
107
107
|
ctx = PulseContext.get()
|
|
108
108
|
if ctx.render is None:
|
|
109
109
|
raise RuntimeError("navigate() must be invoked inside a Pulse callback context")
|
|
110
|
-
ctx.render.send(
|
|
110
|
+
ctx.render.send(
|
|
111
|
+
{"type": "navigate_to", "path": path, "replace": replace, "hard": hard}
|
|
112
|
+
)
|
|
111
113
|
|
|
112
114
|
|
|
113
115
|
def redirect(path: str, *, replace: bool = False) -> NoReturn:
|
|
@@ -41,7 +41,7 @@ class ReactProxy:
|
|
|
41
41
|
self.server_address = server_address
|
|
42
42
|
self._client = None
|
|
43
43
|
|
|
44
|
-
def
|
|
44
|
+
def rewrite_url(self, url: str) -> str:
|
|
45
45
|
"""Rewrite internal React server URLs to external server address."""
|
|
46
46
|
if self.react_server_address in url:
|
|
47
47
|
return url.replace(self.react_server_address, self.server_address)
|
|
@@ -207,7 +207,7 @@ class ReactProxy:
|
|
|
207
207
|
response_headers: dict[str, str] = {}
|
|
208
208
|
for k, v in r.headers.items():
|
|
209
209
|
if k.lower() in ("location", "content-location"):
|
|
210
|
-
v = self.
|
|
210
|
+
v = self.rewrite_url(v)
|
|
211
211
|
response_headers[k] = v
|
|
212
212
|
|
|
213
213
|
return StreamingResponse(
|
|
@@ -440,6 +440,7 @@ class RenderSession:
|
|
|
440
440
|
type="navigate_to",
|
|
441
441
|
path=msg["path"],
|
|
442
442
|
replace=msg["replace"],
|
|
443
|
+
hard=msg.get("hard", False),
|
|
443
444
|
)
|
|
444
445
|
|
|
445
446
|
prev_sender = self._send_message
|
|
@@ -563,14 +564,20 @@ class RenderSession:
|
|
|
563
564
|
# Prefer client-side navigation over emitting VDOM operations
|
|
564
565
|
self.send(
|
|
565
566
|
ServerNavigateToMessage(
|
|
566
|
-
type="navigate_to",
|
|
567
|
+
type="navigate_to",
|
|
568
|
+
path=r.path,
|
|
569
|
+
replace=r.replace,
|
|
570
|
+
hard=False,
|
|
567
571
|
)
|
|
568
572
|
)
|
|
569
573
|
except NotFoundInterrupt:
|
|
570
574
|
# Use app-configured not-found path; fallback to '/404'
|
|
571
575
|
self.send(
|
|
572
576
|
ServerNavigateToMessage(
|
|
573
|
-
type="navigate_to",
|
|
577
|
+
type="navigate_to",
|
|
578
|
+
path=ctx.app.not_found,
|
|
579
|
+
replace=True,
|
|
580
|
+
hard=False,
|
|
574
581
|
)
|
|
575
582
|
)
|
|
576
583
|
|
|
@@ -293,6 +293,7 @@ class CssImport(Import):
|
|
|
293
293
|
- Absolute path (e.g., "/path/to/styles.css")
|
|
294
294
|
module: If True, import as a CSS module (default export for class access).
|
|
295
295
|
If False, import for side effects only (global styles).
|
|
296
|
+
Automatically set to True if path ends with ".module.css".
|
|
296
297
|
relative: If True, resolve path relative to the caller's file.
|
|
297
298
|
before: List of import sources that should come after this import.
|
|
298
299
|
|
|
@@ -300,8 +301,8 @@ class CssImport(Import):
|
|
|
300
301
|
# Side-effect CSS import (global styles)
|
|
301
302
|
CssImport("@mantine/core/styles.css")
|
|
302
303
|
|
|
303
|
-
# CSS module for class access
|
|
304
|
-
styles = CssImport("./styles.module.css",
|
|
304
|
+
# CSS module for class access (module=True auto-detected from .module.css)
|
|
305
|
+
styles = CssImport("./styles.module.css", relative=True)
|
|
305
306
|
styles.container # Returns JSMember for 'container' class
|
|
306
307
|
|
|
307
308
|
# Local CSS file (will be copied during codegen)
|
|
@@ -316,6 +317,10 @@ class CssImport(Import):
|
|
|
316
317
|
relative: bool = False,
|
|
317
318
|
before: Sequence[str] = (),
|
|
318
319
|
) -> None:
|
|
320
|
+
# Auto-detect CSS modules based on filename
|
|
321
|
+
if path.endswith(".module.css"):
|
|
322
|
+
module = True
|
|
323
|
+
|
|
319
324
|
source_path: Path | None = None
|
|
320
325
|
import_src = path
|
|
321
326
|
|
|
@@ -265,7 +265,7 @@ class Component(Generic[P]):
|
|
|
265
265
|
if key is not None and not isinstance(key, str):
|
|
266
266
|
raise ValueError("key must be a string or None")
|
|
267
267
|
|
|
268
|
-
# Flatten children if component
|
|
268
|
+
# Flatten children if component accepts them via `*children` parameter
|
|
269
269
|
if self._takes_children and args:
|
|
270
270
|
flattened = _flatten_children(
|
|
271
271
|
args, # pyright: ignore[reportArgumentType]
|
|
@@ -356,11 +356,11 @@ class ComponentNode:
|
|
|
356
356
|
)
|
|
357
357
|
if not isinstance(children_arg, tuple):
|
|
358
358
|
children_arg = (children_arg,)
|
|
359
|
-
# Flatten children
|
|
359
|
+
# Flatten children when component accepts them via `*children` parameter
|
|
360
360
|
flattened_children = _flatten_children(
|
|
361
361
|
children_arg, parent_name=f"<{self.name}>", warn_stacklevel=4
|
|
362
362
|
)
|
|
363
|
-
|
|
363
|
+
return ComponentNode(
|
|
364
364
|
fn=self.fn,
|
|
365
365
|
args=tuple(flattened_children),
|
|
366
366
|
kwargs=self.kwargs,
|
|
@@ -368,7 +368,6 @@ class ComponentNode:
|
|
|
368
368
|
key=self.key,
|
|
369
369
|
takes_children=self.takes_children,
|
|
370
370
|
)
|
|
371
|
-
return result
|
|
372
371
|
|
|
373
372
|
@override
|
|
374
373
|
def __repr__(self) -> str:
|
|
@@ -606,19 +605,18 @@ def _callable_qualname(fn: Callable[..., Any]) -> str:
|
|
|
606
605
|
|
|
607
606
|
|
|
608
607
|
def _takes_children(fn: Callable[..., Any]) -> bool:
|
|
609
|
-
|
|
610
|
-
|
|
608
|
+
"""Return True if function accepts children via `*children` parameter.
|
|
609
|
+
|
|
610
|
+
Convention: A component accepts children if and only if it has a VAR_POSITIONAL
|
|
611
|
+
parameter named "children". This convention should be documented in user-facing docs.
|
|
612
|
+
"""
|
|
611
613
|
try:
|
|
612
614
|
sig = signature(fn)
|
|
613
615
|
except (ValueError, TypeError):
|
|
614
616
|
# Builtins or callables without inspectable signature: assume no children
|
|
615
617
|
return False
|
|
616
618
|
for p in sig.parameters.values():
|
|
617
|
-
if p.kind
|
|
618
|
-
Parameter.VAR_POSITIONAL,
|
|
619
|
-
Parameter.POSITIONAL_ONLY,
|
|
620
|
-
Parameter.POSITIONAL_OR_KEYWORD,
|
|
621
|
-
):
|
|
619
|
+
if p.kind is Parameter.VAR_POSITIONAL and p.name == "children":
|
|
622
620
|
return True
|
|
623
621
|
return False
|
|
624
622
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|