pulse-framework 0.1.54__py3-none-any.whl → 0.1.56__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 +5 -6
- pulse/app.py +144 -57
- pulse/channel.py +139 -7
- pulse/cli/cmd.py +16 -2
- pulse/code_analysis.py +38 -0
- pulse/codegen/codegen.py +61 -62
- pulse/codegen/templates/route.py +100 -56
- pulse/component.py +128 -6
- pulse/components/for_.py +30 -4
- pulse/components/if_.py +28 -5
- pulse/components/react_router.py +61 -3
- pulse/context.py +39 -5
- pulse/cookies.py +108 -4
- pulse/decorators.py +193 -24
- pulse/env.py +56 -2
- pulse/form.py +198 -5
- pulse/helpers.py +7 -1
- pulse/hooks/core.py +135 -5
- pulse/hooks/effects.py +61 -77
- pulse/hooks/init.py +60 -1
- pulse/hooks/runtime.py +241 -0
- pulse/hooks/setup.py +77 -0
- pulse/hooks/stable.py +58 -1
- pulse/hooks/state.py +107 -20
- pulse/js/__init__.py +41 -25
- pulse/js/array.py +9 -6
- pulse/js/console.py +15 -12
- pulse/js/date.py +9 -6
- pulse/js/document.py +5 -2
- pulse/js/error.py +7 -4
- pulse/js/json.py +9 -6
- pulse/js/map.py +8 -5
- pulse/js/math.py +9 -6
- pulse/js/navigator.py +5 -2
- pulse/js/number.py +9 -6
- pulse/js/obj.py +16 -13
- pulse/js/object.py +9 -6
- pulse/js/promise.py +19 -13
- pulse/js/pulse.py +28 -25
- pulse/js/react.py +190 -44
- pulse/js/regexp.py +7 -4
- pulse/js/set.py +8 -5
- pulse/js/string.py +9 -6
- pulse/js/weakmap.py +8 -5
- pulse/js/weakset.py +8 -5
- pulse/js/window.py +6 -3
- pulse/messages.py +5 -0
- pulse/middleware.py +147 -76
- pulse/plugin.py +76 -5
- pulse/queries/client.py +186 -39
- pulse/queries/common.py +52 -3
- pulse/queries/infinite_query.py +154 -2
- pulse/queries/mutation.py +127 -7
- pulse/queries/query.py +112 -11
- pulse/react_component.py +66 -3
- pulse/reactive.py +314 -30
- pulse/reactive_extensions.py +106 -26
- pulse/render_session.py +304 -173
- pulse/request.py +46 -11
- pulse/routing.py +140 -4
- pulse/serializer.py +71 -0
- pulse/state.py +177 -9
- pulse/test_helpers.py +15 -0
- pulse/transpiler/__init__.py +13 -3
- pulse/transpiler/assets.py +66 -0
- pulse/transpiler/dynamic_import.py +131 -0
- pulse/transpiler/emit_context.py +49 -0
- pulse/transpiler/function.py +6 -2
- pulse/transpiler/imports.py +33 -27
- pulse/transpiler/js_module.py +64 -8
- pulse/transpiler/py_module.py +1 -7
- pulse/transpiler/transpiler.py +4 -0
- pulse/user_session.py +119 -18
- {pulse_framework-0.1.54.dist-info → pulse_framework-0.1.56.dist-info}/METADATA +5 -5
- pulse_framework-0.1.56.dist-info/RECORD +127 -0
- pulse/js/react_dom.py +0 -30
- pulse/transpiler/react_component.py +0 -51
- pulse_framework-0.1.54.dist-info/RECORD +0 -124
- {pulse_framework-0.1.54.dist-info → pulse_framework-0.1.56.dist-info}/WHEEL +0 -0
- {pulse_framework-0.1.54.dist-info → pulse_framework-0.1.56.dist-info}/entry_points.txt +0 -0
pulse/hooks/stable.py
CHANGED
|
@@ -8,6 +8,17 @@ TCallable = TypeVar("TCallable", bound=Callable[..., Any])
|
|
|
8
8
|
|
|
9
9
|
|
|
10
10
|
class StableEntry:
|
|
11
|
+
"""Container for a stable value and its wrapper function.
|
|
12
|
+
|
|
13
|
+
Holds a value and a wrapper function that always delegates to the
|
|
14
|
+
current value, allowing the wrapper reference to remain stable while
|
|
15
|
+
the underlying value can change.
|
|
16
|
+
|
|
17
|
+
Attributes:
|
|
18
|
+
value: The current wrapped value.
|
|
19
|
+
wrapper: Stable function that delegates to the current value.
|
|
20
|
+
"""
|
|
21
|
+
|
|
11
22
|
__slots__ = ("value", "wrapper") # pyright: ignore[reportUnannotatedClassAttribute]
|
|
12
23
|
value: Any
|
|
13
24
|
wrapper: Callable[..., Any]
|
|
@@ -25,6 +36,13 @@ class StableEntry:
|
|
|
25
36
|
|
|
26
37
|
|
|
27
38
|
class StableRegistry(HookState):
|
|
39
|
+
"""Internal hook state that stores stable entries by key.
|
|
40
|
+
|
|
41
|
+
Maintains a dictionary of StableEntry objects, allowing stable
|
|
42
|
+
wrappers to persist across renders while their underlying values
|
|
43
|
+
can be updated.
|
|
44
|
+
"""
|
|
45
|
+
|
|
28
46
|
__slots__ = ("entries",) # pyright: ignore[reportUnannotatedClassAttribute]
|
|
29
47
|
|
|
30
48
|
def __init__(self) -> None:
|
|
@@ -58,7 +76,46 @@ def stable(key: str, value: TCallable) -> TCallable: ...
|
|
|
58
76
|
def stable(key: str, value: T) -> Callable[[], T]: ...
|
|
59
77
|
|
|
60
78
|
|
|
61
|
-
def stable(key: str, value: Any = MISSING):
|
|
79
|
+
def stable(key: str, value: Any = MISSING) -> Any:
|
|
80
|
+
"""Return a stable wrapper that always calls the latest value.
|
|
81
|
+
|
|
82
|
+
Creates a wrapper function that maintains a stable reference across renders
|
|
83
|
+
while delegating to the current value. Useful for event handlers and callbacks
|
|
84
|
+
that need to stay referentially stable.
|
|
85
|
+
|
|
86
|
+
Args:
|
|
87
|
+
key: Unique identifier for this stable value within the component.
|
|
88
|
+
value: Optional value or callable to wrap. If provided, updates the
|
|
89
|
+
stored value and returns the wrapper. If omitted, returns the
|
|
90
|
+
existing wrapper for the key.
|
|
91
|
+
|
|
92
|
+
Returns:
|
|
93
|
+
A stable wrapper function that delegates to the current value. If the
|
|
94
|
+
value is callable, the wrapper calls it with any provided arguments.
|
|
95
|
+
If not callable, the wrapper returns the value directly.
|
|
96
|
+
|
|
97
|
+
Raises:
|
|
98
|
+
ValueError: If key is empty.
|
|
99
|
+
KeyError: If value is not provided and no entry exists for the key.
|
|
100
|
+
|
|
101
|
+
Example:
|
|
102
|
+
|
|
103
|
+
```python
|
|
104
|
+
def my_component():
|
|
105
|
+
s = ps.state("data", lambda: DataState())
|
|
106
|
+
|
|
107
|
+
# Without stable, this would create a new function each render
|
|
108
|
+
handle_click = ps.stable("click", lambda: s.increment())
|
|
109
|
+
|
|
110
|
+
return m.Button("Click", on_click=handle_click)
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
Use Cases:
|
|
114
|
+
- Event handlers passed to child components to prevent unnecessary re-renders
|
|
115
|
+
- Callbacks registered with external systems
|
|
116
|
+
- Any function reference that needs to stay stable across renders
|
|
117
|
+
) -> Any:
|
|
118
|
+
"""
|
|
62
119
|
if not key:
|
|
63
120
|
raise ValueError("stable() requires a non-empty string key")
|
|
64
121
|
|
pulse/hooks/state.py
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
|
+
import inspect
|
|
1
2
|
from collections.abc import Callable
|
|
2
|
-
from
|
|
3
|
+
from types import CodeType, FrameType
|
|
4
|
+
from typing import Any, TypeVar, override
|
|
3
5
|
|
|
6
|
+
from pulse.component import is_component_code
|
|
4
7
|
from pulse.hooks.core import HookMetadata, HookState, hooks
|
|
5
8
|
from pulse.state import State
|
|
6
9
|
|
|
@@ -8,42 +11,71 @@ S = TypeVar("S", bound=State)
|
|
|
8
11
|
|
|
9
12
|
|
|
10
13
|
class StateHookState(HookState):
|
|
14
|
+
"""Internal hook state for managing State instances across renders.
|
|
15
|
+
|
|
16
|
+
Stores State instances keyed by string identifier and tracks which keys
|
|
17
|
+
have been accessed during the current render cycle.
|
|
18
|
+
"""
|
|
19
|
+
|
|
11
20
|
__slots__ = ("instances", "called_keys") # pyright: ignore[reportUnannotatedClassAttribute]
|
|
12
|
-
instances: dict[str, State]
|
|
13
|
-
called_keys: set[str]
|
|
21
|
+
instances: dict[tuple[str, Any], State]
|
|
22
|
+
called_keys: set[tuple[str, Any]]
|
|
14
23
|
|
|
15
24
|
def __init__(self) -> None:
|
|
16
25
|
super().__init__()
|
|
17
26
|
self.instances = {}
|
|
18
27
|
self.called_keys = set()
|
|
19
28
|
|
|
29
|
+
def _make_key(self, identity: Any, key: str | None) -> tuple[str, Any]:
|
|
30
|
+
if key is None:
|
|
31
|
+
return ("code", identity)
|
|
32
|
+
return ("key", key)
|
|
33
|
+
|
|
20
34
|
@override
|
|
21
35
|
def on_render_start(self, render_cycle: int) -> None:
|
|
22
36
|
super().on_render_start(render_cycle)
|
|
23
37
|
self.called_keys.clear()
|
|
24
38
|
|
|
25
|
-
def get_or_create_state(
|
|
26
|
-
|
|
39
|
+
def get_or_create_state(
|
|
40
|
+
self,
|
|
41
|
+
identity: Any,
|
|
42
|
+
key: str | None,
|
|
43
|
+
arg: State | Callable[[], State],
|
|
44
|
+
) -> State:
|
|
45
|
+
full_identity = self._make_key(identity, key)
|
|
46
|
+
if full_identity in self.called_keys:
|
|
47
|
+
if key is None:
|
|
48
|
+
raise RuntimeError(
|
|
49
|
+
"`pulse.state` can only be called once per component render at the same location. "
|
|
50
|
+
+ "Use the `key` parameter to disambiguate: ps.state(..., key=unique_value)"
|
|
51
|
+
)
|
|
27
52
|
raise RuntimeError(
|
|
28
53
|
f"`pulse.state` can only be called once per component render with key='{key}'"
|
|
29
54
|
)
|
|
30
|
-
self.called_keys.add(
|
|
55
|
+
self.called_keys.add(full_identity)
|
|
31
56
|
|
|
32
|
-
existing = self.instances.get(
|
|
57
|
+
existing = self.instances.get(full_identity)
|
|
33
58
|
if existing is not None:
|
|
34
59
|
# Dispose any State instances passed directly as args that aren't being used
|
|
35
60
|
if isinstance(arg, State) and arg is not existing:
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
61
|
+
arg.dispose()
|
|
62
|
+
if existing.__disposed__:
|
|
63
|
+
key_label = f"key='{key}'" if key is not None else "callsite"
|
|
64
|
+
raise RuntimeError(
|
|
65
|
+
"`pulse.state` found a disposed cached State for "
|
|
66
|
+
+ key_label
|
|
67
|
+
+ ". Do not dispose states returned by `pulse.state`."
|
|
68
|
+
)
|
|
42
69
|
return existing
|
|
43
70
|
|
|
44
71
|
# Create new state
|
|
45
72
|
instance = _instantiate_state(arg)
|
|
46
|
-
|
|
73
|
+
if instance.__disposed__:
|
|
74
|
+
raise RuntimeError(
|
|
75
|
+
"`pulse.state` received a disposed State instance. "
|
|
76
|
+
+ "Do not dispose states passed to `pulse.state`."
|
|
77
|
+
)
|
|
78
|
+
self.instances[full_identity] = instance
|
|
47
79
|
return instance
|
|
48
80
|
|
|
49
81
|
@override
|
|
@@ -71,6 +103,26 @@ def _state_factory():
|
|
|
71
103
|
return StateHookState()
|
|
72
104
|
|
|
73
105
|
|
|
106
|
+
def _frame_offset(frame: FrameType) -> int:
|
|
107
|
+
offset = frame.f_lasti
|
|
108
|
+
if offset < 0:
|
|
109
|
+
offset = frame.f_lineno
|
|
110
|
+
return offset
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def collect_component_identity(
|
|
114
|
+
frame: FrameType,
|
|
115
|
+
) -> tuple[tuple[CodeType, int], ...]:
|
|
116
|
+
identity: list[tuple[CodeType, int]] = []
|
|
117
|
+
cursor: FrameType | None = frame
|
|
118
|
+
while cursor is not None:
|
|
119
|
+
identity.append((cursor.f_code, _frame_offset(cursor)))
|
|
120
|
+
if is_component_code(cursor.f_code):
|
|
121
|
+
return tuple(identity)
|
|
122
|
+
cursor = cursor.f_back
|
|
123
|
+
return tuple(identity[:1])
|
|
124
|
+
|
|
125
|
+
|
|
74
126
|
_state_hook = hooks.create(
|
|
75
127
|
"pulse:core.state",
|
|
76
128
|
_state_factory,
|
|
@@ -81,25 +133,60 @@ _state_hook = hooks.create(
|
|
|
81
133
|
)
|
|
82
134
|
|
|
83
135
|
|
|
84
|
-
def state(
|
|
85
|
-
|
|
136
|
+
def state(
|
|
137
|
+
arg: S | Callable[[], S],
|
|
138
|
+
*,
|
|
139
|
+
key: str | None = None,
|
|
140
|
+
) -> S:
|
|
141
|
+
"""Get or create a state instance associated with a key or callsite.
|
|
86
142
|
|
|
87
143
|
Args:
|
|
88
|
-
key: A unique string key identifying this state within the component.
|
|
89
144
|
arg: A State instance or a callable that returns a State instance.
|
|
145
|
+
key: Optional key to disambiguate multiple calls from the same location.
|
|
90
146
|
|
|
91
147
|
Returns:
|
|
92
|
-
The
|
|
148
|
+
The same State instance on subsequent renders with the same key.
|
|
93
149
|
|
|
94
150
|
Raises:
|
|
95
151
|
ValueError: If key is empty.
|
|
96
152
|
RuntimeError: If called more than once per render with the same key.
|
|
97
153
|
TypeError: If arg is not a State or callable returning a State.
|
|
154
|
+
|
|
155
|
+
Example:
|
|
156
|
+
|
|
157
|
+
```python
|
|
158
|
+
def counter():
|
|
159
|
+
s = ps.state("counter", lambda: CounterState())
|
|
160
|
+
return m.Button(f"Count: {s.count}", on_click=lambda: s.increment())
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
Notes:
|
|
164
|
+
- Key must be non-empty string
|
|
165
|
+
- Can only be called once per render with the same key
|
|
166
|
+
- Factory is only called on first render; subsequent renders return cached instance
|
|
167
|
+
- State is disposed when component unmounts
|
|
98
168
|
"""
|
|
99
|
-
if not key:
|
|
169
|
+
if key is not None and not isinstance(key, str):
|
|
170
|
+
raise TypeError("state() key must be a string")
|
|
171
|
+
|
|
172
|
+
if key == "":
|
|
100
173
|
raise ValueError("state() requires a non-empty string key")
|
|
174
|
+
|
|
175
|
+
resolved_key = key
|
|
176
|
+
resolved_arg = arg
|
|
177
|
+
|
|
178
|
+
identity: Any
|
|
179
|
+
if resolved_key is None:
|
|
180
|
+
frame = inspect.currentframe()
|
|
181
|
+
assert frame is not None
|
|
182
|
+
caller = frame.f_back
|
|
183
|
+
assert caller is not None
|
|
184
|
+
identity = collect_component_identity(caller)
|
|
185
|
+
else:
|
|
186
|
+
identity = resolved_key
|
|
187
|
+
|
|
101
188
|
hook_state = _state_hook()
|
|
102
|
-
return hook_state.get_or_create_state(
|
|
189
|
+
return hook_state.get_or_create_state(identity, resolved_key, resolved_arg) # pyright: ignore[reportReturnType]
|
|
103
190
|
|
|
104
191
|
|
|
105
192
|
__all__ = ["state", "StateHookState"]
|
pulse/js/__init__.py
CHANGED
|
@@ -1,29 +1,45 @@
|
|
|
1
1
|
"""JavaScript module bindings for use in @javascript decorated functions (transpiler).
|
|
2
2
|
|
|
3
|
-
Usage
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
#
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
3
|
+
## Usage
|
|
4
|
+
|
|
5
|
+
Import JS classes (for constructors and static methods):
|
|
6
|
+
|
|
7
|
+
```python
|
|
8
|
+
from pulse.js import Set, Number, Array, Date, Promise, Map, Error
|
|
9
|
+
Set([1, 2, 3]) # -> new Set([1, 2, 3])
|
|
10
|
+
Number.isFinite(42) # -> Number.isFinite(42)
|
|
11
|
+
Array.isArray(x) # -> Array.isArray(x)
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
Import JS namespace objects (function-only modules):
|
|
15
|
+
|
|
16
|
+
```python
|
|
17
|
+
from pulse.js import Math, JSON, console, window, document, navigator
|
|
18
|
+
Math.floor(3.7) # -> Math.floor(3.7)
|
|
19
|
+
JSON.stringify(obj) # -> JSON.stringify(obj)
|
|
20
|
+
console.log("hi") # -> console.log("hi")
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Alternative: import namespace modules for namespace access:
|
|
24
|
+
|
|
25
|
+
```python
|
|
26
|
+
import pulse.js.json as JSON
|
|
27
|
+
JSON.stringify(obj) # -> JSON.stringify(obj)
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Statement functions:
|
|
31
|
+
|
|
32
|
+
```python
|
|
33
|
+
from pulse.js import throw
|
|
34
|
+
throw(Error("message")) # -> throw Error("message");
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Object literals (plain JS objects instead of Map):
|
|
38
|
+
|
|
39
|
+
```python
|
|
40
|
+
from pulse.js import obj
|
|
41
|
+
obj(a=1, b=2) # -> { a: 1, b: 2 }
|
|
42
|
+
```
|
|
27
43
|
"""
|
|
28
44
|
|
|
29
45
|
import importlib as _importlib
|
|
@@ -39,7 +55,6 @@ _MODULE_EXPORTS_NAMESPACE: dict[str, str] = {
|
|
|
39
55
|
"JSON": "pulse.js.json",
|
|
40
56
|
"Math": "pulse.js.math",
|
|
41
57
|
"React": "pulse.js.react",
|
|
42
|
-
"ReactDOM": "pulse.js.react_dom",
|
|
43
58
|
"console": "pulse.js.console",
|
|
44
59
|
"window": "pulse.js.window",
|
|
45
60
|
"document": "pulse.js.document",
|
|
@@ -54,6 +69,7 @@ _MODULE_EXPORTS_ATTRIBUTE: dict[str, str] = {
|
|
|
54
69
|
"Map": "pulse.js.map",
|
|
55
70
|
"Object": "pulse.js.object",
|
|
56
71
|
"Promise": "pulse.js.promise",
|
|
72
|
+
"React": "pulse.js.react",
|
|
57
73
|
"RegExp": "pulse.js.regexp",
|
|
58
74
|
"Set": "pulse.js.set",
|
|
59
75
|
"String": "pulse.js.string",
|
pulse/js/array.py
CHANGED
|
@@ -2,13 +2,16 @@
|
|
|
2
2
|
JavaScript Array builtin module.
|
|
3
3
|
|
|
4
4
|
Usage:
|
|
5
|
-
from pulse.js import Array
|
|
6
|
-
Array.isArray([1, 2, 3]) # -> Array.isArray([1, 2, 3])
|
|
7
|
-
Array.from_([1, 2, 3]) # -> Array.from([1, 2, 3])
|
|
8
|
-
Array(10) # -> new Array(10)
|
|
9
5
|
|
|
10
|
-
|
|
11
|
-
|
|
6
|
+
```python
|
|
7
|
+
from pulse.js import Array
|
|
8
|
+
Array.isArray([1, 2, 3]) # -> Array.isArray([1, 2, 3])
|
|
9
|
+
Array.from_([1, 2, 3]) # -> Array.from([1, 2, 3])
|
|
10
|
+
Array(10) # -> new Array(10)
|
|
11
|
+
|
|
12
|
+
# Or import from module directly:
|
|
13
|
+
from pulse.js.array import Array
|
|
14
|
+
```
|
|
12
15
|
"""
|
|
13
16
|
|
|
14
17
|
from __future__ import annotations
|
pulse/js/console.py
CHANGED
|
@@ -2,18 +2,21 @@
|
|
|
2
2
|
JavaScript Console builtin module.
|
|
3
3
|
|
|
4
4
|
Usage:
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
5
|
+
|
|
6
|
+
```python
|
|
7
|
+
import pulse.js.console as console
|
|
8
|
+
console.log("Hello") # -> console.log("Hello")
|
|
9
|
+
console.error("Error") # -> console.error("Error")
|
|
10
|
+
console.assert(True, "msg") # -> console.assert(True, "msg")
|
|
11
|
+
|
|
12
|
+
# Note: For 'assert' (Python keyword), use namespace import:
|
|
13
|
+
# import pulse.js.console as console; console.assert(...)
|
|
14
|
+
# Or use the underscore version for direct import:
|
|
15
|
+
from pulse.js.console import log, error, warn, info, debug, assert_
|
|
16
|
+
log("Hello") # -> console.log("Hello")
|
|
17
|
+
error("Error") # -> console.error("Error")
|
|
18
|
+
assert_(True, "msg") # -> console.assert(True, "msg")
|
|
19
|
+
```
|
|
17
20
|
"""
|
|
18
21
|
|
|
19
22
|
from typing import Any as _Any
|
pulse/js/date.py
CHANGED
|
@@ -2,13 +2,16 @@
|
|
|
2
2
|
JavaScript Date builtin module.
|
|
3
3
|
|
|
4
4
|
Usage:
|
|
5
|
-
from pulse.js import Date
|
|
6
|
-
Date() # -> new Date()
|
|
7
|
-
Date.now() # -> Date.now()
|
|
8
|
-
Date.parse("2023-01-01") # -> Date.parse("2023-01-01")
|
|
9
5
|
|
|
10
|
-
|
|
11
|
-
|
|
6
|
+
```python
|
|
7
|
+
from pulse.js import Date
|
|
8
|
+
Date() # -> new Date()
|
|
9
|
+
Date.now() # -> Date.now()
|
|
10
|
+
Date.parse("2023-01-01") # -> Date.parse("2023-01-01")
|
|
11
|
+
|
|
12
|
+
# Or import from module directly:
|
|
13
|
+
from pulse.js.date import Date
|
|
14
|
+
```
|
|
12
15
|
"""
|
|
13
16
|
|
|
14
17
|
from typing import Any as _Any
|
pulse/js/document.py
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
"""Browser document global object.
|
|
2
2
|
|
|
3
3
|
Usage:
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
|
|
5
|
+
```python
|
|
6
|
+
from pulse.js import document
|
|
7
|
+
document.querySelector("#app") # -> document.querySelector("#app")
|
|
8
|
+
```
|
|
6
9
|
"""
|
|
7
10
|
|
|
8
11
|
from collections.abc import Callable as _Callable
|
pulse/js/error.py
CHANGED
|
@@ -2,11 +2,14 @@
|
|
|
2
2
|
JavaScript Error builtin module.
|
|
3
3
|
|
|
4
4
|
Usage:
|
|
5
|
-
from pulse.js import Error
|
|
6
|
-
Error("message") # -> new Error("message")
|
|
7
5
|
|
|
8
|
-
|
|
9
|
-
|
|
6
|
+
```python
|
|
7
|
+
from pulse.js import Error
|
|
8
|
+
Error("message") # -> new Error("message")
|
|
9
|
+
|
|
10
|
+
from pulse.js.error import TypeError, RangeError, ReferenceError
|
|
11
|
+
TypeError("message") # -> new TypeError("message")
|
|
12
|
+
```
|
|
10
13
|
"""
|
|
11
14
|
|
|
12
15
|
from pulse.transpiler.js_module import JsModule
|
pulse/js/json.py
CHANGED
|
@@ -2,13 +2,16 @@
|
|
|
2
2
|
JavaScript JSON builtin module.
|
|
3
3
|
|
|
4
4
|
Usage:
|
|
5
|
-
import pulse.js.json as JSON
|
|
6
|
-
JSON.stringify({"a": 1}) # -> JSON.stringify({"a": 1})
|
|
7
|
-
JSON.parse('{"a": 1}') # -> JSON.parse('{"a": 1}')
|
|
8
5
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
6
|
+
```python
|
|
7
|
+
import pulse.js.json as JSON
|
|
8
|
+
JSON.stringify({"a": 1}) # -> JSON.stringify({"a": 1})
|
|
9
|
+
JSON.parse('{"a": 1}') # -> JSON.parse('{"a": 1}')
|
|
10
|
+
|
|
11
|
+
from pulse.js.json import stringify, parse
|
|
12
|
+
stringify({"a": 1}) # -> JSON.stringify({"a": 1})
|
|
13
|
+
parse('{"a": 1}') # -> JSON.parse('{"a": 1}')
|
|
14
|
+
```
|
|
12
15
|
"""
|
|
13
16
|
|
|
14
17
|
from collections.abc import Callable as _Callable
|
pulse/js/map.py
CHANGED
|
@@ -2,12 +2,15 @@
|
|
|
2
2
|
JavaScript Map builtin module.
|
|
3
3
|
|
|
4
4
|
Usage:
|
|
5
|
-
from pulse.js import Map
|
|
6
|
-
Map() # -> new Map()
|
|
7
|
-
Map([["a", 1]]) # -> new Map([["a", 1]])
|
|
8
5
|
|
|
9
|
-
|
|
10
|
-
|
|
6
|
+
```python
|
|
7
|
+
from pulse.js import Map
|
|
8
|
+
Map() # -> new Map()
|
|
9
|
+
Map([["a", 1]]) # -> new Map([["a", 1]])
|
|
10
|
+
|
|
11
|
+
# Or import from module directly:
|
|
12
|
+
from pulse.js.map import Map
|
|
13
|
+
```
|
|
11
14
|
"""
|
|
12
15
|
|
|
13
16
|
from collections.abc import Callable as _Callable
|
pulse/js/math.py
CHANGED
|
@@ -2,13 +2,16 @@
|
|
|
2
2
|
JavaScript Math builtin module.
|
|
3
3
|
|
|
4
4
|
Usage:
|
|
5
|
-
import pulse.js.math as Math
|
|
6
|
-
Math.PI # -> Math.PI
|
|
7
|
-
Math.floor(3.7) # -> Math.floor(3.7)
|
|
8
5
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
6
|
+
```python
|
|
7
|
+
import pulse.js.math as Math
|
|
8
|
+
Math.PI # -> Math.PI
|
|
9
|
+
Math.floor(3.7) # -> Math.floor(3.7)
|
|
10
|
+
|
|
11
|
+
from pulse.js.math import PI, floor
|
|
12
|
+
PI # -> Math.PI
|
|
13
|
+
floor(3.7) # -> Math.floor(3.7)
|
|
14
|
+
```
|
|
12
15
|
"""
|
|
13
16
|
|
|
14
17
|
from pulse.transpiler.js_module import JsModule
|
pulse/js/navigator.py
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
"""Browser navigator global object.
|
|
2
2
|
|
|
3
3
|
Usage:
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
|
|
5
|
+
```python
|
|
6
|
+
from pulse.js import navigator, console
|
|
7
|
+
console.log(navigator.userAgent)
|
|
8
|
+
```
|
|
6
9
|
"""
|
|
7
10
|
|
|
8
11
|
from typing import Any as _Any
|
pulse/js/number.py
CHANGED
|
@@ -2,13 +2,16 @@
|
|
|
2
2
|
JavaScript Number builtin module.
|
|
3
3
|
|
|
4
4
|
Usage:
|
|
5
|
-
from pulse.js import Number
|
|
6
|
-
Number.isFinite(42) # -> Number.isFinite(42)
|
|
7
|
-
Number.MAX_SAFE_INTEGER # -> Number.MAX_SAFE_INTEGER
|
|
8
|
-
Number(x) # -> new Number(x)
|
|
9
5
|
|
|
10
|
-
|
|
11
|
-
|
|
6
|
+
```python
|
|
7
|
+
from pulse.js import Number
|
|
8
|
+
Number.isFinite(42) # -> Number.isFinite(42)
|
|
9
|
+
Number.MAX_SAFE_INTEGER # -> Number.MAX_SAFE_INTEGER
|
|
10
|
+
Number(x) # -> new Number(x)
|
|
11
|
+
|
|
12
|
+
# Or import from module directly:
|
|
13
|
+
from pulse.js.number import Number
|
|
14
|
+
```
|
|
12
15
|
"""
|
|
13
16
|
|
|
14
17
|
from typing import Any as _Any
|
pulse/js/obj.py
CHANGED
|
@@ -2,19 +2,22 @@
|
|
|
2
2
|
JavaScript object literal creation.
|
|
3
3
|
|
|
4
4
|
Usage:
|
|
5
|
-
from pulse.js import obj
|
|
6
5
|
|
|
7
|
-
|
|
8
|
-
|
|
6
|
+
```python
|
|
7
|
+
from pulse.js import obj
|
|
9
8
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
obj(a=1, **base) # -> { a: 1, ...base }
|
|
9
|
+
# Create plain JS objects (not Maps):
|
|
10
|
+
obj(a=1, b=2) # -> { a: 1, b: 2 }
|
|
13
11
|
|
|
14
|
-
|
|
15
|
-
|
|
12
|
+
# With spread syntax:
|
|
13
|
+
obj(**base, c=3) # -> { ...base, c: 3 }
|
|
14
|
+
obj(a=1, **base) # -> { a: 1, ...base }
|
|
16
15
|
|
|
17
|
-
|
|
16
|
+
# Empty object:
|
|
17
|
+
obj() # -> {}
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Unlike `dict()` which transpiles to `new Map()`, `obj()` creates plain JavaScript
|
|
18
21
|
object literals. Use this for React props, style objects, and anywhere you
|
|
19
22
|
need a plain JS object.
|
|
20
23
|
"""
|
|
@@ -36,13 +39,13 @@ if TYPE_CHECKING:
|
|
|
36
39
|
|
|
37
40
|
@dataclass(slots=True)
|
|
38
41
|
class ObjTransformer(Expr):
|
|
39
|
-
"""Transformer for obj() with
|
|
42
|
+
"""Transformer for `obj()` with `**spread` support.
|
|
40
43
|
|
|
41
|
-
obj(key=value, ...) -> { key: value, ... }
|
|
42
|
-
obj(**base, key=value) -> { ...base, key: value }
|
|
44
|
+
- `obj(key=value, ...)` -> `{ key: value, ... }`
|
|
45
|
+
- `obj(**base, key=value)` -> `{ ...base, key: value }`
|
|
43
46
|
|
|
44
47
|
Creates a plain JavaScript object literal.
|
|
45
|
-
Use this instead of dict() when you need a plain object (e.g., for React props).
|
|
48
|
+
Use this instead of `dict()` when you need a plain object (e.g., for React props).
|
|
46
49
|
"""
|
|
47
50
|
|
|
48
51
|
@override
|
pulse/js/object.py
CHANGED
|
@@ -2,13 +2,16 @@
|
|
|
2
2
|
JavaScript Object builtin module.
|
|
3
3
|
|
|
4
4
|
Usage:
|
|
5
|
-
from pulse.js import Object
|
|
6
|
-
Object.keys({"a": 1}) # -> Object.keys({"a": 1})
|
|
7
|
-
Object.assign({}, {"a": 1}) # -> Object.assign({}, {"a": 1})
|
|
8
|
-
Object.is_(x, y) # -> Object.is(x, y)
|
|
9
5
|
|
|
10
|
-
|
|
11
|
-
|
|
6
|
+
```python
|
|
7
|
+
from pulse.js import Object
|
|
8
|
+
Object.keys({"a": 1}) # -> Object.keys({"a": 1})
|
|
9
|
+
Object.assign({}, {"a": 1}) # -> Object.assign({}, {"a": 1})
|
|
10
|
+
Object.is_(x, y) # -> Object.is(x, y)
|
|
11
|
+
|
|
12
|
+
# Or import from module directly:
|
|
13
|
+
from pulse.js.object import Object
|
|
14
|
+
```
|
|
12
15
|
"""
|
|
13
16
|
|
|
14
17
|
from collections.abc import Iterable as _Iterable
|