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/js/promise.py CHANGED
@@ -2,15 +2,18 @@
2
2
  JavaScript Promise builtin module.
3
3
 
4
4
  Usage:
5
- from pulse.js import Promise
6
- Promise(executor) # -> new Promise(executor)
7
- Promise.resolve(value) # -> Promise.resolve(value)
8
- Promise.reject(reason) # -> Promise.reject(reason)
9
5
 
10
- # Or import from module directly:
11
- from pulse.js.promise import Promise
6
+ ```python
7
+ from pulse.js import Promise
8
+ Promise(executor) # -> new Promise(executor)
9
+ Promise.resolve(value) # -> Promise.resolve(value)
10
+ Promise.reject(reason) # -> Promise.reject(reason)
12
11
 
13
- The Promise class is generic and supports async/await via the Awaitable protocol.
12
+ # Or import from module directly:
13
+ from pulse.js.promise import Promise
14
+ ```
15
+
16
+ The `Promise` class is generic and supports async/await via the Awaitable protocol.
14
17
  """
15
18
 
16
19
  from collections.abc import Callable as _Callable
@@ -36,16 +39,19 @@ PromiseSettledResult = PromiseFulfilledResult[T] | PromiseRejectedResult
36
39
  class Promise(_Generic[T_co]):
37
40
  """JavaScript Promise - a thenable that represents an async operation.
38
41
 
39
- Promise is both generic over its resolved type and implements Awaitable,
42
+ `Promise` is both generic over its resolved type and implements `Awaitable`,
40
43
  allowing it to be used with Python's async/await syntax which transpiles
41
44
  to JavaScript async/await.
42
45
 
43
46
  Example:
44
- @javascript
45
- async def fetch_data() -> str:
46
- response: Promise[Response] = fetch("/api/data")
47
- data = await response # Awaits the promise
48
- return data.text()
47
+
48
+ ```python
49
+ @javascript
50
+ async def fetch_data() -> str:
51
+ response: Promise[Response] = fetch("/api/data")
52
+ data = await response # Awaits the promise
53
+ return data.text()
54
+ ```
49
55
  """
50
56
 
51
57
  def __init__(
pulse/js/pulse.py CHANGED
@@ -2,31 +2,34 @@
2
2
  Pulse UI client bindings for channel communication.
3
3
 
4
4
  Usage:
5
- from pulse.js.pulse import usePulseChannel, ChannelBridge, PulseChannelResetError
6
-
7
- @ps.javascript(jsx=True)
8
- def MyChannelComponent(*, channel_id: str):
9
- bridge = usePulseChannel(channel_id)
10
-
11
- # Subscribe to events
12
- useEffect(
13
- lambda: bridge.on("server:notify", lambda payload: console.log(payload)),
14
- [bridge],
15
- )
16
-
17
- # Emit events to server
18
- def send_ping():
19
- bridge.emit("client:ping", {"message": "hello"})
20
-
21
- # Make requests to server
22
- async def send_request():
23
- response = await bridge.request("client:request", {"data": 123})
24
- console.log(response)
25
-
26
- return ps.div()[
27
- ps.button(onClick=send_ping)["Send Ping"],
28
- ps.button(onClick=send_request)["Send Request"],
29
- ]
5
+
6
+ ```python
7
+ from pulse.js.pulse import usePulseChannel, ChannelBridge, PulseChannelResetError
8
+
9
+ @ps.javascript(jsx=True)
10
+ def MyChannelComponent(*, channel_id: str):
11
+ bridge = usePulseChannel(channel_id)
12
+
13
+ # Subscribe to events
14
+ useEffect(
15
+ lambda: bridge.on("server:notify", lambda payload: console.log(payload)),
16
+ [bridge],
17
+ )
18
+
19
+ # Emit events to server
20
+ def send_ping():
21
+ bridge.emit("client:ping", {"message": "hello"})
22
+
23
+ # Make requests to server
24
+ async def send_request():
25
+ response = await bridge.request("client:request", {"data": 123})
26
+ console.log(response)
27
+
28
+ return ps.div()[
29
+ ps.button(onClick=send_ping)["Send Ping"],
30
+ ps.button(onClick=send_request)["Send Request"],
31
+ ]
32
+ ```
30
33
  """
31
34
 
32
35
  from collections.abc import Awaitable as _Awaitable
pulse/js/react.py CHANGED
@@ -2,22 +2,40 @@
2
2
  JavaScript React module.
3
3
 
4
4
  Usage:
5
- from pulse.js.react import useState, useEffect, useRef
6
- state, setState = useState(0) # -> const [state, setState] = useState(0)
7
- useEffect(lambda: print("hi"), []) # -> useEffect(() => console.log("hi"), [])
8
- ref = useRef(None) # -> const ref = useRef(null)
9
-
10
- # Also available as namespace:
11
- import pulse.js.react as React
12
- React.useState(0) # -> React.useState(0)
5
+
6
+ ```python
7
+ from pulse.js.react import useState, useEffect, useRef
8
+ state, setState = useState(0) # -> const [state, setState] = useState(0)
9
+ useEffect(lambda: print("hi"), []) # -> useEffect(() => console.log("hi"), [])
10
+ ref = useRef(None) # -> const ref = useRef(null)
11
+
12
+ # Also available as namespace:
13
+ import pulse.js.react as React
14
+ React.useState(0) # -> React.useState(0)
15
+ ```
13
16
  """
14
17
 
18
+ import ast as _ast
15
19
  from collections.abc import Callable as _Callable
20
+ from typing import TYPE_CHECKING as _TYPE_CHECKING
16
21
  from typing import Any as _Any
17
22
  from typing import Protocol as _Protocol
18
23
  from typing import TypeVar as _TypeVar
24
+ from typing import override as _override
19
25
 
26
+ from pulse.component import component as _component
27
+ from pulse.transpiler import Import as _Import
28
+ from pulse.transpiler.errors import TranspileError as _TranspileError
29
+ from pulse.transpiler.function import Constant as _Constant
20
30
  from pulse.transpiler.js_module import JsModule
31
+ from pulse.transpiler.nodes import Call as _Call
32
+ from pulse.transpiler.nodes import Expr as _Expr
33
+ from pulse.transpiler.nodes import Jsx as _Jsx
34
+ from pulse.transpiler.nodes import Node as _PulseNode
35
+ from pulse.transpiler.vdom import VDOMNode as _VDOMNode
36
+
37
+ if _TYPE_CHECKING:
38
+ from pulse.transpiler.transpiler import Transpiler as _Transpiler
21
39
 
22
40
  # Type variables for hooks
23
41
  T = _TypeVar("T")
@@ -101,9 +119,12 @@ def useState(
101
119
  """Returns a stateful value and a function to update it.
102
120
 
103
121
  Example:
104
- count, set_count = useState(0)
105
- set_count(count + 1)
106
- set_count(lambda prev: prev + 1)
122
+
123
+ ```python
124
+ count, set_count = useState(0)
125
+ set_count(count + 1)
126
+ set_count(lambda prev: prev + 1)
127
+ ```
107
128
  """
108
129
  ...
109
130
 
@@ -113,16 +134,19 @@ def useReducer(
113
134
  initial_arg: S,
114
135
  init: _Callable[[S], S] | None = None,
115
136
  ) -> tuple[S, Dispatch[A]]:
116
- """An alternative to useState for complex state logic.
137
+ """An alternative to `useState` for complex state logic.
117
138
 
118
139
  Example:
119
- def reducer(state, action):
120
- if action['type'] == 'increment':
121
- return {'count': state['count'] + 1}
122
- return state
123
140
 
124
- state, dispatch = useReducer(reducer, {'count': 0})
125
- dispatch({'type': 'increment'})
141
+ ```python
142
+ def reducer(state, action):
143
+ if action['type'] == 'increment':
144
+ return {'count': state['count'] + 1}
145
+ return state
146
+
147
+ state, dispatch = useReducer(reducer, {'count': 0})
148
+ dispatch({'type': 'increment'})
149
+ ```
126
150
  """
127
151
  ...
128
152
 
@@ -139,8 +163,11 @@ def useEffect(
139
163
  """Accepts a function that contains imperative, possibly effectful code.
140
164
 
141
165
  Example:
142
- useEffect(lambda: print("mounted"), [])
143
- useEffect(lambda: (print("update"), lambda: print("cleanup"))[-1], [dep])
166
+
167
+ ```python
168
+ useEffect(lambda: print("mounted"), [])
169
+ useEffect(lambda: (print("update"), lambda: print("cleanup"))[-1], [dep])
170
+ ```
144
171
  """
145
172
  ...
146
173
 
@@ -149,10 +176,13 @@ def useLayoutEffect(
149
176
  effect: _Callable[[], None | _Callable[[], None]],
150
177
  deps: list[_Any] | None = None,
151
178
  ) -> None:
152
- """Like useEffect, but fires synchronously after all DOM mutations.
179
+ """Like `useEffect`, but fires synchronously after all DOM mutations.
153
180
 
154
181
  Example:
155
- useLayoutEffect(lambda: measure_element(), [])
182
+
183
+ ```python
184
+ useLayoutEffect(lambda: measure_element(), [])
185
+ ```
156
186
  """
157
187
  ...
158
188
 
@@ -176,9 +206,12 @@ def useRef(initial_value: T) -> MutableRefObject[T]:
176
206
  """Returns a mutable ref object.
177
207
 
178
208
  Example:
179
- input_ref = useRef(None)
180
- # In JSX: <input ref={input_ref} />
181
- input_ref.current.focus()
209
+
210
+ ```python
211
+ input_ref = useRef(None)
212
+ # In JSX: <input ref={input_ref} />
213
+ input_ref.current.focus()
214
+ ```
182
215
  """
183
216
  ...
184
217
 
@@ -201,7 +234,10 @@ def useMemo(factory: _Callable[[], T], deps: list[_Any]) -> T:
201
234
  """Returns a memoized value.
202
235
 
203
236
  Example:
204
- expensive = useMemo(lambda: compute_expensive(a, b), [a, b])
237
+
238
+ ```python
239
+ expensive = useMemo(lambda: compute_expensive(a, b), [a, b])
240
+ ```
205
241
  """
206
242
  ...
207
243
 
@@ -210,7 +246,10 @@ def useCallback(callback: T, deps: list[_Any]) -> T:
210
246
  """Returns a memoized callback.
211
247
 
212
248
  Example:
213
- handle_click = useCallback(lambda e: print(e), [])
249
+
250
+ ```python
251
+ handle_click = useCallback(lambda e: print(e), [])
252
+ ```
214
253
  """
215
254
  ...
216
255
 
@@ -224,8 +263,11 @@ def useTransition() -> tuple[bool, TransitionStartFunction]:
224
263
  """Returns a stateful value for pending state and a function to start transition.
225
264
 
226
265
  Example:
227
- is_pending, start_transition = useTransition()
228
- start_transition(lambda: set_state(new_value))
266
+
267
+ ```python
268
+ is_pending, start_transition = useTransition()
269
+ start_transition(lambda: set_state(new_value))
270
+ ```
229
271
  """
230
272
  ...
231
273
 
@@ -239,7 +281,10 @@ def useContext(context: Context[T]) -> T:
239
281
  """Returns the current context value for the given context.
240
282
 
241
283
  Example:
242
- theme = useContext(ThemeContext)
284
+
285
+ ```python
286
+ theme = useContext(ThemeContext)
287
+ ```
243
288
  """
244
289
  ...
245
290
 
@@ -253,9 +298,12 @@ def useId() -> str:
253
298
  """Generates a unique ID that is stable across server and client.
254
299
 
255
300
  Example:
256
- id = useId()
257
- # <label htmlFor={id}>Name</label>
258
- # <input id={id} />
301
+
302
+ ```python
303
+ id = useId()
304
+ # <label htmlFor={id}>Name</label>
305
+ # <input id={id} />
306
+ ```
259
307
  """
260
308
  ...
261
309
 
@@ -273,10 +321,13 @@ def useSyncExternalStore(
273
321
  """Subscribe to an external store.
274
322
 
275
323
  Example:
276
- width = useSyncExternalStore(
277
- subscribe_to_resize,
278
- lambda: window.innerWidth
279
- )
324
+
325
+ ```python
326
+ width = useSyncExternalStore(
327
+ subscribe_to_resize,
328
+ lambda: window.innerWidth
329
+ )
330
+ ```
280
331
  """
281
332
  ...
282
333
 
@@ -321,9 +372,102 @@ def forwardRef(
321
372
  ...
322
373
 
323
374
 
324
- def lazy(load: _Callable[[], _Any]) -> _Any:
325
- """Lets you defer loading a component's code until it is rendered."""
326
- ...
375
+ class _LazyComponentFactory(_Expr):
376
+ """React.lazy binding that works both at definition time and in `@javascript`.
377
+
378
+ This Expr represents React's `lazy` function. It can be:
379
+ - Called at Python definition time: `lazy(factory)` → `Jsx(Constant(...))`
380
+ - Used as a reference in `@javascript`: `some_fn(lazy)` → `some_fn(lazy)`
381
+ - Called inside `@javascript`: `lazy(factory)` → creates `Constant+Jsx`
382
+
383
+ Usage:
384
+
385
+ ```python
386
+ # At definition time (Python executes this)
387
+ LazyChart = lazy(Import("Chart", "./Chart", lazy=True))
388
+
389
+ # As reference in transpiled code
390
+ @javascript
391
+ def foo():
392
+ return higher_order_fn(lazy) # → higher_order_fn(lazy)
393
+
394
+ # Called in transpiled code
395
+ @javascript
396
+ def bar():
397
+ LazyComp = lazy(factory) # → const LazyComp_1 = lazy(factory)
398
+ return LazyComp()
399
+ ```
400
+ """
401
+
402
+ __slots__: tuple[str, ...] = ("_lazy_import",)
403
+ _lazy_import: _Import | None
404
+
405
+ def __init__(self) -> None:
406
+ # Defer Import creation to avoid polluting global import registry at module load
407
+ self._lazy_import = None
408
+
409
+ @property
410
+ def _import(self) -> _Import:
411
+ """Lazily create the React.lazy import."""
412
+ if self._lazy_import is None:
413
+ self._lazy_import = _Import("lazy", "react")
414
+ return self._lazy_import
415
+
416
+ def _create_lazy_component(self, factory: _Expr) -> _Jsx:
417
+ """Create a lazy-loaded component from a factory expression.
418
+
419
+ Args:
420
+ factory: An Expr that evaluates to a dynamic import factory
421
+
422
+ Returns:
423
+ A Jsx-wrapped lazy component
424
+ """
425
+ lazy_call = _Call(self._import, [factory])
426
+ const = _Constant(lazy_call, lazy_call)
427
+ return _Jsx(const)
428
+
429
+ @_override
430
+ def emit(self, out: list[str]) -> None:
431
+ """Emit as reference to the lazy import."""
432
+ self._import.emit(out)
433
+
434
+ @_override
435
+ def render(self) -> _VDOMNode:
436
+ raise TypeError("lazy cannot be rendered to VDOM")
437
+
438
+ @_override
439
+ def transpile_call(
440
+ self,
441
+ args: list[_ast.expr],
442
+ keywords: list[_ast.keyword],
443
+ ctx: "_Transpiler",
444
+ ) -> _Expr:
445
+ """Handle lazy(factory) calls in @javascript functions."""
446
+ if keywords:
447
+ raise _TranspileError("lazy() does not accept keyword arguments")
448
+ if len(args) != 1:
449
+ raise _TranspileError("lazy() takes exactly 1 argument")
450
+
451
+ factory = ctx.emit_expr(args[0])
452
+ return self._create_lazy_component(factory)
453
+
454
+ @_override
455
+ def __call__(self, factory: _Import) -> _Jsx: # pyright: ignore[reportIncompatibleMethodOverride]
456
+ """Python-time call: create a lazy-loaded component.
457
+
458
+ Args:
459
+ factory: An Import with lazy=True that generates a dynamic import factory
460
+
461
+ Returns:
462
+ A Jsx-wrapped lazy component that can be used as LazyChart(props)[children]
463
+ """
464
+ return self._create_lazy_component(factory)
465
+
466
+
467
+ # Singleton instance - use as: lazy(Import(...))
468
+ lazy: _LazyComponentFactory = _LazyComponentFactory()
469
+ # Register so transpiler can resolve it from closure
470
+ _Expr.register(lazy, lazy)
327
471
 
328
472
 
329
473
  def createContext(default_value: T) -> Context[T]:
@@ -332,13 +476,15 @@ def createContext(default_value: T) -> Context[T]:
332
476
 
333
477
 
334
478
  # =============================================================================
335
- # Fragments
479
+ # Components (stub declarations become Jsx-wrapped imports)
336
480
  # =============================================================================
337
481
 
338
482
 
339
- class Fragment:
340
- """Lets you group elements without a wrapper node."""
341
-
483
+ @_component
484
+ def Suspense(
485
+ *, fallback: ReactNode | _PulseNode | None = None, name: str | None = None
486
+ ) -> ReactElement:
487
+ """Lets you display a fallback while its children are loading."""
342
488
  ...
343
489
 
344
490
 
pulse/js/regexp.py CHANGED
@@ -2,11 +2,14 @@
2
2
  JavaScript RegExp builtin module.
3
3
 
4
4
  Usage:
5
- from pulse.js import RegExp
6
- RegExp(pattern, flags) # -> new RegExp(pattern, flags)
7
5
 
8
- # Or import from module directly:
9
- from pulse.js.regexp import RegExp
6
+ ```python
7
+ from pulse.js import RegExp
8
+ RegExp(pattern, flags) # -> new RegExp(pattern, flags)
9
+
10
+ # Or import from module directly:
11
+ from pulse.js.regexp import RegExp
12
+ ```
10
13
  """
11
14
 
12
15
  from pulse.transpiler.js_module import JsModule
pulse/js/set.py CHANGED
@@ -2,12 +2,15 @@
2
2
  JavaScript Set builtin module.
3
3
 
4
4
  Usage:
5
- from pulse.js import Set
6
- Set() # -> new Set()
7
- Set([1, 2, 3]) # -> new Set([1, 2, 3])
8
5
 
9
- # Or import from module directly:
10
- from pulse.js.set import Set
6
+ ```python
7
+ from pulse.js import Set
8
+ Set() # -> new Set()
9
+ Set([1, 2, 3]) # -> new Set([1, 2, 3])
10
+
11
+ # Or import from module directly:
12
+ from pulse.js.set import Set
13
+ ```
11
14
  """
12
15
 
13
16
  from collections.abc import Callable as _Callable
pulse/js/string.py CHANGED
@@ -2,13 +2,16 @@
2
2
  JavaScript String builtin module.
3
3
 
4
4
  Usage:
5
- from pulse.js import String
6
- String(x) # -> new String(x)
7
- String.fromCharCode(65) # -> String.fromCharCode(65)
8
- String.fromCodePoint(0x1F600) # -> String.fromCodePoint(0x1F600)
9
5
 
10
- # Or import from module directly:
11
- from pulse.js.string import String
6
+ ```python
7
+ from pulse.js import String
8
+ String(x) # -> new String(x)
9
+ String.fromCharCode(65) # -> String.fromCharCode(65)
10
+ String.fromCodePoint(0x1F600) # -> String.fromCodePoint(0x1F600)
11
+
12
+ # Or import from module directly:
13
+ from pulse.js.string import String
14
+ ```
12
15
  """
13
16
 
14
17
  from typing import Any as _Any
pulse/js/weakmap.py CHANGED
@@ -2,12 +2,15 @@
2
2
  JavaScript WeakMap builtin module.
3
3
 
4
4
  Usage:
5
- from pulse.js import WeakMap
6
- WeakMap() # -> new WeakMap()
7
- WeakMap([[obj, "value"]]) # -> new WeakMap([[obj, "value"]])
8
5
 
9
- # Or import from module directly:
10
- from pulse.js.weakmap import WeakMap
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
+ ```
11
14
  """
12
15
 
13
16
  from collections.abc import Iterable as _Iterable
pulse/js/weakset.py CHANGED
@@ -2,12 +2,15 @@
2
2
  JavaScript WeakSet builtin module.
3
3
 
4
4
  Usage:
5
- from pulse.js import WeakSet
6
- WeakSet() # -> new WeakSet()
7
- WeakSet([obj1, obj2]) # -> new WeakSet([obj1, obj2])
8
5
 
9
- # Or import from module directly:
10
- from pulse.js.weakset import WeakSet
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
+ ```
11
14
  """
12
15
 
13
16
  from collections.abc import Iterable as _Iterable
pulse/js/window.py CHANGED
@@ -1,9 +1,12 @@
1
1
  """Browser window global object.
2
2
 
3
3
  Usage:
4
- from pulse.js import window
5
- window.alert("Hello!") # -> window.alert("Hello!")
6
- window.innerWidth # -> window.innerWidth
4
+
5
+ ```python
6
+ from pulse.js import window
7
+ window.alert("Hello!") # -> window.alert("Hello!")
8
+ window.innerWidth # -> window.innerWidth
9
+ ```
7
10
  """
8
11
 
9
12
  from collections.abc import Callable as _Callable
pulse/messages.py CHANGED
@@ -48,6 +48,10 @@ class ServerNavigateToMessage(TypedDict):
48
48
  hard: bool
49
49
 
50
50
 
51
+ class ServerReloadMessage(TypedDict):
52
+ type: Literal["reload"]
53
+
54
+
51
55
  class ServerApiCallMessage(TypedDict):
52
56
  type: Literal["api_call"]
53
57
  # Correlation id to match request/response
@@ -158,6 +162,7 @@ ServerMessage = (
158
162
  | ServerErrorMessage
159
163
  | ServerApiCallMessage
160
164
  | ServerNavigateToMessage
165
+ | ServerReloadMessage
161
166
  | ServerChannelMessage
162
167
  | ServerJsExecMessage
163
168
  )