pulse-framework 0.1.62__py3-none-any.whl
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/__init__.py +1493 -0
- pulse/_examples.py +29 -0
- pulse/app.py +1086 -0
- pulse/channel.py +607 -0
- pulse/cli/__init__.py +0 -0
- pulse/cli/cmd.py +575 -0
- pulse/cli/dependencies.py +181 -0
- pulse/cli/folder_lock.py +134 -0
- pulse/cli/helpers.py +271 -0
- pulse/cli/logging.py +102 -0
- pulse/cli/models.py +35 -0
- pulse/cli/packages.py +262 -0
- pulse/cli/processes.py +292 -0
- pulse/cli/secrets.py +39 -0
- pulse/cli/uvicorn_log_config.py +87 -0
- pulse/code_analysis.py +38 -0
- pulse/codegen/__init__.py +0 -0
- pulse/codegen/codegen.py +359 -0
- pulse/codegen/templates/__init__.py +0 -0
- pulse/codegen/templates/layout.py +106 -0
- pulse/codegen/templates/route.py +345 -0
- pulse/codegen/templates/routes_ts.py +42 -0
- pulse/codegen/utils.py +20 -0
- pulse/component.py +237 -0
- pulse/components/__init__.py +0 -0
- pulse/components/for_.py +83 -0
- pulse/components/if_.py +86 -0
- pulse/components/react_router.py +94 -0
- pulse/context.py +108 -0
- pulse/cookies.py +322 -0
- pulse/decorators.py +344 -0
- pulse/dom/__init__.py +0 -0
- pulse/dom/elements.py +1024 -0
- pulse/dom/events.py +445 -0
- pulse/dom/props.py +1250 -0
- pulse/dom/svg.py +0 -0
- pulse/dom/tags.py +328 -0
- pulse/dom/tags.pyi +480 -0
- pulse/env.py +178 -0
- pulse/form.py +538 -0
- pulse/helpers.py +541 -0
- pulse/hooks/__init__.py +0 -0
- pulse/hooks/core.py +452 -0
- pulse/hooks/effects.py +88 -0
- pulse/hooks/init.py +668 -0
- pulse/hooks/runtime.py +464 -0
- pulse/hooks/setup.py +254 -0
- pulse/hooks/stable.py +138 -0
- pulse/hooks/state.py +192 -0
- pulse/js/__init__.py +125 -0
- pulse/js/__init__.pyi +115 -0
- pulse/js/_types.py +299 -0
- pulse/js/array.py +339 -0
- pulse/js/console.py +50 -0
- pulse/js/date.py +119 -0
- pulse/js/document.py +145 -0
- pulse/js/error.py +140 -0
- pulse/js/json.py +66 -0
- pulse/js/map.py +97 -0
- pulse/js/math.py +69 -0
- pulse/js/navigator.py +79 -0
- pulse/js/number.py +57 -0
- pulse/js/obj.py +81 -0
- pulse/js/object.py +172 -0
- pulse/js/promise.py +172 -0
- pulse/js/pulse.py +115 -0
- pulse/js/react.py +495 -0
- pulse/js/regexp.py +57 -0
- pulse/js/set.py +124 -0
- pulse/js/string.py +38 -0
- pulse/js/weakmap.py +53 -0
- pulse/js/weakset.py +48 -0
- pulse/js/window.py +205 -0
- pulse/messages.py +202 -0
- pulse/middleware.py +471 -0
- pulse/plugin.py +96 -0
- pulse/proxy.py +242 -0
- pulse/py.typed +0 -0
- pulse/queries/__init__.py +0 -0
- pulse/queries/client.py +609 -0
- pulse/queries/common.py +101 -0
- pulse/queries/effect.py +55 -0
- pulse/queries/infinite_query.py +1418 -0
- pulse/queries/mutation.py +295 -0
- pulse/queries/protocol.py +136 -0
- pulse/queries/query.py +1314 -0
- pulse/queries/store.py +120 -0
- pulse/react_component.py +88 -0
- pulse/reactive.py +1208 -0
- pulse/reactive_extensions.py +1172 -0
- pulse/render_session.py +768 -0
- pulse/renderer.py +584 -0
- pulse/request.py +205 -0
- pulse/routing.py +598 -0
- pulse/serializer.py +279 -0
- pulse/state.py +556 -0
- pulse/test_helpers.py +15 -0
- pulse/transpiler/__init__.py +111 -0
- pulse/transpiler/assets.py +81 -0
- pulse/transpiler/builtins.py +1029 -0
- pulse/transpiler/dynamic_import.py +130 -0
- pulse/transpiler/emit_context.py +49 -0
- pulse/transpiler/errors.py +96 -0
- pulse/transpiler/function.py +611 -0
- pulse/transpiler/id.py +18 -0
- pulse/transpiler/imports.py +341 -0
- pulse/transpiler/js_module.py +336 -0
- pulse/transpiler/modules/__init__.py +33 -0
- pulse/transpiler/modules/asyncio.py +57 -0
- pulse/transpiler/modules/json.py +24 -0
- pulse/transpiler/modules/math.py +265 -0
- pulse/transpiler/modules/pulse/__init__.py +5 -0
- pulse/transpiler/modules/pulse/tags.py +250 -0
- pulse/transpiler/modules/typing.py +63 -0
- pulse/transpiler/nodes.py +1987 -0
- pulse/transpiler/py_module.py +135 -0
- pulse/transpiler/transpiler.py +1100 -0
- pulse/transpiler/vdom.py +256 -0
- pulse/types/__init__.py +0 -0
- pulse/types/event_handler.py +50 -0
- pulse/user_session.py +386 -0
- pulse/version.py +69 -0
- pulse_framework-0.1.62.dist-info/METADATA +198 -0
- pulse_framework-0.1.62.dist-info/RECORD +126 -0
- pulse_framework-0.1.62.dist-info/WHEEL +4 -0
- pulse_framework-0.1.62.dist-info/entry_points.txt +3 -0
pulse/request.py
ADDED
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from collections.abc import Mapping, MutableMapping
|
|
4
|
+
from http.cookies import SimpleCookie
|
|
5
|
+
from typing import Any, cast
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def _bytes_kv_to_str(headers: list[tuple[bytes, bytes]]) -> dict[str, str]:
|
|
9
|
+
out: dict[str, str] = {}
|
|
10
|
+
for k, v in headers:
|
|
11
|
+
try:
|
|
12
|
+
out[k.decode("latin1").lower()] = v.decode("latin1")
|
|
13
|
+
except Exception:
|
|
14
|
+
# Best effort
|
|
15
|
+
out[str(k).lower()] = str(v)
|
|
16
|
+
return out
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class PulseRequest:
|
|
20
|
+
"""Normalized request object for both HTTP prerender and WebSocket connect.
|
|
21
|
+
|
|
22
|
+
Provides a consistent interface for accessing request data regardless of
|
|
23
|
+
the underlying transport (FastAPI/Starlette HTTP or Socket.IO WebSocket).
|
|
24
|
+
|
|
25
|
+
Attributes:
|
|
26
|
+
headers: Request headers with lowercased keys.
|
|
27
|
+
cookies: Request cookies as name-value pairs.
|
|
28
|
+
scheme: URL scheme (http/https).
|
|
29
|
+
method: HTTP method (GET, POST, etc.).
|
|
30
|
+
path: URL path.
|
|
31
|
+
query_string: Query string (without leading ?).
|
|
32
|
+
client: Client address as (host, port) tuple, or None.
|
|
33
|
+
auth: Auth data (Socket.IO only).
|
|
34
|
+
raw: Underlying request object for advanced use.
|
|
35
|
+
|
|
36
|
+
Args:
|
|
37
|
+
headers: Request headers (keys will be lowercased).
|
|
38
|
+
cookies: Request cookies.
|
|
39
|
+
scheme: URL scheme (http/https).
|
|
40
|
+
method: HTTP method.
|
|
41
|
+
path: URL path.
|
|
42
|
+
query_string: Query string (without ?).
|
|
43
|
+
client: Client address as (host, port) tuple.
|
|
44
|
+
auth: Auth data (for Socket.IO).
|
|
45
|
+
raw: Underlying request object.
|
|
46
|
+
"""
|
|
47
|
+
|
|
48
|
+
headers: dict[str, str]
|
|
49
|
+
cookies: dict[str, str]
|
|
50
|
+
scheme: str
|
|
51
|
+
method: str
|
|
52
|
+
path: str
|
|
53
|
+
query_string: str
|
|
54
|
+
client: tuple[str, int] | None
|
|
55
|
+
auth: Any | None
|
|
56
|
+
raw: Any | None
|
|
57
|
+
|
|
58
|
+
def __init__(
|
|
59
|
+
self,
|
|
60
|
+
*,
|
|
61
|
+
headers: Mapping[str, str] | None = None,
|
|
62
|
+
cookies: Mapping[str, str] | None = None,
|
|
63
|
+
scheme: str | None = None,
|
|
64
|
+
method: str | None = None,
|
|
65
|
+
path: str | None = None,
|
|
66
|
+
query_string: str | None = None,
|
|
67
|
+
client: tuple[str, int] | None = None,
|
|
68
|
+
auth: Any | None = None,
|
|
69
|
+
raw: Any | None = None,
|
|
70
|
+
) -> None:
|
|
71
|
+
self.headers = {k.lower(): v for k, v in (headers or {}).items()}
|
|
72
|
+
self.cookies = dict(cookies or {})
|
|
73
|
+
self.scheme = scheme or ""
|
|
74
|
+
self.method = method or ""
|
|
75
|
+
self.path = path or ""
|
|
76
|
+
self.query_string = query_string or ""
|
|
77
|
+
self.client = client
|
|
78
|
+
self.auth = auth
|
|
79
|
+
self.raw = raw
|
|
80
|
+
|
|
81
|
+
@property
|
|
82
|
+
def url(self) -> str:
|
|
83
|
+
"""Full URL including scheme, host, path, and query string."""
|
|
84
|
+
qs = f"?{self.query_string}" if self.query_string else ""
|
|
85
|
+
host = self.headers.get("host", "")
|
|
86
|
+
if host:
|
|
87
|
+
return f"{self.scheme or 'http'}://{host}{self.path}{qs}"
|
|
88
|
+
return f"{self.path}{qs}"
|
|
89
|
+
|
|
90
|
+
@staticmethod
|
|
91
|
+
def from_fastapi(request: Any) -> "PulseRequest":
|
|
92
|
+
"""Create from a FastAPI/Starlette request.
|
|
93
|
+
|
|
94
|
+
Args:
|
|
95
|
+
request: FastAPI/Starlette Request object.
|
|
96
|
+
|
|
97
|
+
Returns:
|
|
98
|
+
PulseRequest instance with normalized request data.
|
|
99
|
+
"""
|
|
100
|
+
# FastAPI/Starlette Request
|
|
101
|
+
headers = {k.lower(): v for k, v in request.headers.items()}
|
|
102
|
+
cookies = dict(request.cookies or {})
|
|
103
|
+
scheme = request.url.scheme
|
|
104
|
+
method = request.method
|
|
105
|
+
path = request.url.path
|
|
106
|
+
query_string = request.url.query or ""
|
|
107
|
+
client = (request.client.host, request.client.port) if request.client else None
|
|
108
|
+
return PulseRequest(
|
|
109
|
+
headers=headers,
|
|
110
|
+
cookies=cookies,
|
|
111
|
+
scheme=scheme,
|
|
112
|
+
method=method,
|
|
113
|
+
path=path,
|
|
114
|
+
query_string=query_string,
|
|
115
|
+
client=client,
|
|
116
|
+
raw=request,
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
@staticmethod
|
|
120
|
+
def from_socketio_environ(
|
|
121
|
+
environ: MutableMapping[str, Any], auth: Any | None
|
|
122
|
+
) -> "PulseRequest":
|
|
123
|
+
"""Create from a Socket.IO environ dictionary.
|
|
124
|
+
|
|
125
|
+
Args:
|
|
126
|
+
environ: Socket.IO environ dictionary (WSGI or ASGI-like).
|
|
127
|
+
auth: Auth data passed during Socket.IO connect.
|
|
128
|
+
|
|
129
|
+
Returns:
|
|
130
|
+
PulseRequest instance with normalized request data.
|
|
131
|
+
"""
|
|
132
|
+
# python-socketio passes a WSGI/ASGI-like environ. Try to detect ASGI scope first.
|
|
133
|
+
scope: MutableMapping[str, Any] = environ.get("asgi.scope") or environ
|
|
134
|
+
|
|
135
|
+
headers: dict[str, str] = {}
|
|
136
|
+
cookies: dict[str, str] = {}
|
|
137
|
+
scheme = ""
|
|
138
|
+
method = ""
|
|
139
|
+
path = ""
|
|
140
|
+
query_string = ""
|
|
141
|
+
client: tuple[str, int] | None = None
|
|
142
|
+
|
|
143
|
+
if isinstance(scope, Mapping) and "type" in scope: # ASGI scope
|
|
144
|
+
scheme = scope.get("scheme", "")
|
|
145
|
+
method = scope.get("method", "") or "GET"
|
|
146
|
+
path = scope.get("path", "")
|
|
147
|
+
raw_qs = scope.get("query_string", b"") or b""
|
|
148
|
+
try:
|
|
149
|
+
query_string = raw_qs.decode("latin1")
|
|
150
|
+
except Exception:
|
|
151
|
+
query_string = ""
|
|
152
|
+
asgi_headers = cast(
|
|
153
|
+
list[tuple[bytes, bytes]], scope.get("headers", []) or []
|
|
154
|
+
)
|
|
155
|
+
headers = _bytes_kv_to_str(asgi_headers)
|
|
156
|
+
# Cookies from header if present
|
|
157
|
+
cookie_header = headers.get("cookie")
|
|
158
|
+
if cookie_header:
|
|
159
|
+
sc = SimpleCookie()
|
|
160
|
+
sc.load(cookie_header)
|
|
161
|
+
cookies = {k: v.value for k, v in sc.items()}
|
|
162
|
+
scope_client = scope.get("client")
|
|
163
|
+
if scope_client:
|
|
164
|
+
client = tuple(scope_client)
|
|
165
|
+
else:
|
|
166
|
+
# WSGI-like environ
|
|
167
|
+
scheme = scope.get("wsgi.url_scheme", "") or scope.get("scheme", "")
|
|
168
|
+
method = scope.get("REQUEST_METHOD", "GET")
|
|
169
|
+
path = scope.get("PATH_INFO", "")
|
|
170
|
+
query_string = scope.get("QUERY_STRING", "")
|
|
171
|
+
# headers from HTTP_* keys
|
|
172
|
+
for k, v in scope.items():
|
|
173
|
+
if isinstance(k, str) and k.startswith("HTTP_"):
|
|
174
|
+
name = k[5:].replace("_", "-").lower()
|
|
175
|
+
headers[name] = str(v)
|
|
176
|
+
if "CONTENT_TYPE" in scope:
|
|
177
|
+
headers["content-type"] = str(scope["CONTENT_TYPE"]) # type: ignore
|
|
178
|
+
if "CONTENT_LENGTH" in scope:
|
|
179
|
+
headers["content-length"] = str(scope["CONTENT_LENGTH"]) # type: ignore
|
|
180
|
+
# Cookies
|
|
181
|
+
cookie_header = headers.get("cookie") or scope.get("HTTP_COOKIE")
|
|
182
|
+
if cookie_header:
|
|
183
|
+
sc = SimpleCookie()
|
|
184
|
+
sc.load(cookie_header) # type: ignore[arg-type]
|
|
185
|
+
cookies = {k: v.value for k, v in sc.items()}
|
|
186
|
+
# client is not standard in WSGI; try remote addr
|
|
187
|
+
remote = scope.get("REMOTE_ADDR")
|
|
188
|
+
port = scope.get("REMOTE_PORT")
|
|
189
|
+
if remote is not None and port is not None:
|
|
190
|
+
try:
|
|
191
|
+
client = (str(remote), int(port))
|
|
192
|
+
except Exception:
|
|
193
|
+
client = None
|
|
194
|
+
|
|
195
|
+
return PulseRequest(
|
|
196
|
+
headers=headers,
|
|
197
|
+
cookies=cookies,
|
|
198
|
+
scheme=scheme,
|
|
199
|
+
method=method,
|
|
200
|
+
path=path,
|
|
201
|
+
query_string=query_string,
|
|
202
|
+
client=client,
|
|
203
|
+
auth=auth,
|
|
204
|
+
raw=environ,
|
|
205
|
+
)
|