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.
Files changed (126) hide show
  1. pulse/__init__.py +1493 -0
  2. pulse/_examples.py +29 -0
  3. pulse/app.py +1086 -0
  4. pulse/channel.py +607 -0
  5. pulse/cli/__init__.py +0 -0
  6. pulse/cli/cmd.py +575 -0
  7. pulse/cli/dependencies.py +181 -0
  8. pulse/cli/folder_lock.py +134 -0
  9. pulse/cli/helpers.py +271 -0
  10. pulse/cli/logging.py +102 -0
  11. pulse/cli/models.py +35 -0
  12. pulse/cli/packages.py +262 -0
  13. pulse/cli/processes.py +292 -0
  14. pulse/cli/secrets.py +39 -0
  15. pulse/cli/uvicorn_log_config.py +87 -0
  16. pulse/code_analysis.py +38 -0
  17. pulse/codegen/__init__.py +0 -0
  18. pulse/codegen/codegen.py +359 -0
  19. pulse/codegen/templates/__init__.py +0 -0
  20. pulse/codegen/templates/layout.py +106 -0
  21. pulse/codegen/templates/route.py +345 -0
  22. pulse/codegen/templates/routes_ts.py +42 -0
  23. pulse/codegen/utils.py +20 -0
  24. pulse/component.py +237 -0
  25. pulse/components/__init__.py +0 -0
  26. pulse/components/for_.py +83 -0
  27. pulse/components/if_.py +86 -0
  28. pulse/components/react_router.py +94 -0
  29. pulse/context.py +108 -0
  30. pulse/cookies.py +322 -0
  31. pulse/decorators.py +344 -0
  32. pulse/dom/__init__.py +0 -0
  33. pulse/dom/elements.py +1024 -0
  34. pulse/dom/events.py +445 -0
  35. pulse/dom/props.py +1250 -0
  36. pulse/dom/svg.py +0 -0
  37. pulse/dom/tags.py +328 -0
  38. pulse/dom/tags.pyi +480 -0
  39. pulse/env.py +178 -0
  40. pulse/form.py +538 -0
  41. pulse/helpers.py +541 -0
  42. pulse/hooks/__init__.py +0 -0
  43. pulse/hooks/core.py +452 -0
  44. pulse/hooks/effects.py +88 -0
  45. pulse/hooks/init.py +668 -0
  46. pulse/hooks/runtime.py +464 -0
  47. pulse/hooks/setup.py +254 -0
  48. pulse/hooks/stable.py +138 -0
  49. pulse/hooks/state.py +192 -0
  50. pulse/js/__init__.py +125 -0
  51. pulse/js/__init__.pyi +115 -0
  52. pulse/js/_types.py +299 -0
  53. pulse/js/array.py +339 -0
  54. pulse/js/console.py +50 -0
  55. pulse/js/date.py +119 -0
  56. pulse/js/document.py +145 -0
  57. pulse/js/error.py +140 -0
  58. pulse/js/json.py +66 -0
  59. pulse/js/map.py +97 -0
  60. pulse/js/math.py +69 -0
  61. pulse/js/navigator.py +79 -0
  62. pulse/js/number.py +57 -0
  63. pulse/js/obj.py +81 -0
  64. pulse/js/object.py +172 -0
  65. pulse/js/promise.py +172 -0
  66. pulse/js/pulse.py +115 -0
  67. pulse/js/react.py +495 -0
  68. pulse/js/regexp.py +57 -0
  69. pulse/js/set.py +124 -0
  70. pulse/js/string.py +38 -0
  71. pulse/js/weakmap.py +53 -0
  72. pulse/js/weakset.py +48 -0
  73. pulse/js/window.py +205 -0
  74. pulse/messages.py +202 -0
  75. pulse/middleware.py +471 -0
  76. pulse/plugin.py +96 -0
  77. pulse/proxy.py +242 -0
  78. pulse/py.typed +0 -0
  79. pulse/queries/__init__.py +0 -0
  80. pulse/queries/client.py +609 -0
  81. pulse/queries/common.py +101 -0
  82. pulse/queries/effect.py +55 -0
  83. pulse/queries/infinite_query.py +1418 -0
  84. pulse/queries/mutation.py +295 -0
  85. pulse/queries/protocol.py +136 -0
  86. pulse/queries/query.py +1314 -0
  87. pulse/queries/store.py +120 -0
  88. pulse/react_component.py +88 -0
  89. pulse/reactive.py +1208 -0
  90. pulse/reactive_extensions.py +1172 -0
  91. pulse/render_session.py +768 -0
  92. pulse/renderer.py +584 -0
  93. pulse/request.py +205 -0
  94. pulse/routing.py +598 -0
  95. pulse/serializer.py +279 -0
  96. pulse/state.py +556 -0
  97. pulse/test_helpers.py +15 -0
  98. pulse/transpiler/__init__.py +111 -0
  99. pulse/transpiler/assets.py +81 -0
  100. pulse/transpiler/builtins.py +1029 -0
  101. pulse/transpiler/dynamic_import.py +130 -0
  102. pulse/transpiler/emit_context.py +49 -0
  103. pulse/transpiler/errors.py +96 -0
  104. pulse/transpiler/function.py +611 -0
  105. pulse/transpiler/id.py +18 -0
  106. pulse/transpiler/imports.py +341 -0
  107. pulse/transpiler/js_module.py +336 -0
  108. pulse/transpiler/modules/__init__.py +33 -0
  109. pulse/transpiler/modules/asyncio.py +57 -0
  110. pulse/transpiler/modules/json.py +24 -0
  111. pulse/transpiler/modules/math.py +265 -0
  112. pulse/transpiler/modules/pulse/__init__.py +5 -0
  113. pulse/transpiler/modules/pulse/tags.py +250 -0
  114. pulse/transpiler/modules/typing.py +63 -0
  115. pulse/transpiler/nodes.py +1987 -0
  116. pulse/transpiler/py_module.py +135 -0
  117. pulse/transpiler/transpiler.py +1100 -0
  118. pulse/transpiler/vdom.py +256 -0
  119. pulse/types/__init__.py +0 -0
  120. pulse/types/event_handler.py +50 -0
  121. pulse/user_session.py +386 -0
  122. pulse/version.py +69 -0
  123. pulse_framework-0.1.62.dist-info/METADATA +198 -0
  124. pulse_framework-0.1.62.dist-info/RECORD +126 -0
  125. pulse_framework-0.1.62.dist-info/WHEEL +4 -0
  126. pulse_framework-0.1.62.dist-info/entry_points.txt +3 -0
pulse/js/weakmap.py ADDED
@@ -0,0 +1,53 @@
1
+ """
2
+ JavaScript WeakMap builtin module.
3
+
4
+ Usage:
5
+
6
+ ```python
7
+ from pulse.js import WeakMap
8
+ WeakMap() # -> new WeakMap()
9
+ WeakMap([[obj, "value"]]) # -> new WeakMap([[obj, "value"]])
10
+
11
+ # Or import from module directly:
12
+ from pulse.js.weakmap import WeakMap
13
+ ```
14
+ """
15
+
16
+ from collections.abc import Iterable as _Iterable
17
+ from typing import Generic as _Generic
18
+ from typing import TypeVar as _TypeVar
19
+
20
+ from pulse.transpiler.js_module import JsModule
21
+
22
+ K = _TypeVar("K") # Keys must be objects in JS, but we can't enforce that statically
23
+ V = _TypeVar("V")
24
+
25
+
26
+ class WeakMap(_Generic[K, V]):
27
+ """JavaScript WeakMap - a collection of key/value pairs with weak key references.
28
+
29
+ WeakMap[K, V] holds weak references to keys, allowing garbage collection.
30
+ Keys must be objects (not primitives).
31
+ """
32
+
33
+ def __init__(self, iterable: _Iterable[tuple[K, V]] | None = None, /) -> None: ...
34
+
35
+ def delete(self, key: K) -> bool:
36
+ """Remove a key and its value. Returns True if the key existed."""
37
+ ...
38
+
39
+ def get(self, key: K) -> V | None:
40
+ """Return the value for a key, or None if not present."""
41
+ ...
42
+
43
+ def has(self, key: K) -> bool:
44
+ """Return True if the key exists in the WeakMap."""
45
+ ...
46
+
47
+ def set(self, key: K, value: V) -> "WeakMap[K, V]":
48
+ """Set a key/value pair. Returns the WeakMap for chaining."""
49
+ ...
50
+
51
+
52
+ # Self-register this module as a JS builtin (global identifiers)
53
+ JsModule.register(name=None)
pulse/js/weakset.py ADDED
@@ -0,0 +1,48 @@
1
+ """
2
+ JavaScript WeakSet builtin module.
3
+
4
+ Usage:
5
+
6
+ ```python
7
+ from pulse.js import WeakSet
8
+ WeakSet() # -> new WeakSet()
9
+ WeakSet([obj1, obj2]) # -> new WeakSet([obj1, obj2])
10
+
11
+ # Or import from module directly:
12
+ from pulse.js.weakset import WeakSet
13
+ ```
14
+ """
15
+
16
+ from collections.abc import Iterable as _Iterable
17
+ from typing import Generic as _Generic
18
+ from typing import TypeVar as _TypeVar
19
+
20
+ from pulse.transpiler.js_module import JsModule
21
+
22
+ T = _TypeVar("T") # Values must be objects in JS, but we can't enforce that statically
23
+
24
+
25
+ class WeakSet(_Generic[T]):
26
+ """JavaScript WeakSet - a collection of objects with weak references.
27
+
28
+ WeakSet[T] holds weak references to values, allowing garbage collection.
29
+ Values must be objects (not primitives).
30
+ """
31
+
32
+ def __init__(self, iterable: _Iterable[T] | None = None, /) -> None: ...
33
+
34
+ def add(self, value: T) -> "WeakSet[T]":
35
+ """Add a value to the WeakSet. Returns the WeakSet for chaining."""
36
+ ...
37
+
38
+ def delete(self, value: T) -> bool:
39
+ """Remove a value. Returns True if the value existed."""
40
+ ...
41
+
42
+ def has(self, value: T) -> bool:
43
+ """Return True if the value exists in the WeakSet."""
44
+ ...
45
+
46
+
47
+ # Self-register this module as a JS builtin (global identifiers)
48
+ JsModule.register(name=None)
pulse/js/window.py ADDED
@@ -0,0 +1,205 @@
1
+ """Browser window global object.
2
+
3
+ Usage:
4
+
5
+ ```python
6
+ from pulse.js import window
7
+ window.alert("Hello!") # -> window.alert("Hello!")
8
+ window.innerWidth # -> window.innerWidth
9
+ ```
10
+ """
11
+
12
+ from collections.abc import Callable as _Callable
13
+ from typing import Any as _Any
14
+
15
+ from pulse.js._types import Element as _Element
16
+ from pulse.js._types import Selection as _Selection
17
+ from pulse.transpiler.js_module import JsModule
18
+
19
+ # Dimensions
20
+ innerWidth: int
21
+ innerHeight: int
22
+ outerWidth: int
23
+ outerHeight: int
24
+
25
+ # Scroll position
26
+ scrollX: float
27
+ scrollY: float
28
+ pageXOffset: float # Alias for scrollX
29
+ pageYOffset: float # Alias for scrollY
30
+
31
+ # Screen information
32
+ devicePixelRatio: float
33
+
34
+ # Location and history (typed as Any since they're complex interfaces)
35
+ location: _Any
36
+ history: _Any
37
+ navigator: _Any
38
+ document: _Any
39
+
40
+ # Storage
41
+ localStorage: _Any
42
+ sessionStorage: _Any
43
+
44
+ # Performance
45
+ performance: _Any
46
+
47
+
48
+ # Dialog methods
49
+ def alert(message: str = "", /) -> None:
50
+ """Display an alert dialog with the given message."""
51
+ ...
52
+
53
+
54
+ def confirm(message: str = "", /) -> bool:
55
+ """Display a confirmation dialog. Returns True if user clicks OK."""
56
+ ...
57
+
58
+
59
+ def prompt(message: str = "", default: str = "", /) -> str | None:
60
+ """Display a prompt dialog. Returns input or None if cancelled."""
61
+ ...
62
+
63
+
64
+ # Scroll methods
65
+ def scrollTo(x: float | dict[str, float], y: float | None = None, /) -> None:
66
+ """Scroll to the given position."""
67
+ ...
68
+
69
+
70
+ def scrollBy(x: float | dict[str, float], y: float | None = None, /) -> None:
71
+ """Scroll by the given amount."""
72
+ ...
73
+
74
+
75
+ def scroll(x: float | dict[str, float], y: float | None = None, /) -> None:
76
+ """Alias for scrollTo."""
77
+ ...
78
+
79
+
80
+ # Selection
81
+ def getSelection() -> _Selection | None:
82
+ """Return the current text selection."""
83
+ ...
84
+
85
+
86
+ def getComputedStyle(element: _Element, pseudoElt: str | None = None, /) -> _Any:
87
+ """Return the computed style of an element."""
88
+ ...
89
+
90
+
91
+ # Focus
92
+ def focus() -> None:
93
+ """Give focus to the window."""
94
+ ...
95
+
96
+
97
+ def blur() -> None:
98
+ """Remove focus from the window."""
99
+ ...
100
+
101
+
102
+ # Open/close
103
+ def open(
104
+ url: str = "",
105
+ target: str = "_blank",
106
+ features: str = "",
107
+ /,
108
+ ) -> _Any | None:
109
+ """Open a new window. Returns the new window object or None."""
110
+ ...
111
+
112
+
113
+ def close() -> None:
114
+ """Close the window (only works for windows opened by script)."""
115
+ ...
116
+
117
+
118
+ # Timers (these return timer IDs)
119
+ def setTimeout(handler: _Callable[..., None], timeout: int = 0, /, *args: _Any) -> int:
120
+ """Schedule a function to run after a delay. Returns timer ID."""
121
+ ...
122
+
123
+
124
+ def clearTimeout(timeoutId: int) -> None:
125
+ """Cancel a timeout scheduled with setTimeout."""
126
+ ...
127
+
128
+
129
+ def setInterval(handler: _Callable[..., None], timeout: int = 0, /, *args: _Any) -> int:
130
+ """Schedule a function to run repeatedly. Returns timer ID."""
131
+ ...
132
+
133
+
134
+ def clearInterval(intervalId: int) -> None:
135
+ """Cancel an interval scheduled with setInterval."""
136
+ ...
137
+
138
+
139
+ # Animation
140
+ def requestAnimationFrame(callback: _Callable[[float], None]) -> int:
141
+ """Request a callback before the next repaint. Returns request ID."""
142
+ ...
143
+
144
+
145
+ def cancelAnimationFrame(requestId: int) -> None:
146
+ """Cancel an animation frame request."""
147
+ ...
148
+
149
+
150
+ # Event listeners
151
+ def addEventListener(
152
+ type: str,
153
+ listener: _Callable[..., None],
154
+ options: bool | dict[str, bool] | None = None,
155
+ /,
156
+ ) -> None:
157
+ """Add an event listener to the window."""
158
+ ...
159
+
160
+
161
+ def removeEventListener(
162
+ type: str,
163
+ listener: _Callable[..., None],
164
+ options: bool | dict[str, bool] | None = None,
165
+ /,
166
+ ) -> None:
167
+ """Remove an event listener from the window."""
168
+ ...
169
+
170
+
171
+ def dispatchEvent(event: _Any) -> bool:
172
+ """Dispatch an event to the window."""
173
+ ...
174
+
175
+
176
+ # Encoding
177
+ def atob(encoded: str) -> str:
178
+ """Decode a base64 encoded string."""
179
+ ...
180
+
181
+
182
+ def btoa(data: str) -> str:
183
+ """Encode a string as base64."""
184
+ ...
185
+
186
+
187
+ # Misc
188
+ def matchMedia(query: str) -> _Any:
189
+ """Return a MediaQueryList for the given media query."""
190
+ ...
191
+
192
+
193
+ def print_() -> None:
194
+ """Open the print dialog."""
195
+ ...
196
+
197
+
198
+ def postMessage(
199
+ message: _Any, targetOrigin: str, transfer: list[_Any] | None = None, /
200
+ ) -> None:
201
+ """Post a message to another window."""
202
+ ...
203
+
204
+
205
+ JsModule.register(name="window")
pulse/messages.py ADDED
@@ -0,0 +1,202 @@
1
+ from typing import Any, Literal, NotRequired, TypedDict
2
+
3
+ from pulse.routing import RouteInfo
4
+ from pulse.transpiler.vdom import VDOM, VDOMNode, VDOMOperation
5
+
6
+
7
+ # ====================
8
+ # Server messages
9
+ # ====================
10
+ class ServerInitMessage(TypedDict):
11
+ type: Literal["vdom_init"]
12
+ path: str
13
+ vdom: VDOM
14
+
15
+
16
+ class ServerUpdateMessage(TypedDict):
17
+ type: Literal["vdom_update"]
18
+ path: str
19
+ ops: list[VDOMOperation]
20
+
21
+
22
+ ServerErrorPhase = Literal[
23
+ "render", "callback", "mount", "unmount", "navigate", "server", "effect", "connect"
24
+ ]
25
+
26
+
27
+ class ServerErrorInfo(TypedDict, total=False):
28
+ # High-level human message
29
+ message: str
30
+ # Full stack trace string (server formatted)
31
+ stack: str
32
+ # Which phase failed
33
+ phase: ServerErrorPhase
34
+ # Optional extra details (callback key, etc.)
35
+ details: dict[str, Any]
36
+
37
+
38
+ class ServerErrorMessage(TypedDict):
39
+ type: Literal["server_error"]
40
+ path: str
41
+ error: ServerErrorInfo
42
+
43
+
44
+ class ServerNavigateToMessage(TypedDict):
45
+ type: Literal["navigate_to"]
46
+ path: str
47
+ replace: bool
48
+ hard: bool
49
+
50
+
51
+ class ServerReloadMessage(TypedDict):
52
+ type: Literal["reload"]
53
+
54
+
55
+ class ServerApiCallMessage(TypedDict):
56
+ type: Literal["api_call"]
57
+ # Correlation id to match request/response
58
+ id: str
59
+ url: str
60
+ method: str
61
+ headers: dict[str, str]
62
+ # Body can be JSON-serializable or None
63
+ body: Any | None
64
+ # Whether to include credentials (cookies)
65
+ credentials: Literal["include", "omit"]
66
+
67
+
68
+ class ServerChannelRequestMessage(TypedDict):
69
+ type: Literal["channel_message"]
70
+ channel: str
71
+ event: str
72
+ payload: Any
73
+ requestId: NotRequired[str]
74
+ error: NotRequired[Any]
75
+
76
+
77
+ class ServerChannelResponseMessage(TypedDict):
78
+ type: Literal["channel_message"]
79
+ channel: str
80
+ event: None
81
+ responseTo: str
82
+ payload: Any
83
+ error: NotRequired[Any]
84
+
85
+
86
+ class ServerJsExecMessage(TypedDict):
87
+ """Execute JavaScript expression on the client."""
88
+
89
+ type: Literal["js_exec"]
90
+ path: str
91
+ id: str
92
+ expr: VDOMNode
93
+
94
+
95
+ # ====================
96
+ # Client messages
97
+ # ====================
98
+ class ClientCallbackMessage(TypedDict):
99
+ type: Literal["callback"]
100
+ path: str
101
+ callback: str
102
+ args: list[Any]
103
+
104
+
105
+ class ClientAttachMessage(TypedDict):
106
+ type: Literal["attach"]
107
+ path: str
108
+ routeInfo: RouteInfo
109
+
110
+
111
+ class ClientUpdateMessage(TypedDict):
112
+ type: Literal["update"]
113
+ path: str
114
+ routeInfo: RouteInfo
115
+
116
+
117
+ class ClientDetachMessage(TypedDict):
118
+ type: Literal["detach"]
119
+ path: str
120
+
121
+
122
+ class ClientApiResultMessage(TypedDict):
123
+ type: Literal["api_result"]
124
+ id: str
125
+ ok: bool
126
+ status: int
127
+ headers: dict[str, str]
128
+ body: Any | None
129
+
130
+
131
+ class ClientChannelRequestMessage(TypedDict):
132
+ type: Literal["channel_message"]
133
+ channel: str
134
+ event: str
135
+ payload: Any
136
+ requestId: NotRequired[str]
137
+ error: NotRequired[Any]
138
+
139
+
140
+ class ClientChannelResponseMessage(TypedDict):
141
+ type: Literal["channel_message"]
142
+ channel: str
143
+ event: None
144
+ responseTo: str
145
+ payload: Any
146
+ error: NotRequired[Any]
147
+
148
+
149
+ class ClientJsResultMessage(TypedDict):
150
+ """Result of client-side JS execution."""
151
+
152
+ type: Literal["js_result"]
153
+ id: str
154
+ result: Any
155
+ error: str | None
156
+
157
+
158
+ ServerChannelMessage = ServerChannelRequestMessage | ServerChannelResponseMessage
159
+ ServerMessage = (
160
+ ServerInitMessage
161
+ | ServerUpdateMessage
162
+ | ServerErrorMessage
163
+ | ServerApiCallMessage
164
+ | ServerNavigateToMessage
165
+ | ServerReloadMessage
166
+ | ServerChannelMessage
167
+ | ServerJsExecMessage
168
+ )
169
+
170
+
171
+ ClientPulseMessage = (
172
+ ClientCallbackMessage
173
+ | ClientAttachMessage
174
+ | ClientUpdateMessage
175
+ | ClientDetachMessage
176
+ | ClientApiResultMessage
177
+ | ClientJsResultMessage
178
+ )
179
+ ClientChannelMessage = ClientChannelRequestMessage | ClientChannelResponseMessage
180
+ ClientMessage = ClientPulseMessage | ClientChannelMessage
181
+
182
+
183
+ class PrerenderPayload(TypedDict):
184
+ paths: list[str]
185
+ routeInfo: RouteInfo
186
+ ttlSeconds: NotRequired[float | int]
187
+ renderId: NotRequired[str]
188
+
189
+
190
+ class SocketIODirectives(TypedDict):
191
+ headers: dict[str, str]
192
+ auth: dict[str, str]
193
+
194
+
195
+ class Directives(TypedDict):
196
+ headers: dict[str, str]
197
+ socketio: SocketIODirectives
198
+
199
+
200
+ class Prerender(TypedDict):
201
+ views: dict[str, ServerInitMessage | ServerNavigateToMessage | None]
202
+ directives: Directives