pulse-framework 0.1.51__py3-none-any.whl → 0.1.52__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 (84) hide show
  1. pulse/__init__.py +542 -562
  2. pulse/_examples.py +29 -0
  3. pulse/app.py +0 -14
  4. pulse/cli/cmd.py +96 -80
  5. pulse/cli/dependencies.py +10 -41
  6. pulse/cli/folder_lock.py +3 -3
  7. pulse/cli/helpers.py +40 -67
  8. pulse/cli/logging.py +102 -0
  9. pulse/cli/packages.py +16 -0
  10. pulse/cli/processes.py +40 -23
  11. pulse/codegen/codegen.py +70 -35
  12. pulse/codegen/js.py +2 -4
  13. pulse/codegen/templates/route.py +94 -146
  14. pulse/component.py +115 -0
  15. pulse/components/for_.py +1 -1
  16. pulse/components/if_.py +1 -1
  17. pulse/components/react_router.py +16 -22
  18. pulse/{html → dom}/events.py +1 -1
  19. pulse/{html → dom}/props.py +6 -6
  20. pulse/{html → dom}/tags.py +11 -11
  21. pulse/dom/tags.pyi +480 -0
  22. pulse/form.py +7 -6
  23. pulse/hooks/init.py +1 -13
  24. pulse/js/__init__.py +37 -41
  25. pulse/js/__init__.pyi +22 -2
  26. pulse/js/_types.py +5 -3
  27. pulse/js/array.py +121 -38
  28. pulse/js/console.py +9 -9
  29. pulse/js/date.py +22 -19
  30. pulse/js/document.py +8 -4
  31. pulse/js/error.py +12 -14
  32. pulse/js/json.py +4 -3
  33. pulse/js/map.py +17 -7
  34. pulse/js/math.py +2 -2
  35. pulse/js/navigator.py +4 -4
  36. pulse/js/number.py +8 -8
  37. pulse/js/object.py +9 -13
  38. pulse/js/promise.py +25 -9
  39. pulse/js/regexp.py +6 -6
  40. pulse/js/set.py +20 -8
  41. pulse/js/string.py +7 -7
  42. pulse/js/weakmap.py +6 -6
  43. pulse/js/weakset.py +6 -6
  44. pulse/js/window.py +17 -14
  45. pulse/messages.py +1 -4
  46. pulse/react_component.py +3 -1001
  47. pulse/render_session.py +74 -66
  48. pulse/renderer.py +311 -238
  49. pulse/routing.py +1 -10
  50. pulse/transpiler/__init__.py +84 -114
  51. pulse/transpiler/builtins.py +661 -343
  52. pulse/transpiler/errors.py +78 -2
  53. pulse/transpiler/function.py +463 -133
  54. pulse/transpiler/id.py +18 -0
  55. pulse/transpiler/imports.py +230 -325
  56. pulse/transpiler/js_module.py +218 -209
  57. pulse/transpiler/modules/__init__.py +16 -13
  58. pulse/transpiler/modules/asyncio.py +45 -26
  59. pulse/transpiler/modules/json.py +12 -8
  60. pulse/transpiler/modules/math.py +161 -216
  61. pulse/transpiler/modules/pulse/__init__.py +5 -0
  62. pulse/transpiler/modules/pulse/tags.py +231 -0
  63. pulse/transpiler/modules/typing.py +33 -28
  64. pulse/transpiler/nodes.py +1607 -923
  65. pulse/transpiler/py_module.py +118 -95
  66. pulse/transpiler/react_component.py +51 -0
  67. pulse/transpiler/transpiler.py +593 -437
  68. pulse/transpiler/vdom.py +255 -0
  69. {pulse_framework-0.1.51.dist-info → pulse_framework-0.1.52.dist-info}/METADATA +1 -1
  70. pulse_framework-0.1.52.dist-info/RECORD +120 -0
  71. pulse/html/tags.pyi +0 -470
  72. pulse/transpiler/constants.py +0 -110
  73. pulse/transpiler/context.py +0 -26
  74. pulse/transpiler/ids.py +0 -16
  75. pulse/transpiler/modules/re.py +0 -466
  76. pulse/transpiler/modules/tags.py +0 -268
  77. pulse/transpiler/utils.py +0 -4
  78. pulse/vdom.py +0 -599
  79. pulse_framework-0.1.51.dist-info/RECORD +0 -119
  80. /pulse/{html → dom}/__init__.py +0 -0
  81. /pulse/{html → dom}/elements.py +0 -0
  82. /pulse/{html → dom}/svg.py +0 -0
  83. {pulse_framework-0.1.51.dist-info → pulse_framework-0.1.52.dist-info}/WHEEL +0 -0
  84. {pulse_framework-0.1.51.dist-info → pulse_framework-0.1.52.dist-info}/entry_points.txt +0 -0
pulse/js/json.py CHANGED
@@ -15,7 +15,7 @@ from collections.abc import Callable as _Callable
15
15
  from collections.abc import Sequence as _Sequence
16
16
  from typing import TypeVar as _TypeVar
17
17
 
18
- from pulse.transpiler.js_module import register_js_module as _register_js_module
18
+ from pulse.transpiler.js_module import JsModule
19
19
 
20
20
  # JSON types
21
21
  JSONValue = None | bool | int | float | str | list["JSONValue"] | dict[str, "JSONValue"]
@@ -25,7 +25,7 @@ JSONReviver = _Callable[[str, JSONValue], JSONValue]
25
25
  T = _TypeVar("T")
26
26
 
27
27
 
28
- def parse(text: str, reviver: JSONReviver | None = None) -> JSONValue:
28
+ def parse(text: str, reviver: JSONReviver | None = None, /) -> JSONValue:
29
29
  """Parse a JSON string into a JavaScript value.
30
30
 
31
31
  Args:
@@ -43,6 +43,7 @@ def stringify(
43
43
  value: JSONValue,
44
44
  replacer: JSONReplacer | None = None,
45
45
  space: int | str | None = None,
46
+ /,
46
47
  ) -> str:
47
48
  """Convert a JavaScript value to a JSON string.
48
49
 
@@ -59,4 +60,4 @@ def stringify(
59
60
 
60
61
 
61
62
  # Self-register this module as a JS builtin
62
- _register_js_module(name="JSON")
63
+ JsModule.register(name="JSON")
pulse/js/map.py CHANGED
@@ -2,21 +2,22 @@
2
2
  JavaScript Map builtin module.
3
3
 
4
4
  Usage:
5
- import pulse.js.map as Map
5
+ from pulse.js import Map
6
6
  Map() # -> new Map()
7
7
  Map([["a", 1]]) # -> new Map([["a", 1]])
8
8
 
9
+ # Or import from module directly:
9
10
  from pulse.js.map import Map
10
- Map() # -> new Map()
11
11
  """
12
12
 
13
13
  from collections.abc import Callable as _Callable
14
14
  from collections.abc import Iterable as _Iterable
15
+ from collections.abc import Iterator as _Iterator
15
16
  from typing import Any as _Any
16
17
  from typing import Generic as _Generic
17
18
  from typing import TypeVar as _TypeVar
18
19
 
19
- from pulse.transpiler.js_module import register_js_module as _register_js_module
20
+ from pulse.transpiler.js_module import JsModule
20
21
 
21
22
  K = _TypeVar("K")
22
23
  V = _TypeVar("V")
@@ -28,7 +29,7 @@ class Map(_Generic[K, V]):
28
29
  Map[K, V] preserves insertion order and allows keys of any type.
29
30
  """
30
31
 
31
- def __init__(self, iterable: _Iterable[tuple[K, V]] | None = None) -> None: ...
32
+ def __init__(self, iterable: _Iterable[tuple[K, V]] | None = None, /) -> None: ...
32
33
 
33
34
  @property
34
35
  def size(self) -> int:
@@ -59,6 +60,7 @@ class Map(_Generic[K, V]):
59
60
  self,
60
61
  callback: _Callable[[V, K, "Map[K, V]"], None],
61
62
  thisArg: _Any | None = None,
63
+ /,
62
64
  ) -> None:
63
65
  """Execute a function for each key/value pair."""
64
66
  ...
@@ -75,10 +77,18 @@ class Map(_Generic[K, V]):
75
77
  """Return an iterator of [key, value] pairs."""
76
78
  ...
77
79
 
78
- def __iter__(self) -> _Iterable[tuple[K, V]]:
80
+ def __iter__(self) -> _Iterator[tuple[K, V]]:
79
81
  """Iterate over [key, value] pairs."""
80
82
  ...
81
83
 
84
+ def __len__(self) -> int:
85
+ """Return the number of key/value pairs (same as size)."""
86
+ ...
87
+
88
+ def __contains__(self, key: K) -> bool:
89
+ """Check if key exists in the Map (same as has)."""
90
+ ...
91
+
82
92
 
83
- # Self-register this module as a JS builtin in global scope
84
- _register_js_module(name="Map", global_scope=True)
93
+ # Self-register this module as a JS builtin (global identifiers)
94
+ JsModule.register(name=None)
pulse/js/math.py CHANGED
@@ -11,7 +11,7 @@ Usage:
11
11
  floor(3.7) # -> Math.floor(3.7)
12
12
  """
13
13
 
14
- from pulse.transpiler.js_module import register_js_module as _register_js_module
14
+ from pulse.transpiler.js_module import JsModule
15
15
 
16
16
  # Constants (type stubs for IDE support)
17
17
  PI: float
@@ -63,4 +63,4 @@ def trunc(x: float) -> int: ...
63
63
 
64
64
 
65
65
  # Self-register this module as a JS builtin
66
- _register_js_module(name="Math")
66
+ JsModule.register(name="Math")
pulse/js/navigator.py CHANGED
@@ -8,7 +8,7 @@ Usage:
8
8
  from typing import Any as _Any
9
9
 
10
10
  from pulse.js._types import Clipboard as _Clipboard
11
- from pulse.transpiler.js_module import register_js_module as _register_js_module
11
+ from pulse.transpiler.js_module import JsModule
12
12
 
13
13
  # User agent and browser info
14
14
  userAgent: str
@@ -63,14 +63,14 @@ def share(data: dict[str, str]) -> _Any:
63
63
  ...
64
64
 
65
65
 
66
- def sendBeacon(url: str, data: str | bytes | _Any | None = None) -> bool:
66
+ def sendBeacon(url: str, data: str | bytes | _Any | None = None, /) -> bool:
67
67
  """Send data to a URL asynchronously. Returns True if successful."""
68
68
  ...
69
69
 
70
70
 
71
- def canShare(data: dict[str, str] | None = None) -> bool:
71
+ def canShare(data: dict[str, str] | None = None, /) -> bool:
72
72
  """Check if data can be shared via the Web Share API."""
73
73
  ...
74
74
 
75
75
 
76
- _register_js_module(name="navigator")
76
+ JsModule.register(name="navigator")
pulse/js/number.py CHANGED
@@ -2,19 +2,19 @@
2
2
  JavaScript Number builtin module.
3
3
 
4
4
  Usage:
5
- import pulse.js.number as Number
5
+ from pulse.js import Number
6
6
  Number.isFinite(42) # -> Number.isFinite(42)
7
7
  Number.MAX_SAFE_INTEGER # -> Number.MAX_SAFE_INTEGER
8
+ Number(x) # -> new Number(x)
8
9
 
9
- from pulse.js.number import isFinite, EPSILON
10
- isFinite(42) # -> Number.isFinite(42)
11
- EPSILON # -> Number.EPSILON
10
+ # Or import from module directly:
11
+ from pulse.js.number import Number
12
12
  """
13
13
 
14
14
  from typing import Any as _Any
15
15
  from typing import ClassVar as _ClassVar
16
16
 
17
- from pulse.transpiler.js_module import register_js_module as _register_js_module
17
+ from pulse.transpiler.js_module import JsModule
18
18
 
19
19
 
20
20
  class Number:
@@ -47,8 +47,8 @@ class Number:
47
47
  def parseFloat(string: str) -> float: ...
48
48
 
49
49
  @staticmethod
50
- def parseInt(string: str, radix: int = 10) -> int: ...
50
+ def parseInt(string: str, radix: int = 10, /) -> int: ...
51
51
 
52
52
 
53
- # Self-register this module as a JS builtin
54
- _register_js_module(name="Number")
53
+ # Self-register this module as a JS builtin (global identifier)
54
+ JsModule.register(name=None)
pulse/js/object.py CHANGED
@@ -2,18 +2,13 @@
2
2
  JavaScript Object builtin module.
3
3
 
4
4
  Usage:
5
- import pulse.js.object as Object
5
+ from pulse.js import Object
6
6
  Object.keys({"a": 1}) # -> Object.keys({"a": 1})
7
7
  Object.assign({}, {"a": 1}) # -> Object.assign({}, {"a": 1})
8
- Object.is(x, y) # -> Object.is(x, y)
9
-
10
- # Note: For 'is' (Python keyword), use namespace import:
11
- # import pulse.js.object as Object; Object.is(...)
12
- # Or use the underscore version for direct import:
13
- from pulse.js.object import keys, assign, is_
14
- keys({"a": 1}) # -> Object.keys({"a": 1})
15
- assign({}, {"a": 1}) # -> Object.assign({}, {"a": 1})
16
- is_(x, y) # -> Object.is(x, y)
8
+ Object.is_(x, y) # -> Object.is(x, y)
9
+
10
+ # Or import from module directly:
11
+ from pulse.js.object import Object
17
12
  """
18
13
 
19
14
  from collections.abc import Iterable as _Iterable
@@ -21,7 +16,7 @@ from typing import Any as _Any
21
16
  from typing import TypedDict as _TypedDict
22
17
  from typing import TypeVar as _TypeVar
23
18
 
24
- from pulse.transpiler.js_module import register_js_module as _register_js_module
19
+ from pulse.transpiler.js_module import JsModule
25
20
 
26
21
  T = _TypeVar("T")
27
22
  K = _TypeVar("K", bound=str) # Object keys are always strings in JS
@@ -55,6 +50,7 @@ class Object:
55
50
  def create(
56
51
  proto: _Any | None,
57
52
  propertiesObject: dict[str, PropertyDescriptor] | None = None,
53
+ /,
58
54
  ) -> _Any:
59
55
  """Create a new object with the specified prototype."""
60
56
  ...
@@ -169,5 +165,5 @@ class Object:
169
165
  ...
170
166
 
171
167
 
172
- # Self-register this module as a JS builtin
173
- _register_js_module(name="Object", global_scope=True)
168
+ # Self-register this module as a JS builtin (global identifier)
169
+ JsModule.register(name=None)
pulse/js/promise.py CHANGED
@@ -2,13 +2,13 @@
2
2
  JavaScript Promise builtin module.
3
3
 
4
4
  Usage:
5
- import pulse.js.promise as Promise
5
+ from pulse.js import Promise
6
+ Promise(executor) # -> new Promise(executor)
6
7
  Promise.resolve(value) # -> Promise.resolve(value)
7
8
  Promise.reject(reason) # -> Promise.reject(reason)
8
9
 
9
- from pulse.js.promise import resolve, reject, all, allSettled, race, any
10
- resolve(value) # -> Promise.resolve(value)
11
- reject(reason) # -> Promise.reject(reason)
10
+ # Or import from module directly:
11
+ from pulse.js.promise import Promise
12
12
 
13
13
  The Promise class is generic and supports async/await via the Awaitable protocol.
14
14
  """
@@ -16,10 +16,12 @@ The Promise class is generic and supports async/await via the Awaitable protocol
16
16
  from collections.abc import Callable as _Callable
17
17
  from collections.abc import Generator as _Generator
18
18
  from collections.abc import Iterable as _Iterable
19
+ from typing import Any as _Any
19
20
  from typing import Generic as _Generic
20
21
  from typing import TypeVar as _TypeVar
22
+ from typing import overload as _overload
21
23
 
22
- from pulse.transpiler.js_module import register_js_module as _register_js_module
24
+ from pulse.transpiler.js_module import JsModule
23
25
 
24
26
  T = _TypeVar("T")
25
27
  T_co = _TypeVar("T_co", covariant=True)
@@ -52,6 +54,7 @@ class Promise(_Generic[T_co]):
52
54
  [_Callable[[T_co], None], _Callable[[Exception], None]], None
53
55
  ]
54
56
  | None = None,
57
+ /,
55
58
  ) -> None:
56
59
  """Create a Promise.
57
60
 
@@ -65,6 +68,7 @@ class Promise(_Generic[T_co]):
65
68
  self,
66
69
  on_fulfilled: _Callable[[T_co], U | "Promise[U]"] | None = None,
67
70
  on_rejected: _Callable[[Exception], U | "Promise[U]"] | None = None,
71
+ /,
68
72
  ) -> "Promise[U]":
69
73
  """Attach callbacks to handle fulfillment and/or rejection."""
70
74
  ...
@@ -85,12 +89,24 @@ class Promise(_Generic[T_co]):
85
89
 
86
90
  # Static methods for Promise construction
87
91
  @staticmethod
88
- def resolve(value: U | None = None) -> "Promise[U] | Promise[None]":
92
+ @_overload
93
+ def resolve() -> "Promise[None]":
94
+ """Create a Promise that resolves with None."""
95
+ ...
96
+
97
+ @staticmethod
98
+ @_overload
99
+ def resolve(value: U, /) -> "Promise[U]":
100
+ """Create a Promise that resolves with the given value."""
101
+ ...
102
+
103
+ @staticmethod
104
+ def resolve(value: U | None = None, /) -> "Promise[U] | Promise[None]":
89
105
  """Create a Promise that resolves with the given value."""
90
106
  ...
91
107
 
92
108
  @staticmethod
93
- def reject(reason: Exception | str) -> "Promise[T]":
109
+ def reject(reason: Exception | str) -> "Promise[_Any]":
94
110
  """Create a Promise that rejects with the given reason."""
95
111
  ...
96
112
 
@@ -146,5 +162,5 @@ class PromiseWithResolvers(_Generic[T]):
146
162
  def reject(self) -> _Callable[[Exception | str], None]: ...
147
163
 
148
164
 
149
- # Self-register this module as a JS builtin
150
- _register_js_module(name="Promise", global_scope=True)
165
+ # Self-register this module as a JS builtin (global identifier)
166
+ JsModule.register(name=None)
pulse/js/regexp.py CHANGED
@@ -2,20 +2,20 @@
2
2
  JavaScript RegExp builtin module.
3
3
 
4
4
  Usage:
5
- import pulse.js.regexp as RegExp
5
+ from pulse.js import RegExp
6
6
  RegExp(pattern, flags) # -> new RegExp(pattern, flags)
7
7
 
8
+ # Or import from module directly:
8
9
  from pulse.js.regexp import RegExp
9
- RegExp(pattern, flags) # -> new RegExp(pattern, flags)
10
10
  """
11
11
 
12
- from pulse.transpiler.js_module import register_js_module as _register_js_module
12
+ from pulse.transpiler.js_module import JsModule
13
13
 
14
14
 
15
15
  class RegExp:
16
16
  """Class for JavaScript RegExp instances."""
17
17
 
18
- def __init__(self, pattern: str, flags: str | None = None): ...
18
+ def __init__(self, pattern: str, flags: str | None = None, /): ...
19
19
 
20
20
  def exec(self, string: str) -> list[str] | None: ...
21
21
  def test(self, string: str) -> bool: ...
@@ -50,5 +50,5 @@ class RegExp:
50
50
  def toString(self) -> str: ...
51
51
 
52
52
 
53
- # Self-register this module as a JS builtin
54
- _register_js_module(name="RegExp", global_scope=True)
53
+ # Self-register this module as a JS builtin (global identifiers)
54
+ JsModule.register(name=None)
pulse/js/set.py CHANGED
@@ -2,21 +2,22 @@
2
2
  JavaScript Set builtin module.
3
3
 
4
4
  Usage:
5
- import pulse.js.set as Set
5
+ from pulse.js import Set
6
6
  Set() # -> new Set()
7
7
  Set([1, 2, 3]) # -> new Set([1, 2, 3])
8
8
 
9
+ # Or import from module directly:
9
10
  from pulse.js.set import Set
10
- Set() # -> new Set()
11
11
  """
12
12
 
13
13
  from collections.abc import Callable as _Callable
14
14
  from collections.abc import Iterable as _Iterable
15
+ from collections.abc import Iterator as _Iterator
15
16
  from typing import Any as _Any
16
17
  from typing import Generic as _Generic
17
18
  from typing import TypeVar as _TypeVar
18
19
 
19
- from pulse.transpiler.js_module import register_js_module as _register_js_module
20
+ from pulse.transpiler.js_module import JsModule
20
21
 
21
22
  T = _TypeVar("T")
22
23
 
@@ -27,7 +28,7 @@ class Set(_Generic[T]):
27
28
  Set[T] stores unique values of type T in insertion order.
28
29
  """
29
30
 
30
- def __init__(self, iterable: _Iterable[T] | None = None) -> None: ...
31
+ def __init__(self, iterable: _Iterable[T] | None = None, /) -> None: ...
31
32
 
32
33
  @property
33
34
  def size(self) -> int:
@@ -51,7 +52,10 @@ class Set(_Generic[T]):
51
52
  ...
52
53
 
53
54
  def forEach(
54
- self, callback: _Callable[[T, T, "Set[T]"], None], thisArg: _Any | None = None
55
+ self,
56
+ callback: _Callable[[T, T, "Set[T]"], None],
57
+ thisArg: _Any | None = None,
58
+ /,
55
59
  ) -> None:
56
60
  """Execute a function for each value.
57
61
 
@@ -71,10 +75,18 @@ class Set(_Generic[T]):
71
75
  """Return an iterator of [value, value] pairs."""
72
76
  ...
73
77
 
74
- def __iter__(self) -> _Iterable[T]:
78
+ def __iter__(self) -> _Iterator[T]:
75
79
  """Iterate over values."""
76
80
  ...
77
81
 
82
+ def __len__(self) -> int:
83
+ """Return the number of values (same as size)."""
84
+ ...
85
+
86
+ def __contains__(self, value: T) -> bool:
87
+ """Check if value exists in the Set (same as has)."""
88
+ ...
89
+
78
90
  # ES2024 Set methods
79
91
  def union(self, other: "Set[T]") -> "Set[T]":
80
92
  """Return a new Set with values from both sets (ES2024)."""
@@ -105,5 +117,5 @@ class Set(_Generic[T]):
105
117
  ...
106
118
 
107
119
 
108
- # Self-register this module as a JS builtin in global scope
109
- _register_js_module(name="Set", global_scope=True)
120
+ # Self-register this module as a JS builtin (global identifiers)
121
+ JsModule.register(name=None)
pulse/js/string.py CHANGED
@@ -2,18 +2,18 @@
2
2
  JavaScript String builtin module.
3
3
 
4
4
  Usage:
5
- import pulse.js.string as String
5
+ from pulse.js import String
6
+ String(x) # -> new String(x)
6
7
  String.fromCharCode(65) # -> String.fromCharCode(65)
7
8
  String.fromCodePoint(0x1F600) # -> String.fromCodePoint(0x1F600)
8
9
 
9
- from pulse.js.string import fromCharCode, fromCodePoint
10
- fromCharCode(65) # -> String.fromCharCode(65)
11
- fromCodePoint(0x1F600) # -> String.fromCodePoint(0x1F600)
10
+ # Or import from module directly:
11
+ from pulse.js.string import String
12
12
  """
13
13
 
14
14
  from typing import Any as _Any
15
15
 
16
- from pulse.transpiler.js_module import register_js_module as _register_js_module
16
+ from pulse.transpiler.js_module import JsModule
17
17
 
18
18
 
19
19
  class String:
@@ -31,5 +31,5 @@ class String:
31
31
  def raw(template: str, *substitutions: str) -> str: ...
32
32
 
33
33
 
34
- # Self-register this module as a JS builtin
35
- _register_js_module(name="String", global_scope=True)
34
+ # Self-register this module as a JS builtin (global identifier)
35
+ JsModule.register(name=None)
pulse/js/weakmap.py CHANGED
@@ -2,19 +2,19 @@
2
2
  JavaScript WeakMap builtin module.
3
3
 
4
4
  Usage:
5
- import pulse.js.weakmap as WeakMap
5
+ from pulse.js import WeakMap
6
6
  WeakMap() # -> new WeakMap()
7
7
  WeakMap([[obj, "value"]]) # -> new WeakMap([[obj, "value"]])
8
8
 
9
+ # Or import from module directly:
9
10
  from pulse.js.weakmap import WeakMap
10
- WeakMap() # -> new WeakMap()
11
11
  """
12
12
 
13
13
  from collections.abc import Iterable as _Iterable
14
14
  from typing import Generic as _Generic
15
15
  from typing import TypeVar as _TypeVar
16
16
 
17
- from pulse.transpiler.js_module import register_js_module as _register_js_module
17
+ from pulse.transpiler.js_module import JsModule
18
18
 
19
19
  K = _TypeVar("K") # Keys must be objects in JS, but we can't enforce that statically
20
20
  V = _TypeVar("V")
@@ -27,7 +27,7 @@ class WeakMap(_Generic[K, V]):
27
27
  Keys must be objects (not primitives).
28
28
  """
29
29
 
30
- def __init__(self, iterable: _Iterable[tuple[K, V]] | None = None) -> None: ...
30
+ def __init__(self, iterable: _Iterable[tuple[K, V]] | None = None, /) -> None: ...
31
31
 
32
32
  def delete(self, key: K) -> bool:
33
33
  """Remove a key and its value. Returns True if the key existed."""
@@ -46,5 +46,5 @@ class WeakMap(_Generic[K, V]):
46
46
  ...
47
47
 
48
48
 
49
- # Self-register this module as a JS builtin
50
- _register_js_module(name="WeakMap", global_scope=True)
49
+ # Self-register this module as a JS builtin (global identifiers)
50
+ JsModule.register(name=None)
pulse/js/weakset.py CHANGED
@@ -2,19 +2,19 @@
2
2
  JavaScript WeakSet builtin module.
3
3
 
4
4
  Usage:
5
- import pulse.js.weakset as WeakSet
5
+ from pulse.js import WeakSet
6
6
  WeakSet() # -> new WeakSet()
7
7
  WeakSet([obj1, obj2]) # -> new WeakSet([obj1, obj2])
8
8
 
9
+ # Or import from module directly:
9
10
  from pulse.js.weakset import WeakSet
10
- WeakSet() # -> new WeakSet()
11
11
  """
12
12
 
13
13
  from collections.abc import Iterable as _Iterable
14
14
  from typing import Generic as _Generic
15
15
  from typing import TypeVar as _TypeVar
16
16
 
17
- from pulse.transpiler.js_module import register_js_module as _register_js_module
17
+ from pulse.transpiler.js_module import JsModule
18
18
 
19
19
  T = _TypeVar("T") # Values must be objects in JS, but we can't enforce that statically
20
20
 
@@ -26,7 +26,7 @@ class WeakSet(_Generic[T]):
26
26
  Values must be objects (not primitives).
27
27
  """
28
28
 
29
- def __init__(self, iterable: _Iterable[T] | None = None) -> None: ...
29
+ def __init__(self, iterable: _Iterable[T] | None = None, /) -> None: ...
30
30
 
31
31
  def add(self, value: T) -> "WeakSet[T]":
32
32
  """Add a value to the WeakSet. Returns the WeakSet for chaining."""
@@ -41,5 +41,5 @@ class WeakSet(_Generic[T]):
41
41
  ...
42
42
 
43
43
 
44
- # Self-register this module as a JS builtin in global scope
45
- _register_js_module(name="WeakSet", global_scope=True)
44
+ # Self-register this module as a JS builtin (global identifiers)
45
+ JsModule.register(name=None)
pulse/js/window.py CHANGED
@@ -1,4 +1,4 @@
1
- """Browser window global Any.
1
+ """Browser window global object.
2
2
 
3
3
  Usage:
4
4
  from pulse.js import window
@@ -11,7 +11,7 @@ from typing import Any as _Any
11
11
 
12
12
  from pulse.js._types import Element as _Element
13
13
  from pulse.js._types import Selection as _Selection
14
- from pulse.transpiler.js_module import register_js_module as _register_js_module
14
+ from pulse.transpiler.js_module import JsModule
15
15
 
16
16
  # Dimensions
17
17
  innerWidth: int
@@ -43,33 +43,33 @@ performance: _Any
43
43
 
44
44
 
45
45
  # Dialog methods
46
- def alert(message: str = "") -> None:
46
+ def alert(message: str = "", /) -> None:
47
47
  """Display an alert dialog with the given message."""
48
48
  ...
49
49
 
50
50
 
51
- def confirm(message: str = "") -> bool:
51
+ def confirm(message: str = "", /) -> bool:
52
52
  """Display a confirmation dialog. Returns True if user clicks OK."""
53
53
  ...
54
54
 
55
55
 
56
- def prompt(message: str = "", default: str = "") -> str | None:
56
+ def prompt(message: str = "", default: str = "", /) -> str | None:
57
57
  """Display a prompt dialog. Returns input or None if cancelled."""
58
58
  ...
59
59
 
60
60
 
61
61
  # Scroll methods
62
- def scrollTo(x: float | dict[str, float], y: float | None = None) -> None:
62
+ def scrollTo(x: float | dict[str, float], y: float | None = None, /) -> None:
63
63
  """Scroll to the given position."""
64
64
  ...
65
65
 
66
66
 
67
- def scrollBy(x: float | dict[str, float], y: float | None = None) -> None:
67
+ def scrollBy(x: float | dict[str, float], y: float | None = None, /) -> None:
68
68
  """Scroll by the given amount."""
69
69
  ...
70
70
 
71
71
 
72
- def scroll(x: float | dict[str, float], y: float | None = None) -> None:
72
+ def scroll(x: float | dict[str, float], y: float | None = None, /) -> None:
73
73
  """Alias for scrollTo."""
74
74
  ...
75
75
 
@@ -80,7 +80,7 @@ def getSelection() -> _Selection | None:
80
80
  ...
81
81
 
82
82
 
83
- def getComputedStyle(element: _Element, pseudoElt: str | None = None) -> _Any:
83
+ def getComputedStyle(element: _Element, pseudoElt: str | None = None, /) -> _Any:
84
84
  """Return the computed style of an element."""
85
85
  ...
86
86
 
@@ -101,8 +101,9 @@ def open(
101
101
  url: str = "",
102
102
  target: str = "_blank",
103
103
  features: str = "",
104
+ /,
104
105
  ) -> _Any | None:
105
- """Open a new window. Returns the new window Any or None."""
106
+ """Open a new window. Returns the new window object or None."""
106
107
  ...
107
108
 
108
109
 
@@ -112,7 +113,7 @@ def close() -> None:
112
113
 
113
114
 
114
115
  # Timers (these return timer IDs)
115
- def setTimeout(handler: _Callable[..., None], timeout: int = 0, *args: _Any) -> int:
116
+ def setTimeout(handler: _Callable[..., None], timeout: int = 0, /, *args: _Any) -> int:
116
117
  """Schedule a function to run after a delay. Returns timer ID."""
117
118
  ...
118
119
 
@@ -122,7 +123,7 @@ def clearTimeout(timeoutId: int) -> None:
122
123
  ...
123
124
 
124
125
 
125
- def setInterval(handler: _Callable[..., None], timeout: int = 0, *args: _Any) -> int:
126
+ def setInterval(handler: _Callable[..., None], timeout: int = 0, /, *args: _Any) -> int:
126
127
  """Schedule a function to run repeatedly. Returns timer ID."""
127
128
  ...
128
129
 
@@ -148,6 +149,7 @@ def addEventListener(
148
149
  type: str,
149
150
  listener: _Callable[..., None],
150
151
  options: bool | dict[str, bool] | None = None,
152
+ /,
151
153
  ) -> None:
152
154
  """Add an event listener to the window."""
153
155
  ...
@@ -157,6 +159,7 @@ def removeEventListener(
157
159
  type: str,
158
160
  listener: _Callable[..., None],
159
161
  options: bool | dict[str, bool] | None = None,
162
+ /,
160
163
  ) -> None:
161
164
  """Remove an event listener from the window."""
162
165
  ...
@@ -190,10 +193,10 @@ def print_() -> None:
190
193
 
191
194
 
192
195
  def postMessage(
193
- message: _Any, targetOrigin: str, transfer: list[_Any] | None = None
196
+ message: _Any, targetOrigin: str, transfer: list[_Any] | None = None, /
194
197
  ) -> None:
195
198
  """Post a message to another window."""
196
199
  ...
197
200
 
198
201
 
199
- _register_js_module(name="window")
202
+ JsModule.register(name="window")
pulse/messages.py CHANGED
@@ -1,7 +1,7 @@
1
1
  from typing import Any, Literal, NotRequired, TypedDict
2
2
 
3
3
  from pulse.routing import RouteInfo
4
- from pulse.vdom import VDOM, VDOMOperation
4
+ from pulse.transpiler.vdom import VDOM, VDOMOperation
5
5
 
6
6
 
7
7
  # ====================
@@ -11,9 +11,6 @@ class ServerInitMessage(TypedDict):
11
11
  type: Literal["vdom_init"]
12
12
  path: str
13
13
  vdom: VDOM
14
- callbacks: list[str]
15
- render_props: list[str]
16
- jsexpr_paths: list[str] # paths containing JS expressions
17
14
 
18
15
 
19
16
  class ServerUpdateMessage(TypedDict):