pulse-framework 0.1.65__tar.gz → 0.1.66a2__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.65 → pulse_framework-0.1.66a2}/PKG-INFO +1 -1
- {pulse_framework-0.1.65 → pulse_framework-0.1.66a2}/pyproject.toml +1 -1
- {pulse_framework-0.1.65 → pulse_framework-0.1.66a2}/src/pulse/app.py +66 -16
- {pulse_framework-0.1.65 → pulse_framework-0.1.66a2}/src/pulse/codegen/templates/layout.py +3 -8
- {pulse_framework-0.1.65 → pulse_framework-0.1.66a2}/src/pulse/render_session.py +62 -0
- {pulse_framework-0.1.65 → pulse_framework-0.1.66a2}/README.md +0 -0
- {pulse_framework-0.1.65 → pulse_framework-0.1.66a2}/src/pulse/__init__.py +0 -0
- {pulse_framework-0.1.65 → pulse_framework-0.1.66a2}/src/pulse/_examples.py +0 -0
- {pulse_framework-0.1.65 → pulse_framework-0.1.66a2}/src/pulse/channel.py +0 -0
- {pulse_framework-0.1.65 → pulse_framework-0.1.66a2}/src/pulse/cli/__init__.py +0 -0
- {pulse_framework-0.1.65 → pulse_framework-0.1.66a2}/src/pulse/cli/cmd.py +0 -0
- {pulse_framework-0.1.65 → pulse_framework-0.1.66a2}/src/pulse/cli/dependencies.py +0 -0
- {pulse_framework-0.1.65 → pulse_framework-0.1.66a2}/src/pulse/cli/folder_lock.py +0 -0
- {pulse_framework-0.1.65 → pulse_framework-0.1.66a2}/src/pulse/cli/helpers.py +0 -0
- {pulse_framework-0.1.65 → pulse_framework-0.1.66a2}/src/pulse/cli/logging.py +0 -0
- {pulse_framework-0.1.65 → pulse_framework-0.1.66a2}/src/pulse/cli/models.py +0 -0
- {pulse_framework-0.1.65 → pulse_framework-0.1.66a2}/src/pulse/cli/packages.py +0 -0
- {pulse_framework-0.1.65 → pulse_framework-0.1.66a2}/src/pulse/cli/processes.py +0 -0
- {pulse_framework-0.1.65 → pulse_framework-0.1.66a2}/src/pulse/cli/secrets.py +0 -0
- {pulse_framework-0.1.65 → pulse_framework-0.1.66a2}/src/pulse/cli/uvicorn_log_config.py +0 -0
- {pulse_framework-0.1.65 → pulse_framework-0.1.66a2}/src/pulse/code_analysis.py +0 -0
- {pulse_framework-0.1.65 → pulse_framework-0.1.66a2}/src/pulse/codegen/__init__.py +0 -0
- {pulse_framework-0.1.65 → pulse_framework-0.1.66a2}/src/pulse/codegen/codegen.py +0 -0
- {pulse_framework-0.1.65 → pulse_framework-0.1.66a2}/src/pulse/codegen/templates/__init__.py +0 -0
- {pulse_framework-0.1.65 → pulse_framework-0.1.66a2}/src/pulse/codegen/templates/route.py +0 -0
- {pulse_framework-0.1.65 → pulse_framework-0.1.66a2}/src/pulse/codegen/templates/routes_ts.py +0 -0
- {pulse_framework-0.1.65 → pulse_framework-0.1.66a2}/src/pulse/codegen/utils.py +0 -0
- {pulse_framework-0.1.65 → pulse_framework-0.1.66a2}/src/pulse/component.py +0 -0
- {pulse_framework-0.1.65 → pulse_framework-0.1.66a2}/src/pulse/components/__init__.py +0 -0
- {pulse_framework-0.1.65 → pulse_framework-0.1.66a2}/src/pulse/components/for_.py +0 -0
- {pulse_framework-0.1.65 → pulse_framework-0.1.66a2}/src/pulse/components/if_.py +0 -0
- {pulse_framework-0.1.65 → pulse_framework-0.1.66a2}/src/pulse/components/react_router.py +0 -0
- {pulse_framework-0.1.65 → pulse_framework-0.1.66a2}/src/pulse/context.py +0 -0
- {pulse_framework-0.1.65 → pulse_framework-0.1.66a2}/src/pulse/cookies.py +0 -0
- {pulse_framework-0.1.65 → pulse_framework-0.1.66a2}/src/pulse/decorators.py +0 -0
- {pulse_framework-0.1.65 → pulse_framework-0.1.66a2}/src/pulse/dom/__init__.py +0 -0
- {pulse_framework-0.1.65 → pulse_framework-0.1.66a2}/src/pulse/dom/elements.py +0 -0
- {pulse_framework-0.1.65 → pulse_framework-0.1.66a2}/src/pulse/dom/events.py +0 -0
- {pulse_framework-0.1.65 → pulse_framework-0.1.66a2}/src/pulse/dom/props.py +0 -0
- {pulse_framework-0.1.65 → pulse_framework-0.1.66a2}/src/pulse/dom/svg.py +0 -0
- {pulse_framework-0.1.65 → pulse_framework-0.1.66a2}/src/pulse/dom/tags.py +0 -0
- {pulse_framework-0.1.65 → pulse_framework-0.1.66a2}/src/pulse/dom/tags.pyi +0 -0
- {pulse_framework-0.1.65 → pulse_framework-0.1.66a2}/src/pulse/env.py +0 -0
- {pulse_framework-0.1.65 → pulse_framework-0.1.66a2}/src/pulse/forms.py +0 -0
- {pulse_framework-0.1.65 → pulse_framework-0.1.66a2}/src/pulse/helpers.py +0 -0
- {pulse_framework-0.1.65 → pulse_framework-0.1.66a2}/src/pulse/hooks/__init__.py +0 -0
- {pulse_framework-0.1.65 → pulse_framework-0.1.66a2}/src/pulse/hooks/core.py +0 -0
- {pulse_framework-0.1.65 → pulse_framework-0.1.66a2}/src/pulse/hooks/effects.py +0 -0
- {pulse_framework-0.1.65 → pulse_framework-0.1.66a2}/src/pulse/hooks/init.py +0 -0
- {pulse_framework-0.1.65 → pulse_framework-0.1.66a2}/src/pulse/hooks/runtime.py +0 -0
- {pulse_framework-0.1.65 → pulse_framework-0.1.66a2}/src/pulse/hooks/setup.py +0 -0
- {pulse_framework-0.1.65 → pulse_framework-0.1.66a2}/src/pulse/hooks/stable.py +0 -0
- {pulse_framework-0.1.65 → pulse_framework-0.1.66a2}/src/pulse/hooks/state.py +0 -0
- {pulse_framework-0.1.65 → pulse_framework-0.1.66a2}/src/pulse/js/__init__.py +0 -0
- {pulse_framework-0.1.65 → pulse_framework-0.1.66a2}/src/pulse/js/__init__.pyi +0 -0
- {pulse_framework-0.1.65 → pulse_framework-0.1.66a2}/src/pulse/js/_types.py +0 -0
- {pulse_framework-0.1.65 → pulse_framework-0.1.66a2}/src/pulse/js/array.py +0 -0
- {pulse_framework-0.1.65 → pulse_framework-0.1.66a2}/src/pulse/js/console.py +0 -0
- {pulse_framework-0.1.65 → pulse_framework-0.1.66a2}/src/pulse/js/date.py +0 -0
- {pulse_framework-0.1.65 → pulse_framework-0.1.66a2}/src/pulse/js/document.py +0 -0
- {pulse_framework-0.1.65 → pulse_framework-0.1.66a2}/src/pulse/js/error.py +0 -0
- {pulse_framework-0.1.65 → pulse_framework-0.1.66a2}/src/pulse/js/json.py +0 -0
- {pulse_framework-0.1.65 → pulse_framework-0.1.66a2}/src/pulse/js/map.py +0 -0
- {pulse_framework-0.1.65 → pulse_framework-0.1.66a2}/src/pulse/js/math.py +0 -0
- {pulse_framework-0.1.65 → pulse_framework-0.1.66a2}/src/pulse/js/navigator.py +0 -0
- {pulse_framework-0.1.65 → pulse_framework-0.1.66a2}/src/pulse/js/number.py +0 -0
- {pulse_framework-0.1.65 → pulse_framework-0.1.66a2}/src/pulse/js/obj.py +0 -0
- {pulse_framework-0.1.65 → pulse_framework-0.1.66a2}/src/pulse/js/object.py +0 -0
- {pulse_framework-0.1.65 → pulse_framework-0.1.66a2}/src/pulse/js/promise.py +0 -0
- {pulse_framework-0.1.65 → pulse_framework-0.1.66a2}/src/pulse/js/pulse.py +0 -0
- {pulse_framework-0.1.65 → pulse_framework-0.1.66a2}/src/pulse/js/react.py +0 -0
- {pulse_framework-0.1.65 → pulse_framework-0.1.66a2}/src/pulse/js/regexp.py +0 -0
- {pulse_framework-0.1.65 → pulse_framework-0.1.66a2}/src/pulse/js/set.py +0 -0
- {pulse_framework-0.1.65 → pulse_framework-0.1.66a2}/src/pulse/js/string.py +0 -0
- {pulse_framework-0.1.65 → pulse_framework-0.1.66a2}/src/pulse/js/weakmap.py +0 -0
- {pulse_framework-0.1.65 → pulse_framework-0.1.66a2}/src/pulse/js/weakset.py +0 -0
- {pulse_framework-0.1.65 → pulse_framework-0.1.66a2}/src/pulse/js/window.py +0 -0
- {pulse_framework-0.1.65 → pulse_framework-0.1.66a2}/src/pulse/messages.py +0 -0
- {pulse_framework-0.1.65 → pulse_framework-0.1.66a2}/src/pulse/middleware.py +0 -0
- {pulse_framework-0.1.65 → pulse_framework-0.1.66a2}/src/pulse/plugin.py +0 -0
- {pulse_framework-0.1.65 → pulse_framework-0.1.66a2}/src/pulse/proxy.py +0 -0
- {pulse_framework-0.1.65 → pulse_framework-0.1.66a2}/src/pulse/py.typed +0 -0
- {pulse_framework-0.1.65 → pulse_framework-0.1.66a2}/src/pulse/queries/__init__.py +0 -0
- {pulse_framework-0.1.65 → pulse_framework-0.1.66a2}/src/pulse/queries/client.py +0 -0
- {pulse_framework-0.1.65 → pulse_framework-0.1.66a2}/src/pulse/queries/common.py +0 -0
- {pulse_framework-0.1.65 → pulse_framework-0.1.66a2}/src/pulse/queries/effect.py +0 -0
- {pulse_framework-0.1.65 → pulse_framework-0.1.66a2}/src/pulse/queries/infinite_query.py +0 -0
- {pulse_framework-0.1.65 → pulse_framework-0.1.66a2}/src/pulse/queries/mutation.py +0 -0
- {pulse_framework-0.1.65 → pulse_framework-0.1.66a2}/src/pulse/queries/protocol.py +0 -0
- {pulse_framework-0.1.65 → pulse_framework-0.1.66a2}/src/pulse/queries/query.py +0 -0
- {pulse_framework-0.1.65 → pulse_framework-0.1.66a2}/src/pulse/queries/store.py +0 -0
- {pulse_framework-0.1.65 → pulse_framework-0.1.66a2}/src/pulse/react_component.py +0 -0
- {pulse_framework-0.1.65 → pulse_framework-0.1.66a2}/src/pulse/reactive.py +0 -0
- {pulse_framework-0.1.65 → pulse_framework-0.1.66a2}/src/pulse/reactive_extensions.py +0 -0
- {pulse_framework-0.1.65 → pulse_framework-0.1.66a2}/src/pulse/renderer.py +0 -0
- {pulse_framework-0.1.65 → pulse_framework-0.1.66a2}/src/pulse/request.py +0 -0
- {pulse_framework-0.1.65 → pulse_framework-0.1.66a2}/src/pulse/requirements.py +0 -0
- {pulse_framework-0.1.65 → pulse_framework-0.1.66a2}/src/pulse/routing.py +0 -0
- {pulse_framework-0.1.65 → pulse_framework-0.1.66a2}/src/pulse/scheduling.py +0 -0
- {pulse_framework-0.1.65 → pulse_framework-0.1.66a2}/src/pulse/serializer.py +0 -0
- {pulse_framework-0.1.65 → pulse_framework-0.1.66a2}/src/pulse/state.py +0 -0
- {pulse_framework-0.1.65 → pulse_framework-0.1.66a2}/src/pulse/test_helpers.py +0 -0
- {pulse_framework-0.1.65 → pulse_framework-0.1.66a2}/src/pulse/transpiler/__init__.py +0 -0
- {pulse_framework-0.1.65 → pulse_framework-0.1.66a2}/src/pulse/transpiler/assets.py +0 -0
- {pulse_framework-0.1.65 → pulse_framework-0.1.66a2}/src/pulse/transpiler/builtins.py +0 -0
- {pulse_framework-0.1.65 → pulse_framework-0.1.66a2}/src/pulse/transpiler/dynamic_import.py +0 -0
- {pulse_framework-0.1.65 → pulse_framework-0.1.66a2}/src/pulse/transpiler/emit_context.py +0 -0
- {pulse_framework-0.1.65 → pulse_framework-0.1.66a2}/src/pulse/transpiler/errors.py +0 -0
- {pulse_framework-0.1.65 → pulse_framework-0.1.66a2}/src/pulse/transpiler/function.py +0 -0
- {pulse_framework-0.1.65 → pulse_framework-0.1.66a2}/src/pulse/transpiler/id.py +0 -0
- {pulse_framework-0.1.65 → pulse_framework-0.1.66a2}/src/pulse/transpiler/imports.py +0 -0
- {pulse_framework-0.1.65 → pulse_framework-0.1.66a2}/src/pulse/transpiler/js_module.py +0 -0
- {pulse_framework-0.1.65 → pulse_framework-0.1.66a2}/src/pulse/transpiler/modules/__init__.py +0 -0
- {pulse_framework-0.1.65 → pulse_framework-0.1.66a2}/src/pulse/transpiler/modules/asyncio.py +0 -0
- {pulse_framework-0.1.65 → pulse_framework-0.1.66a2}/src/pulse/transpiler/modules/json.py +0 -0
- {pulse_framework-0.1.65 → pulse_framework-0.1.66a2}/src/pulse/transpiler/modules/math.py +0 -0
- {pulse_framework-0.1.65 → pulse_framework-0.1.66a2}/src/pulse/transpiler/modules/pulse/__init__.py +0 -0
- {pulse_framework-0.1.65 → pulse_framework-0.1.66a2}/src/pulse/transpiler/modules/pulse/tags.py +0 -0
- {pulse_framework-0.1.65 → pulse_framework-0.1.66a2}/src/pulse/transpiler/modules/typing.py +0 -0
- {pulse_framework-0.1.65 → pulse_framework-0.1.66a2}/src/pulse/transpiler/nodes.py +0 -0
- {pulse_framework-0.1.65 → pulse_framework-0.1.66a2}/src/pulse/transpiler/py_module.py +0 -0
- {pulse_framework-0.1.65 → pulse_framework-0.1.66a2}/src/pulse/transpiler/transpiler.py +0 -0
- {pulse_framework-0.1.65 → pulse_framework-0.1.66a2}/src/pulse/transpiler/vdom.py +0 -0
- {pulse_framework-0.1.65 → pulse_framework-0.1.66a2}/src/pulse/types/__init__.py +0 -0
- {pulse_framework-0.1.65 → pulse_framework-0.1.66a2}/src/pulse/types/event_handler.py +0 -0
- {pulse_framework-0.1.65 → pulse_framework-0.1.66a2}/src/pulse/user_session.py +0 -0
- {pulse_framework-0.1.65 → pulse_framework-0.1.66a2}/src/pulse/version.py +0 -0
|
@@ -107,8 +107,8 @@ PulseMode = Literal["subdomains", "single-server"]
|
|
|
107
107
|
"""Deployment mode for the application.
|
|
108
108
|
|
|
109
109
|
Values:
|
|
110
|
-
|
|
111
|
-
|
|
110
|
+
"single-server": Python and React served from the same origin (default).
|
|
111
|
+
"subdomains": Python API on a subdomain (e.g., api.example.com).
|
|
112
112
|
"""
|
|
113
113
|
|
|
114
114
|
|
|
@@ -118,12 +118,12 @@ class ConnectionStatusConfig:
|
|
|
118
118
|
Configuration for connection status message delays.
|
|
119
119
|
|
|
120
120
|
Attributes:
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
121
|
+
initial_connecting_delay: Delay in seconds before showing "Connecting..." message
|
|
122
|
+
on initial connection attempt. Default: 2.0
|
|
123
|
+
initial_error_delay: Additional delay in seconds before showing error message
|
|
124
|
+
on initial connection attempt (after connecting message). Default: 8.0
|
|
125
|
+
reconnect_error_delay: Delay in seconds before showing error message when
|
|
126
|
+
reconnecting after losing connection. Default: 8.0
|
|
127
127
|
"""
|
|
128
128
|
|
|
129
129
|
initial_connecting_delay: float = 2.0
|
|
@@ -171,15 +171,15 @@ class App:
|
|
|
171
171
|
import pulse as ps
|
|
172
172
|
|
|
173
173
|
app = ps.App(
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
174
|
+
routes=[
|
|
175
|
+
ps.Route("/", render=home),
|
|
176
|
+
ps.Route("/users/:id", render=user_detail),
|
|
177
|
+
],
|
|
178
|
+
session_timeout=120.0,
|
|
179
179
|
)
|
|
180
180
|
|
|
181
181
|
if __name__ == "__main__":
|
|
182
|
-
|
|
182
|
+
app.run(port=8000)
|
|
183
183
|
```
|
|
184
184
|
"""
|
|
185
185
|
|
|
@@ -544,10 +544,23 @@ class App:
|
|
|
544
544
|
paths = [ensure_absolute_path(path) for path in paths]
|
|
545
545
|
payload["paths"] = paths
|
|
546
546
|
route_info = payload.get("routeInfo")
|
|
547
|
+
debug = os.environ.get("PULSE_DEBUG_RENDER")
|
|
548
|
+
if debug:
|
|
549
|
+
print(
|
|
550
|
+
"[PulseDebug][prerender] session=%s header_render_id=%s payload_render_id=%s paths=%s route_info=%s"
|
|
551
|
+
% (
|
|
552
|
+
session.sid,
|
|
553
|
+
request.headers.get("x-pulse-render-id"),
|
|
554
|
+
payload.get("renderId"),
|
|
555
|
+
paths,
|
|
556
|
+
route_info,
|
|
557
|
+
)
|
|
558
|
+
)
|
|
547
559
|
|
|
548
560
|
client_addr: str | None = get_client_address(request)
|
|
549
561
|
# Reuse render session from header (set by middleware) or create new one
|
|
550
562
|
render = PulseContext.get().render
|
|
563
|
+
reused = render is not None
|
|
551
564
|
if render is not None:
|
|
552
565
|
render_id = render.id
|
|
553
566
|
else:
|
|
@@ -556,9 +569,15 @@ class App:
|
|
|
556
569
|
render = self.create_render(
|
|
557
570
|
render_id, session, client_address=client_addr
|
|
558
571
|
)
|
|
572
|
+
if debug:
|
|
573
|
+
print(
|
|
574
|
+
"[PulseDebug][prerender] session=%s render=%s reused=%s connected=%s"
|
|
575
|
+
% (session.sid, render_id, reused, render.connected)
|
|
576
|
+
)
|
|
559
577
|
|
|
560
578
|
# Schedule cleanup timeout (will cancel/reschedule on activity)
|
|
561
|
-
|
|
579
|
+
if not render.connected:
|
|
580
|
+
self._schedule_render_cleanup(render_id)
|
|
562
581
|
|
|
563
582
|
def _normalize_prerender_result(
|
|
564
583
|
captured: ServerInitMessage | ServerNavigateToMessage,
|
|
@@ -692,12 +711,16 @@ class App:
|
|
|
692
711
|
):
|
|
693
712
|
# Expect renderId during websocket auth and require a valid user session
|
|
694
713
|
rid = auth.get("render_id") if auth else None
|
|
695
|
-
|
|
696
714
|
# Parse cookies from environ and ensure a session exists
|
|
697
715
|
cookie = self.cookie.get_from_socketio(environ)
|
|
698
716
|
if cookie is None:
|
|
699
717
|
raise ConnectionRefusedError("Socket connect missing cookie")
|
|
700
718
|
session = await self.get_or_create_session(cookie)
|
|
719
|
+
debug = os.environ.get("PULSE_DEBUG_RENDER")
|
|
720
|
+
if debug:
|
|
721
|
+
print(
|
|
722
|
+
"[PulseDebug][connect] session=%s render_id=%s" % (session.sid, rid)
|
|
723
|
+
)
|
|
701
724
|
|
|
702
725
|
if not rid:
|
|
703
726
|
# Still refuse connections without a renderId
|
|
@@ -708,6 +731,12 @@ class App:
|
|
|
708
731
|
# Allow reconnects where the provided renderId no longer exists by creating a new RenderSession
|
|
709
732
|
render = self.render_sessions.get(rid)
|
|
710
733
|
if render is None:
|
|
734
|
+
# The client will try to attach to a non-existing RouteMount, which will cause a reload down the line
|
|
735
|
+
if debug:
|
|
736
|
+
print(
|
|
737
|
+
"[PulseDebug][connect] render_missing session=%s render_id=%s creating=true"
|
|
738
|
+
% (session.sid, rid)
|
|
739
|
+
)
|
|
711
740
|
render = self.create_render(
|
|
712
741
|
rid, session, client_address=get_client_address_socketio(environ)
|
|
713
742
|
)
|
|
@@ -718,6 +747,11 @@ class App:
|
|
|
718
747
|
f"Socket connect session mismatch render={render.id} "
|
|
719
748
|
+ f"owner={owner} session={session.sid}"
|
|
720
749
|
)
|
|
750
|
+
if debug:
|
|
751
|
+
print(
|
|
752
|
+
"[PulseDebug][connect] render_found session=%s render_id=%s owner=%s connected=%s"
|
|
753
|
+
% (session.sid, render.id, owner, render.connected)
|
|
754
|
+
)
|
|
721
755
|
|
|
722
756
|
def on_message(message: ServerMessage):
|
|
723
757
|
payload = serialize(message)
|
|
@@ -830,6 +864,22 @@ class App:
|
|
|
830
864
|
async def _handle_pulse_message(
|
|
831
865
|
self, render: RenderSession, session: UserSession, msg: ClientPulseMessage
|
|
832
866
|
) -> None:
|
|
867
|
+
if os.environ.get("PULSE_DEBUG_RENDER") and msg["type"] in (
|
|
868
|
+
"attach",
|
|
869
|
+
"update",
|
|
870
|
+
"detach",
|
|
871
|
+
):
|
|
872
|
+
print(
|
|
873
|
+
"[PulseDebug][client-message] session=%s render=%s type=%s path=%s route_info=%s"
|
|
874
|
+
% (
|
|
875
|
+
session.sid,
|
|
876
|
+
render.id,
|
|
877
|
+
msg["type"],
|
|
878
|
+
msg.get("path"),
|
|
879
|
+
msg.get("routeInfo"),
|
|
880
|
+
)
|
|
881
|
+
)
|
|
882
|
+
|
|
833
883
|
async def _next() -> Ok[None]:
|
|
834
884
|
if msg["type"] == "attach":
|
|
835
885
|
render.attach(msg["path"], msg["routeInfo"])
|
|
@@ -53,15 +53,11 @@ export async function loader(args: LoaderFunctionArgs) {
|
|
|
53
53
|
return data(prerenderData, { headers });
|
|
54
54
|
}
|
|
55
55
|
|
|
56
|
-
// Client loader: re-prerender on navigation while reusing
|
|
56
|
+
// Client loader: re-prerender on navigation while reusing directives
|
|
57
57
|
export async function clientLoader(args: ClientLoaderFunctionArgs) {
|
|
58
58
|
const url = new URL(args.request.url);
|
|
59
59
|
const matches = matchRoutes(rrPulseRouteTree, url.pathname) ?? [];
|
|
60
60
|
const paths = matches.map(m => m.route.uniquePath);
|
|
61
|
-
const renderId =
|
|
62
|
-
typeof window !== "undefined" && typeof sessionStorage !== "undefined"
|
|
63
|
-
? (sessionStorage.getItem("__PULSE_RENDER_ID") ?? undefined)
|
|
64
|
-
: undefined;
|
|
65
61
|
const directives =
|
|
66
62
|
typeof window !== "undefined" && typeof sessionStorage !== "undefined"
|
|
67
63
|
? (JSON.parse(sessionStorage.getItem("__PULSE_DIRECTIVES") ?? "{}"))
|
|
@@ -76,7 +72,7 @@ export async function clientLoader(args: ClientLoaderFunctionArgs) {
|
|
|
76
72
|
method: "POST",
|
|
77
73
|
headers,
|
|
78
74
|
credentials: "include",
|
|
79
|
-
body: JSON.stringify({ paths, routeInfo: extractServerRouteInfo(args)
|
|
75
|
+
body: JSON.stringify({ paths, routeInfo: extractServerRouteInfo(args) }),
|
|
80
76
|
});
|
|
81
77
|
if (!res.ok) throw new Error("Failed to prerender batch:" + res.status);
|
|
82
78
|
const body = await res.json();
|
|
@@ -92,7 +88,6 @@ export async function clientLoader(args: ClientLoaderFunctionArgs) {
|
|
|
92
88
|
export default function PulseLayout() {
|
|
93
89
|
const data = useLoaderData<typeof loader>();
|
|
94
90
|
if (typeof window !== "undefined" && typeof sessionStorage !== "undefined") {
|
|
95
|
-
sessionStorage.setItem("__PULSE_RENDER_ID", data.renderId);
|
|
96
91
|
sessionStorage.setItem("__PULSE_DIRECTIVES", JSON.stringify(data.directives));
|
|
97
92
|
}
|
|
98
93
|
return (
|
|
@@ -101,6 +96,6 @@ export default function PulseLayout() {
|
|
|
101
96
|
</PulseProvider>
|
|
102
97
|
);
|
|
103
98
|
}
|
|
104
|
-
// Persist
|
|
99
|
+
// Persist directives in sessionStorage for reuse in clientLoader is handled within the component
|
|
105
100
|
"""
|
|
106
101
|
)
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import asyncio
|
|
2
2
|
import logging
|
|
3
|
+
import os
|
|
3
4
|
import traceback
|
|
4
5
|
import uuid
|
|
5
6
|
from asyncio import iscoroutine
|
|
@@ -137,6 +138,11 @@ class RouteMount:
|
|
|
137
138
|
if self.state != "pending":
|
|
138
139
|
return
|
|
139
140
|
action = self.pending_action
|
|
141
|
+
if os.environ.get("PULSE_DEBUG_RENDER"):
|
|
142
|
+
print(
|
|
143
|
+
"[PulseDebug][mount-timeout] render=%s path=%s action=%s state=%s"
|
|
144
|
+
% (self.render.id, self.path, action, self.state)
|
|
145
|
+
)
|
|
140
146
|
self.pending_action = None
|
|
141
147
|
if action == "dispose":
|
|
142
148
|
self.render.dispose_mount(self.path, self)
|
|
@@ -144,6 +150,11 @@ class RouteMount:
|
|
|
144
150
|
self.to_idle()
|
|
145
151
|
|
|
146
152
|
def start_pending(self, timeout: float, *, action: PendingAction = "idle") -> None:
|
|
153
|
+
if os.environ.get("PULSE_DEBUG_RENDER"):
|
|
154
|
+
print(
|
|
155
|
+
"[PulseDebug][mount-pending] render=%s path=%s state=%s action=%s timeout=%s"
|
|
156
|
+
% (self.render.id, self.path, self.state, action, timeout)
|
|
157
|
+
)
|
|
147
158
|
if self.state == "pending":
|
|
148
159
|
prev_action = self.pending_action
|
|
149
160
|
next_action: PendingAction = (
|
|
@@ -166,6 +177,16 @@ class RouteMount:
|
|
|
166
177
|
)
|
|
167
178
|
|
|
168
179
|
def activate(self, send_message: Callable[[ServerMessage], Any]) -> None:
|
|
180
|
+
if os.environ.get("PULSE_DEBUG_RENDER"):
|
|
181
|
+
print(
|
|
182
|
+
"[PulseDebug][mount-activate] render=%s path=%s state=%s queued=%s"
|
|
183
|
+
% (
|
|
184
|
+
self.render.id,
|
|
185
|
+
self.path,
|
|
186
|
+
self.state,
|
|
187
|
+
0 if not self.queue else len(self.queue),
|
|
188
|
+
)
|
|
189
|
+
)
|
|
169
190
|
if self.state != "pending":
|
|
170
191
|
return
|
|
171
192
|
self._cancel_pending_timeout()
|
|
@@ -192,6 +213,11 @@ class RouteMount:
|
|
|
192
213
|
def to_idle(self) -> None:
|
|
193
214
|
if self.state != "pending":
|
|
194
215
|
return
|
|
216
|
+
if os.environ.get("PULSE_DEBUG_RENDER"):
|
|
217
|
+
print(
|
|
218
|
+
"[PulseDebug][mount-idle] render=%s path=%s"
|
|
219
|
+
% (self.render.id, self.path)
|
|
220
|
+
)
|
|
195
221
|
self.state = "idle"
|
|
196
222
|
self.queue = None
|
|
197
223
|
self._cancel_pending_timeout()
|
|
@@ -317,11 +343,15 @@ class RenderSession:
|
|
|
317
343
|
|
|
318
344
|
def connect(self, send_message: Callable[[ServerMessage], Any]):
|
|
319
345
|
"""WebSocket connected. Set sender, don't auto-flush (attach does that)."""
|
|
346
|
+
if os.environ.get("PULSE_DEBUG_RENDER"):
|
|
347
|
+
print("[PulseDebug][render-connect] render=%s" % self.id)
|
|
320
348
|
self._send_message = send_message
|
|
321
349
|
self.connected = True
|
|
322
350
|
|
|
323
351
|
def disconnect(self):
|
|
324
352
|
"""WebSocket disconnected. Start queuing briefly before pausing."""
|
|
353
|
+
if os.environ.get("PULSE_DEBUG_RENDER"):
|
|
354
|
+
print("[PulseDebug][render-disconnect] render=%s" % self.id)
|
|
325
355
|
self._send_message = None
|
|
326
356
|
self.connected = False
|
|
327
357
|
|
|
@@ -395,6 +425,11 @@ class RenderSession:
|
|
|
395
425
|
- Creates mounts in PENDING state and starts queue
|
|
396
426
|
"""
|
|
397
427
|
normalized = [ensure_absolute_path(path) for path in paths]
|
|
428
|
+
if os.environ.get("PULSE_DEBUG_RENDER"):
|
|
429
|
+
print(
|
|
430
|
+
"[PulseDebug][prerender] render=%s paths=%s route_info=%s"
|
|
431
|
+
% (self.id, normalized, route_info)
|
|
432
|
+
)
|
|
398
433
|
|
|
399
434
|
results: dict[str, ServerInitMessage | ServerNavigateToMessage] = {}
|
|
400
435
|
|
|
@@ -402,6 +437,12 @@ class RenderSession:
|
|
|
402
437
|
route = self.routes.find(path)
|
|
403
438
|
info = route_info or route.default_route_info()
|
|
404
439
|
mount = self.route_mounts.get(path)
|
|
440
|
+
if os.environ.get("PULSE_DEBUG_RENDER"):
|
|
441
|
+
route_label = repr(route)
|
|
442
|
+
print(
|
|
443
|
+
"[PulseDebug][prerender] render=%s path=%s mount_state=%s route=%s"
|
|
444
|
+
% (self.id, path, mount.state if mount else None, route_label)
|
|
445
|
+
)
|
|
405
446
|
|
|
406
447
|
if mount is None:
|
|
407
448
|
mount = RouteMount(self, path, route, info)
|
|
@@ -440,6 +481,17 @@ class RenderSession:
|
|
|
440
481
|
mount = self.route_mounts.get(path)
|
|
441
482
|
|
|
442
483
|
if mount is None or mount.state == "idle":
|
|
484
|
+
if os.environ.get("PULSE_DEBUG_RENDER"):
|
|
485
|
+
print(
|
|
486
|
+
"[PulseDebug][attach] render=%s path=%s mount_state=%s mounts=%s route_info=%s"
|
|
487
|
+
% (
|
|
488
|
+
self.id,
|
|
489
|
+
path,
|
|
490
|
+
mount.state if mount else None,
|
|
491
|
+
{key: value.state for key, value in self.route_mounts.items()},
|
|
492
|
+
route_info,
|
|
493
|
+
)
|
|
494
|
+
)
|
|
443
495
|
# Initial render must come from prerender
|
|
444
496
|
print(f"[DEBUG] Missing or idle route '{path}', reloading")
|
|
445
497
|
self.send({"type": "reload"})
|
|
@@ -448,6 +500,11 @@ class RenderSession:
|
|
|
448
500
|
# Update route info for active and pending mounts
|
|
449
501
|
mount.update_route(route_info)
|
|
450
502
|
if mount.state == "pending" and self._send_message:
|
|
503
|
+
if os.environ.get("PULSE_DEBUG_RENDER"):
|
|
504
|
+
print(
|
|
505
|
+
"[PulseDebug][attach] render=%s path=%s activating=true"
|
|
506
|
+
% (self.id, path)
|
|
507
|
+
)
|
|
451
508
|
mount.activate(self._send_message)
|
|
452
509
|
|
|
453
510
|
def update_route(self, path: str, route_info: RouteInfo):
|
|
@@ -463,6 +520,11 @@ class RenderSession:
|
|
|
463
520
|
current = self.route_mounts.get(path)
|
|
464
521
|
if current is not mount:
|
|
465
522
|
return
|
|
523
|
+
if os.environ.get("PULSE_DEBUG_RENDER"):
|
|
524
|
+
print(
|
|
525
|
+
"[PulseDebug][mount-dispose] render=%s path=%s state=%s"
|
|
526
|
+
% (self.id, path, mount.state)
|
|
527
|
+
)
|
|
466
528
|
try:
|
|
467
529
|
self.route_mounts.pop(path, None)
|
|
468
530
|
mount.dispose()
|
|
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
|
{pulse_framework-0.1.65 → pulse_framework-0.1.66a2}/src/pulse/codegen/templates/routes_ts.py
RENAMED
|
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
|
{pulse_framework-0.1.65 → pulse_framework-0.1.66a2}/src/pulse/transpiler/modules/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pulse_framework-0.1.65 → pulse_framework-0.1.66a2}/src/pulse/transpiler/modules/pulse/__init__.py
RENAMED
|
File without changes
|
{pulse_framework-0.1.65 → pulse_framework-0.1.66a2}/src/pulse/transpiler/modules/pulse/tags.py
RENAMED
|
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
|