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.
Files changed (80) hide show
  1. pulse/__init__.py +5 -6
  2. pulse/app.py +144 -57
  3. pulse/channel.py +139 -7
  4. pulse/cli/cmd.py +16 -2
  5. pulse/code_analysis.py +38 -0
  6. pulse/codegen/codegen.py +61 -62
  7. pulse/codegen/templates/route.py +100 -56
  8. pulse/component.py +128 -6
  9. pulse/components/for_.py +30 -4
  10. pulse/components/if_.py +28 -5
  11. pulse/components/react_router.py +61 -3
  12. pulse/context.py +39 -5
  13. pulse/cookies.py +108 -4
  14. pulse/decorators.py +193 -24
  15. pulse/env.py +56 -2
  16. pulse/form.py +198 -5
  17. pulse/helpers.py +7 -1
  18. pulse/hooks/core.py +135 -5
  19. pulse/hooks/effects.py +61 -77
  20. pulse/hooks/init.py +60 -1
  21. pulse/hooks/runtime.py +241 -0
  22. pulse/hooks/setup.py +77 -0
  23. pulse/hooks/stable.py +58 -1
  24. pulse/hooks/state.py +107 -20
  25. pulse/js/__init__.py +41 -25
  26. pulse/js/array.py +9 -6
  27. pulse/js/console.py +15 -12
  28. pulse/js/date.py +9 -6
  29. pulse/js/document.py +5 -2
  30. pulse/js/error.py +7 -4
  31. pulse/js/json.py +9 -6
  32. pulse/js/map.py +8 -5
  33. pulse/js/math.py +9 -6
  34. pulse/js/navigator.py +5 -2
  35. pulse/js/number.py +9 -6
  36. pulse/js/obj.py +16 -13
  37. pulse/js/object.py +9 -6
  38. pulse/js/promise.py +19 -13
  39. pulse/js/pulse.py +28 -25
  40. pulse/js/react.py +190 -44
  41. pulse/js/regexp.py +7 -4
  42. pulse/js/set.py +8 -5
  43. pulse/js/string.py +9 -6
  44. pulse/js/weakmap.py +8 -5
  45. pulse/js/weakset.py +8 -5
  46. pulse/js/window.py +6 -3
  47. pulse/messages.py +5 -0
  48. pulse/middleware.py +147 -76
  49. pulse/plugin.py +76 -5
  50. pulse/queries/client.py +186 -39
  51. pulse/queries/common.py +52 -3
  52. pulse/queries/infinite_query.py +154 -2
  53. pulse/queries/mutation.py +127 -7
  54. pulse/queries/query.py +112 -11
  55. pulse/react_component.py +66 -3
  56. pulse/reactive.py +314 -30
  57. pulse/reactive_extensions.py +106 -26
  58. pulse/render_session.py +304 -173
  59. pulse/request.py +46 -11
  60. pulse/routing.py +140 -4
  61. pulse/serializer.py +71 -0
  62. pulse/state.py +177 -9
  63. pulse/test_helpers.py +15 -0
  64. pulse/transpiler/__init__.py +13 -3
  65. pulse/transpiler/assets.py +66 -0
  66. pulse/transpiler/dynamic_import.py +131 -0
  67. pulse/transpiler/emit_context.py +49 -0
  68. pulse/transpiler/function.py +6 -2
  69. pulse/transpiler/imports.py +33 -27
  70. pulse/transpiler/js_module.py +64 -8
  71. pulse/transpiler/py_module.py +1 -7
  72. pulse/transpiler/transpiler.py +4 -0
  73. pulse/user_session.py +119 -18
  74. {pulse_framework-0.1.54.dist-info → pulse_framework-0.1.56.dist-info}/METADATA +5 -5
  75. pulse_framework-0.1.56.dist-info/RECORD +127 -0
  76. pulse/js/react_dom.py +0 -30
  77. pulse/transpiler/react_component.py +0 -51
  78. pulse_framework-0.1.54.dist-info/RECORD +0 -124
  79. {pulse_framework-0.1.54.dist-info → pulse_framework-0.1.56.dist-info}/WHEEL +0 -0
  80. {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 typing import TypeVar, override
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(self, key: str, arg: State | Callable[[], State]) -> State:
26
- if key in self.called_keys:
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(key)
55
+ self.called_keys.add(full_identity)
31
56
 
32
- existing = self.instances.get(key)
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
- try:
37
- if not arg.__disposed__:
38
- arg.dispose()
39
- except RuntimeError:
40
- # Already disposed, ignore
41
- pass
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
- self.instances[key] = instance
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(key: str, arg: S | Callable[[], S]) -> S:
85
- """Get or create a state instance associated with the given key.
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 state instance (same instance on subsequent renders with the same key).
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(key, arg) # pyright: ignore[reportReturnType]
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
- # Import JS classes (for constructors and static methods):
5
- from pulse.js import Set, Number, Array, Date, Promise, Map, Error
6
- Set([1, 2, 3]) # -> new Set([1, 2, 3])
7
- Number.isFinite(42) # -> Number.isFinite(42)
8
- Array.isArray(x) # -> Array.isArray(x)
9
-
10
- # Import JS namespace objects (function-only modules):
11
- from pulse.js import Math, JSON, console, window, document, navigator
12
- Math.floor(3.7) # -> Math.floor(3.7)
13
- JSON.stringify(obj) # -> JSON.stringify(obj)
14
- console.log("hi") # -> console.log("hi")
15
-
16
- # Alternative: import namespace modules for namespace access:
17
- import pulse.js.json as JSON
18
- JSON.stringify(obj) # -> JSON.stringify(obj)
19
-
20
- # Statement functions:
21
- from pulse.js import throw
22
- throw(Error("message")) # -> throw Error("message");
23
-
24
- # Object literals (plain JS objects instead of Map):
25
- from pulse.js import obj
26
- obj(a=1, b=2) # -> { a: 1, b: 2 }
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
- # Or import from module directly:
11
- from pulse.js.array import Array
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
- import pulse.js.console as console
6
- console.log("Hello") # -> console.log("Hello")
7
- console.error("Error") # -> console.error("Error")
8
- console.assert(True, "msg") # -> console.assert(True, "msg")
9
-
10
- # Note: For 'assert' (Python keyword), use namespace import:
11
- # import pulse.js.console as console; console.assert(...)
12
- # Or use the underscore version for direct import:
13
- from pulse.js.console import log, error, warn, info, debug, assert_
14
- log("Hello") # -> console.log("Hello")
15
- error("Error") # -> console.error("Error")
16
- assert_(True, "msg") # -> console.assert(True, "msg")
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
- # Or import from module directly:
11
- from pulse.js.date import Date
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
- from pulse.js import document
5
- document.querySelector("#app") # -> document.querySelector("#app")
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
- from pulse.js.error import TypeError, RangeError, ReferenceError
9
- TypeError("message") # -> new TypeError("message")
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
- from pulse.js.json import stringify, parse
10
- stringify({"a": 1}) # -> JSON.stringify({"a": 1})
11
- parse('{"a": 1}') # -> JSON.parse('{"a": 1}')
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
- # Or import from module directly:
10
- from pulse.js.map import Map
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
- from pulse.js.math import PI, floor
10
- PI # -> Math.PI
11
- floor(3.7) # -> Math.floor(3.7)
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
- from pulse.js import navigator, console
5
- console.log(navigator.userAgent)
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
- # Or import from module directly:
11
- from pulse.js.number import Number
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
- # Create plain JS objects (not Maps):
8
- obj(a=1, b=2) # -> { a: 1, b: 2 }
6
+ ```python
7
+ from pulse.js import obj
9
8
 
10
- # With spread syntax:
11
- obj(**base, c=3) # -> { ...base, c: 3 }
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
- # Empty object:
15
- obj() # -> {}
12
+ # With spread syntax:
13
+ obj(**base, c=3) # -> { ...base, c: 3 }
14
+ obj(a=1, **base) # -> { a: 1, ...base }
16
15
 
17
- Unlike dict() which transpiles to new Map(), obj() creates plain JavaScript
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 **spread support.
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
- # Or import from module directly:
11
- from pulse.js.object import Object
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