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/number.py ADDED
@@ -0,0 +1,57 @@
1
+ """
2
+ JavaScript Number builtin module.
3
+
4
+ Usage:
5
+
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
+ ```
15
+ """
16
+
17
+ from typing import Any as _Any
18
+ from typing import ClassVar as _ClassVar
19
+
20
+ from pulse.transpiler.js_module import JsModule
21
+
22
+
23
+ class Number:
24
+ """JavaScript Number constructor and namespace."""
25
+
26
+ def __init__(self, value: _Any) -> None: ...
27
+
28
+ EPSILON: _ClassVar[float]
29
+ MAX_SAFE_INTEGER: _ClassVar[int]
30
+ MAX_VALUE: _ClassVar[float]
31
+ MIN_SAFE_INTEGER: _ClassVar[int]
32
+ MIN_VALUE: _ClassVar[float]
33
+ NaN: _ClassVar[float]
34
+ NEGATIVE_INFINITY: _ClassVar[float]
35
+ POSITIVE_INFINITY: _ClassVar[float]
36
+
37
+ @staticmethod
38
+ def isFinite(value: float) -> bool: ...
39
+
40
+ @staticmethod
41
+ def isInteger(value: float) -> bool: ...
42
+
43
+ @staticmethod
44
+ def isNaN(value: float) -> bool: ...
45
+
46
+ @staticmethod
47
+ def isSafeInteger(value: float) -> bool: ...
48
+
49
+ @staticmethod
50
+ def parseFloat(string: str) -> float: ...
51
+
52
+ @staticmethod
53
+ def parseInt(string: str, radix: int = 10, /) -> int: ...
54
+
55
+
56
+ # Self-register this module as a JS builtin (global identifier)
57
+ JsModule.register(name=None)
pulse/js/obj.py ADDED
@@ -0,0 +1,81 @@
1
+ """
2
+ JavaScript object literal creation.
3
+
4
+ Usage:
5
+
6
+ ```python
7
+ from pulse.js import obj
8
+
9
+ # Create plain JS objects (not Maps):
10
+ obj(a=1, b=2) # -> { a: 1, b: 2 }
11
+
12
+ # With spread syntax:
13
+ obj(**base, c=3) # -> { ...base, c: 3 }
14
+ obj(a=1, **base) # -> { a: 1, ...base }
15
+
16
+ # Empty object:
17
+ obj() # -> {}
18
+ ```
19
+
20
+ Unlike `dict()` which transpiles to `new Map()`, `obj()` creates plain JavaScript
21
+ object literals. Use this for React props, style objects, and anywhere you
22
+ need a plain JS object.
23
+ """
24
+
25
+ from __future__ import annotations
26
+
27
+ import ast
28
+ from dataclasses import dataclass
29
+ from typing import TYPE_CHECKING, override
30
+
31
+ from pulse.transpiler.errors import TranspileError
32
+ from pulse.transpiler.nodes import Expr, Object, Spread, spread_dict
33
+
34
+ # TYPE_CHECKING avoids import cycle: Transpiler -> nodes -> Expr -> obj -> Transpiler
35
+ if TYPE_CHECKING:
36
+ from pulse.transpiler.transpiler import Transpiler
37
+
38
+
39
+ @dataclass(slots=True)
40
+ class ObjTransformer(Expr):
41
+ """Transformer for `obj()` with `**spread` support.
42
+
43
+ - `obj(key=value, ...)` -> `{ key: value, ... }`
44
+ - `obj(**base, key=value)` -> `{ ...base, key: value }`
45
+
46
+ Creates a plain JavaScript object literal.
47
+ Use this instead of `dict()` when you need a plain object (e.g., for React props).
48
+ """
49
+
50
+ @override
51
+ def emit(self, out: list[str]) -> None:
52
+ raise TypeError("obj cannot be emitted directly - must be called")
53
+
54
+ @override
55
+ def render(self):
56
+ raise TypeError("obj cannot be rendered - must be called")
57
+
58
+ @override
59
+ def transpile_call(
60
+ self,
61
+ args: list[ast.expr],
62
+ keywords: list[ast.keyword],
63
+ ctx: Transpiler,
64
+ ) -> Expr:
65
+ if args:
66
+ raise TranspileError("obj() only accepts keyword arguments")
67
+
68
+ props: list[tuple[str, Expr] | Spread] = []
69
+ for kw in keywords:
70
+ if kw.arg is None:
71
+ # **spread syntax
72
+ props.append(spread_dict(ctx.emit_expr(kw.value)))
73
+ else:
74
+ # key=value
75
+ props.append((kw.arg, ctx.emit_expr(kw.value)))
76
+
77
+ return Object(props)
78
+
79
+
80
+ # Create singleton instance for use as a callable
81
+ obj = ObjTransformer()
pulse/js/object.py ADDED
@@ -0,0 +1,172 @@
1
+ """
2
+ JavaScript Object builtin module.
3
+
4
+ Usage:
5
+
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
+ ```
15
+ """
16
+
17
+ from collections.abc import Iterable as _Iterable
18
+ from typing import Any as _Any
19
+ from typing import TypedDict as _TypedDict
20
+ from typing import TypeVar as _TypeVar
21
+
22
+ from pulse.transpiler.js_module import JsModule
23
+
24
+ T = _TypeVar("T")
25
+ K = _TypeVar("K", bound=str) # Object keys are always strings in JS
26
+
27
+
28
+ # Property descriptor type - JS uses a plain object with specific keys
29
+ class PropertyDescriptor(_TypedDict, total=False):
30
+ """Type for Object.defineProperty descriptor."""
31
+
32
+ value: _Any
33
+ writable: bool
34
+ get: _Any # Callable[[], T] but we use _Any for flexibility
35
+ set: _Any # Callable[[T], None]
36
+ configurable: bool
37
+ enumerable: bool
38
+
39
+
40
+ class Object:
41
+ """JavaScript Object namespace - static methods for object manipulation.
42
+
43
+ Note: Object is primarily used as a namespace for static methods.
44
+ The types here are as precise as JavaScript's dynamic nature allows.
45
+ """
46
+
47
+ @staticmethod
48
+ def assign(target: T, *sources: _Any) -> T:
49
+ """Copy properties from sources to target. Returns target."""
50
+ ...
51
+
52
+ @staticmethod
53
+ def create(
54
+ proto: _Any | None,
55
+ propertiesObject: dict[str, PropertyDescriptor] | None = None,
56
+ /,
57
+ ) -> _Any:
58
+ """Create a new object with the specified prototype."""
59
+ ...
60
+
61
+ @staticmethod
62
+ def defineProperty(
63
+ obj: T, prop: str, descriptor: PropertyDescriptor | dict[str, _Any]
64
+ ) -> T:
65
+ """Define a property on an object. Returns the object."""
66
+ ...
67
+
68
+ @staticmethod
69
+ def defineProperties(
70
+ obj: T, props: dict[str, PropertyDescriptor | dict[str, _Any]]
71
+ ) -> T:
72
+ """Define multiple properties on an object. Returns the object."""
73
+ ...
74
+
75
+ @staticmethod
76
+ def entries(obj: dict[str, T]) -> list[tuple[str, T]]:
77
+ """Return an array of [key, value] pairs."""
78
+ ...
79
+
80
+ @staticmethod
81
+ def freeze(obj: T) -> T:
82
+ """Freeze an object (prevent modifications). Returns the object."""
83
+ ...
84
+
85
+ @staticmethod
86
+ def fromEntries(entries: _Iterable[tuple[str, T]]) -> dict[str, T]:
87
+ """Create an object from an iterable of [key, value] pairs."""
88
+ ...
89
+
90
+ @staticmethod
91
+ def getOwnPropertyDescriptor(obj: _Any, prop: str) -> PropertyDescriptor | None:
92
+ """Return the property descriptor for a property."""
93
+ ...
94
+
95
+ @staticmethod
96
+ def getOwnPropertyDescriptors(obj: _Any) -> dict[str, PropertyDescriptor]:
97
+ """Return all own property descriptors."""
98
+ ...
99
+
100
+ @staticmethod
101
+ def getOwnPropertyNames(obj: _Any) -> list[str]:
102
+ """Return all own property names (including non-enumerable)."""
103
+ ...
104
+
105
+ @staticmethod
106
+ def getOwnPropertySymbols(obj: _Any) -> list[_Any]:
107
+ """Return all own Symbol properties."""
108
+ ...
109
+
110
+ @staticmethod
111
+ def getPrototypeOf(obj: _Any) -> _Any | None:
112
+ """Return the prototype of an object."""
113
+ ...
114
+
115
+ @staticmethod
116
+ def hasOwn(obj: _Any, prop: str) -> bool:
117
+ """Return True if the object has the specified own property."""
118
+ ...
119
+
120
+ @staticmethod
121
+ def is_(value1: _Any, value2: _Any) -> bool:
122
+ """Determine if two values are the same value (SameValue algorithm)."""
123
+ ...
124
+
125
+ @staticmethod
126
+ def isExtensible(obj: _Any) -> bool:
127
+ """Return True if the object is extensible."""
128
+ ...
129
+
130
+ @staticmethod
131
+ def isFrozen(obj: _Any) -> bool:
132
+ """Return True if the object is frozen."""
133
+ ...
134
+
135
+ @staticmethod
136
+ def isSealed(obj: _Any) -> bool:
137
+ """Return True if the object is sealed."""
138
+ ...
139
+
140
+ @staticmethod
141
+ def keys(obj: dict[str, _Any]) -> list[str]:
142
+ """Return an array of enumerable property names."""
143
+ ...
144
+
145
+ @staticmethod
146
+ def preventExtensions(obj: T) -> T:
147
+ """Prevent new properties from being added. Returns the object."""
148
+ ...
149
+
150
+ @staticmethod
151
+ def seal(obj: T) -> T:
152
+ """Seal an object (prevent adding/removing properties). Returns the object."""
153
+ ...
154
+
155
+ @staticmethod
156
+ def setPrototypeOf(obj: T, prototype: _Any | None) -> T:
157
+ """Set the prototype of an object. Returns the object."""
158
+ ...
159
+
160
+ @staticmethod
161
+ def values(obj: dict[str, T]) -> list[T]:
162
+ """Return an array of enumerable property values."""
163
+ ...
164
+
165
+ @staticmethod
166
+ def groupBy(items: _Iterable[T], keyFn: _Any) -> dict[str, list[T]]:
167
+ """Group items by key function result (ES2024)."""
168
+ ...
169
+
170
+
171
+ # Self-register this module as a JS builtin (global identifier)
172
+ JsModule.register(name=None)
pulse/js/promise.py ADDED
@@ -0,0 +1,172 @@
1
+ """
2
+ JavaScript Promise builtin module.
3
+
4
+ Usage:
5
+
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)
11
+
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.
17
+ """
18
+
19
+ from collections.abc import Callable as _Callable
20
+ from collections.abc import Generator as _Generator
21
+ from collections.abc import Iterable as _Iterable
22
+ from typing import Any as _Any
23
+ from typing import Generic as _Generic
24
+ from typing import TypeVar as _TypeVar
25
+ from typing import overload as _overload
26
+
27
+ from pulse.transpiler.js_module import JsModule
28
+
29
+ T = _TypeVar("T")
30
+ T_co = _TypeVar("T_co", covariant=True)
31
+ U = _TypeVar("U")
32
+
33
+ # Result types for allSettled
34
+ PromiseFulfilledResult = dict[str, T | str] # { status: "fulfilled", value: T }
35
+ PromiseRejectedResult = dict[str, str] # { status: "rejected", reason: any }
36
+ PromiseSettledResult = PromiseFulfilledResult[T] | PromiseRejectedResult
37
+
38
+
39
+ class Promise(_Generic[T_co]):
40
+ """JavaScript Promise - a thenable that represents an async operation.
41
+
42
+ `Promise` is both generic over its resolved type and implements `Awaitable`,
43
+ allowing it to be used with Python's async/await syntax which transpiles
44
+ to JavaScript async/await.
45
+
46
+ Example:
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
+ ```
55
+ """
56
+
57
+ def __init__(
58
+ self,
59
+ executor: _Callable[
60
+ [_Callable[[T_co], None], _Callable[[Exception], None]], None
61
+ ]
62
+ | None = None,
63
+ /,
64
+ ) -> None:
65
+ """Create a Promise.
66
+
67
+ Args:
68
+ executor: Optional function receiving (resolve, reject) callbacks.
69
+ If omitted, creates a pending promise (for use with Promise.resolve/reject).
70
+ """
71
+ ...
72
+
73
+ def then(
74
+ self,
75
+ on_fulfilled: _Callable[[T_co], U | "Promise[U]"] | None = None,
76
+ on_rejected: _Callable[[Exception], U | "Promise[U]"] | None = None,
77
+ /,
78
+ ) -> "Promise[U]":
79
+ """Attach callbacks to handle fulfillment and/or rejection."""
80
+ ...
81
+
82
+ def catch(
83
+ self, on_rejected: _Callable[[Exception], U | "Promise[U]"]
84
+ ) -> "Promise[T_co | U]":
85
+ """Attach a rejection handler callback."""
86
+ ...
87
+
88
+ def finally_(self, on_finally: _Callable[[], None]) -> "Promise[T_co]":
89
+ """Attach a handler that is called when the promise settles (fulfilled or rejected)."""
90
+ ...
91
+
92
+ def __await__(self) -> _Generator[None, None, T_co]:
93
+ """Support await syntax - transpiles to JavaScript await."""
94
+ ...
95
+
96
+ # Static methods for Promise construction
97
+ @staticmethod
98
+ @_overload
99
+ def resolve() -> "Promise[None]":
100
+ """Create a Promise that resolves with None."""
101
+ ...
102
+
103
+ @staticmethod
104
+ @_overload
105
+ def resolve(value: U, /) -> "Promise[U]":
106
+ """Create a Promise that resolves with the given value."""
107
+ ...
108
+
109
+ @staticmethod
110
+ def resolve(value: U | None = None, /) -> "Promise[U] | Promise[None]":
111
+ """Create a Promise that resolves with the given value."""
112
+ ...
113
+
114
+ @staticmethod
115
+ def reject(reason: Exception | str) -> "Promise[_Any]":
116
+ """Create a Promise that rejects with the given reason."""
117
+ ...
118
+
119
+ # Static methods for combining promises
120
+ @staticmethod
121
+ def all(iterable: _Iterable["Promise[T]"]) -> "Promise[list[T]]":
122
+ """Wait for all promises to resolve, or reject on first rejection.
123
+
124
+ Returns a promise that resolves to a list of all resolved values.
125
+ """
126
+ ...
127
+
128
+ @staticmethod
129
+ def allSettled(
130
+ iterable: _Iterable["Promise[T]"],
131
+ ) -> "Promise[list[PromiseSettledResult[T]]]":
132
+ """Wait for all promises to settle (resolve or reject).
133
+
134
+ Returns a promise that resolves to a list of result objects.
135
+ """
136
+ ...
137
+
138
+ @staticmethod
139
+ def any(iterable: _Iterable["Promise[T]"]) -> "Promise[T]":
140
+ """Return first fulfilled promise, or reject if all reject."""
141
+ ...
142
+
143
+ @staticmethod
144
+ def race(iterable: _Iterable["Promise[T]"]) -> "Promise[T]":
145
+ """Return first settled promise (fulfilled or rejected)."""
146
+ ...
147
+
148
+ @staticmethod
149
+ def withResolvers() -> "PromiseWithResolvers[T]":
150
+ """Create a promise with its resolve and reject functions exposed.
151
+
152
+ Returns an object with { promise, resolve, reject }.
153
+ ES2024 feature.
154
+ """
155
+ ...
156
+
157
+
158
+ class PromiseWithResolvers(_Generic[T]):
159
+ """Result type for Promise.withResolvers()."""
160
+
161
+ @property
162
+ def promise(self) -> Promise[T]: ...
163
+
164
+ @property
165
+ def resolve(self) -> _Callable[[T], None]: ...
166
+
167
+ @property
168
+ def reject(self) -> _Callable[[Exception | str], None]: ...
169
+
170
+
171
+ # Self-register this module as a JS builtin (global identifier)
172
+ JsModule.register(name=None)
pulse/js/pulse.py ADDED
@@ -0,0 +1,115 @@
1
+ """
2
+ Pulse UI client bindings for channel communication.
3
+
4
+ Usage:
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
+ ```
33
+ """
34
+
35
+ from collections.abc import Awaitable as _Awaitable
36
+ from collections.abc import Callable as _Callable
37
+ from typing import Any as _Any
38
+ from typing import TypeVar as _TypeVar
39
+
40
+ from pulse.transpiler.js_module import JsModule
41
+
42
+ T = _TypeVar("T")
43
+
44
+
45
+ class PulseChannelResetError(Exception):
46
+ """Error raised when a channel is closed or reset."""
47
+
48
+ pass
49
+
50
+
51
+ class ChannelBridge:
52
+ """A bridge for bidirectional communication between client and server.
53
+
54
+ Provides methods for emitting events, making requests, and subscribing
55
+ to server events on a specific channel.
56
+ """
57
+
58
+ @property
59
+ def id(self) -> str:
60
+ """The unique channel identifier."""
61
+ ...
62
+
63
+ def emit(self, event: str, payload: _Any = None) -> None:
64
+ """Emit an event to the server.
65
+
66
+ Args:
67
+ event: The event name to emit.
68
+ payload: Optional data to send with the event.
69
+ """
70
+ ...
71
+
72
+ def request(self, event: str, payload: _Any = None) -> _Awaitable[_Any]:
73
+ """Make a request to the server and await a response.
74
+
75
+ Args:
76
+ event: The event name to send.
77
+ payload: Optional data to send with the request.
78
+
79
+ Returns:
80
+ A Promise that resolves with the server's response.
81
+ """
82
+ ...
83
+
84
+ def on(self, event: str, handler: _Callable[[_Any], _Any]) -> _Callable[[], None]:
85
+ """Subscribe to events from the server.
86
+
87
+ Args:
88
+ event: The event name to listen for.
89
+ handler: A callback function that receives the event payload.
90
+ May be sync or async. For request events, the return value
91
+ is sent back to the server.
92
+
93
+ Returns:
94
+ A cleanup function that unsubscribes the handler.
95
+ """
96
+ ...
97
+
98
+
99
+ def usePulseChannel(channel_id: str) -> ChannelBridge:
100
+ """React hook to connect to a Pulse channel.
101
+
102
+ Must be called from within a React component. The channel connection
103
+ is automatically managed based on component lifecycle.
104
+
105
+ Args:
106
+ channel_id: The unique identifier for the channel to connect to.
107
+
108
+ Returns:
109
+ A ChannelBridge instance for interacting with the channel.
110
+ """
111
+ ...
112
+
113
+
114
+ # Register as a JS module with named imports from pulse-ui-client
115
+ JsModule.register(name="pulse", src="pulse-ui-client", values="named_import")