pulse-framework 0.1.46__py3-none-any.whl → 0.1.48__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 (73) hide show
  1. pulse/__init__.py +9 -23
  2. pulse/app.py +6 -25
  3. pulse/cli/processes.py +1 -0
  4. pulse/codegen/codegen.py +43 -88
  5. pulse/codegen/js.py +35 -5
  6. pulse/codegen/templates/route.py +341 -254
  7. pulse/form.py +1 -1
  8. pulse/helpers.py +51 -27
  9. pulse/hooks/core.py +2 -2
  10. pulse/hooks/effects.py +1 -1
  11. pulse/hooks/init.py +2 -1
  12. pulse/hooks/setup.py +1 -1
  13. pulse/hooks/stable.py +2 -2
  14. pulse/hooks/states.py +2 -2
  15. pulse/html/props.py +3 -2
  16. pulse/html/tags.py +135 -0
  17. pulse/html/tags.pyi +4 -0
  18. pulse/js/__init__.py +110 -0
  19. pulse/js/__init__.pyi +95 -0
  20. pulse/js/_types.py +297 -0
  21. pulse/js/array.py +253 -0
  22. pulse/js/console.py +47 -0
  23. pulse/js/date.py +113 -0
  24. pulse/js/document.py +138 -0
  25. pulse/js/error.py +139 -0
  26. pulse/js/json.py +62 -0
  27. pulse/js/map.py +84 -0
  28. pulse/js/math.py +66 -0
  29. pulse/js/navigator.py +76 -0
  30. pulse/js/number.py +54 -0
  31. pulse/js/object.py +173 -0
  32. pulse/js/promise.py +150 -0
  33. pulse/js/regexp.py +54 -0
  34. pulse/js/set.py +109 -0
  35. pulse/js/string.py +35 -0
  36. pulse/js/weakmap.py +50 -0
  37. pulse/js/weakset.py +45 -0
  38. pulse/js/window.py +199 -0
  39. pulse/messages.py +22 -3
  40. pulse/proxy.py +21 -8
  41. pulse/react_component.py +167 -14
  42. pulse/reactive_extensions.py +5 -5
  43. pulse/render_session.py +144 -34
  44. pulse/renderer.py +80 -115
  45. pulse/routing.py +1 -18
  46. pulse/transpiler/__init__.py +131 -0
  47. pulse/transpiler/builtins.py +731 -0
  48. pulse/transpiler/constants.py +110 -0
  49. pulse/transpiler/context.py +26 -0
  50. pulse/transpiler/errors.py +2 -0
  51. pulse/transpiler/function.py +250 -0
  52. pulse/transpiler/ids.py +16 -0
  53. pulse/transpiler/imports.py +409 -0
  54. pulse/transpiler/js_module.py +274 -0
  55. pulse/transpiler/modules/__init__.py +30 -0
  56. pulse/transpiler/modules/asyncio.py +38 -0
  57. pulse/transpiler/modules/json.py +20 -0
  58. pulse/transpiler/modules/math.py +320 -0
  59. pulse/transpiler/modules/re.py +466 -0
  60. pulse/transpiler/modules/tags.py +268 -0
  61. pulse/transpiler/modules/typing.py +59 -0
  62. pulse/transpiler/nodes.py +1216 -0
  63. pulse/transpiler/py_module.py +119 -0
  64. pulse/transpiler/transpiler.py +938 -0
  65. pulse/transpiler/utils.py +4 -0
  66. pulse/vdom.py +112 -6
  67. {pulse_framework-0.1.46.dist-info → pulse_framework-0.1.48.dist-info}/METADATA +1 -1
  68. pulse_framework-0.1.48.dist-info/RECORD +119 -0
  69. pulse/codegen/imports.py +0 -204
  70. pulse/css.py +0 -155
  71. pulse_framework-0.1.46.dist-info/RECORD +0 -80
  72. {pulse_framework-0.1.46.dist-info → pulse_framework-0.1.48.dist-info}/WHEEL +0 -0
  73. {pulse_framework-0.1.46.dist-info → pulse_framework-0.1.48.dist-info}/entry_points.txt +0 -0
pulse/js/document.py ADDED
@@ -0,0 +1,138 @@
1
+ """Browser document global object.
2
+
3
+ Usage:
4
+ from pulse.js import document
5
+ document.querySelector("#app") # -> document.querySelector("#app")
6
+ """
7
+
8
+ from collections.abc import Callable as _Callable
9
+ from typing import Any as _Any
10
+
11
+ from pulse.js._types import Element as _Element
12
+ from pulse.js._types import HTMLCollection as _HTMLCollection
13
+ from pulse.js._types import HTMLElement as _HTMLElement
14
+ from pulse.js._types import NodeList as _NodeList
15
+ from pulse.transpiler.js_module import register_js_module as _register_js_module
16
+
17
+ # Read-only properties
18
+ body: _HTMLElement
19
+ head: _HTMLElement
20
+ documentElement: _HTMLElement
21
+ activeElement: _Element | None
22
+ title: str
23
+ readyState: str # "loading" | "interactive" | "complete"
24
+ cookie: str
25
+ referrer: str
26
+ URL: str
27
+ domain: str
28
+
29
+
30
+ # Query methods
31
+ def querySelector(selectors: str) -> _Element | None:
32
+ """Return the first element matching the selector, or None."""
33
+ ...
34
+
35
+
36
+ def querySelectorAll(selectors: str) -> _NodeList[_Element]:
37
+ """Return all elements matching the selector."""
38
+ ...
39
+
40
+
41
+ def getElementById(elementId: str) -> _Element | None:
42
+ """Return the element with the given ID, or None."""
43
+ ...
44
+
45
+
46
+ def getElementsByClassName(classNames: str) -> _HTMLCollection[_Element]:
47
+ """Return all elements with the given class name(s)."""
48
+ ...
49
+
50
+
51
+ def getElementsByTagName(qualifiedName: str) -> _HTMLCollection[_Element]:
52
+ """Return all elements with the given tag name."""
53
+ ...
54
+
55
+
56
+ def getElementsByName(elementName: str) -> _NodeList[_Element]:
57
+ """Return all elements with the given name attribute."""
58
+ ...
59
+
60
+
61
+ # Element creation
62
+ def createElement(tagName: str, options: dict[str, str] | None = None) -> _HTMLElement:
63
+ """Create a new element with the given tag name."""
64
+ ...
65
+
66
+
67
+ def createTextNode(data: str) -> _Any:
68
+ """Create a new text node."""
69
+ ...
70
+
71
+
72
+ def createDocumentFragment() -> _Any:
73
+ """Create a new document fragment."""
74
+ ...
75
+
76
+
77
+ def createComment(data: str) -> _Any:
78
+ """Create a new comment node."""
79
+ ...
80
+
81
+
82
+ # Event methods
83
+ def addEventListener(
84
+ type: str,
85
+ listener: _Callable[..., None],
86
+ options: bool | dict[str, bool] | None = None,
87
+ ) -> None:
88
+ """Add an event listener to the document."""
89
+ ...
90
+
91
+
92
+ def removeEventListener(
93
+ type: str,
94
+ listener: _Callable[..., None],
95
+ options: bool | dict[str, bool] | None = None,
96
+ ) -> None:
97
+ """Remove an event listener from the document."""
98
+ ...
99
+
100
+
101
+ def dispatchEvent(event: _Any) -> bool:
102
+ """Dispatch an event to the document."""
103
+ ...
104
+
105
+
106
+ # Focus methods
107
+ def hasFocus() -> bool:
108
+ """Return True if the document has focus."""
109
+ ...
110
+
111
+
112
+ # Selection
113
+ def getSelection() -> _Any:
114
+ """Return the current selection."""
115
+ ...
116
+
117
+
118
+ # Node tree methods
119
+ def importNode(node: _Element, deep: bool = False) -> _Element:
120
+ """Import a node from another document."""
121
+ ...
122
+
123
+
124
+ def adoptNode(node: _Element) -> _Element:
125
+ """Adopt a node from another document."""
126
+ ...
127
+
128
+
129
+ # Full-screen API
130
+ def exitFullscreen() -> _Any:
131
+ """Exit full-screen mode. Returns a Promise."""
132
+ ...
133
+
134
+
135
+ fullscreenElement: _Element | None
136
+
137
+
138
+ _register_js_module(name="document")
pulse/js/error.py ADDED
@@ -0,0 +1,139 @@
1
+ """
2
+ JavaScript Error builtin module.
3
+
4
+ Usage:
5
+ import pulse.js.error as Error
6
+ Error("message") # -> new Error("message")
7
+ Error.RangeError("message") # -> new RangeError("message")
8
+
9
+ from pulse.js.error import Error, TypeError, RangeError, ReferenceError
10
+ Error("message") # -> new Error("message")
11
+ TypeError("message") # -> new TypeError("message")
12
+ """
13
+
14
+ from pulse.transpiler.js_module import register_js_module as _register_js_module
15
+
16
+
17
+ class Error:
18
+ """Class for JavaScript Error instances."""
19
+
20
+ def __init__(self, message: str | None = None): ...
21
+
22
+ @property
23
+ def message(self) -> str: ...
24
+
25
+ @property
26
+ def name(self) -> str: ...
27
+
28
+ @property
29
+ def stack(self) -> str | None: ...
30
+
31
+ def toString(self) -> str: ...
32
+
33
+
34
+ # Error Subclasses - these are separate globals in JS, not members of Error
35
+ # TODO: These need a different architecture (separate modules or standalone identifiers)
36
+ class EvalError:
37
+ """Class for JavaScript EvalError instances."""
38
+
39
+ def __init__(self, message: str | None = None): ...
40
+
41
+ @property
42
+ def message(self) -> str: ...
43
+
44
+ @property
45
+ def name(self) -> str: ...
46
+
47
+ @property
48
+ def stack(self) -> str | None: ...
49
+
50
+ def toString(self) -> str: ...
51
+
52
+
53
+ class RangeError:
54
+ """Class for JavaScript RangeError instances."""
55
+
56
+ def __init__(self, message: str | None = None): ...
57
+
58
+ @property
59
+ def message(self) -> str: ...
60
+
61
+ @property
62
+ def name(self) -> str: ...
63
+
64
+ @property
65
+ def stack(self) -> str | None: ...
66
+
67
+ def toString(self) -> str: ...
68
+
69
+
70
+ class ReferenceError:
71
+ """Class for JavaScript ReferenceError instances."""
72
+
73
+ def __init__(self, message: str | None = None): ...
74
+
75
+ @property
76
+ def message(self) -> str: ...
77
+
78
+ @property
79
+ def name(self) -> str: ...
80
+
81
+ @property
82
+ def stack(self) -> str | None: ...
83
+
84
+ def toString(self) -> str: ...
85
+
86
+
87
+ class SyntaxError:
88
+ """Class for JavaScript SyntaxError instances."""
89
+
90
+ def __init__(self, message: str | None = None): ...
91
+
92
+ @property
93
+ def message(self) -> str: ...
94
+
95
+ @property
96
+ def name(self) -> str: ...
97
+
98
+ @property
99
+ def stack(self) -> str | None: ...
100
+
101
+ def toString(self) -> str: ...
102
+
103
+
104
+ class TypeError:
105
+ """Class for JavaScript TypeError instances."""
106
+
107
+ def __init__(self, message: str | None = None): ...
108
+
109
+ @property
110
+ def message(self) -> str: ...
111
+
112
+ @property
113
+ def name(self) -> str: ...
114
+
115
+ @property
116
+ def stack(self) -> str | None: ...
117
+
118
+ def toString(self) -> str: ...
119
+
120
+
121
+ class URIError:
122
+ """Class for JavaScript URIError instances."""
123
+
124
+ def __init__(self, message: str | None = None): ...
125
+
126
+ @property
127
+ def message(self) -> str: ...
128
+
129
+ @property
130
+ def name(self) -> str: ...
131
+
132
+ @property
133
+ def stack(self) -> str | None: ...
134
+
135
+ def toString(self) -> str: ...
136
+
137
+
138
+ # Self-register this module as a JS builtin
139
+ _register_js_module(name="Error", global_scope=True)
pulse/js/json.py ADDED
@@ -0,0 +1,62 @@
1
+ """
2
+ JavaScript JSON builtin module.
3
+
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
+
9
+ from pulse.js.json import stringify, parse
10
+ stringify({"a": 1}) # -> JSON.stringify({"a": 1})
11
+ parse('{"a": 1}') # -> JSON.parse('{"a": 1}')
12
+ """
13
+
14
+ from collections.abc import Callable as _Callable
15
+ from collections.abc import Sequence as _Sequence
16
+ from typing import TypeVar as _TypeVar
17
+
18
+ from pulse.transpiler.js_module import register_js_module as _register_js_module
19
+
20
+ # JSON types
21
+ JSONValue = None | bool | int | float | str | list["JSONValue"] | dict[str, "JSONValue"]
22
+ JSONReplacer = _Sequence[str | int] | _Callable[[str, JSONValue], JSONValue]
23
+ JSONReviver = _Callable[[str, JSONValue], JSONValue]
24
+
25
+ T = _TypeVar("T")
26
+
27
+
28
+ def parse(text: str, reviver: JSONReviver | None = None) -> JSONValue:
29
+ """Parse a JSON string into a JavaScript value.
30
+
31
+ Args:
32
+ text: The JSON string to parse.
33
+ reviver: Optional function (key: str, value: Any) -> Any that transforms the parsed value.
34
+ Called for each key-value pair. Return the value to use, or undefined to omit.
35
+
36
+ Returns:
37
+ The parsed JavaScript value (null, bool, number, string, array, or object).
38
+ """
39
+ ...
40
+
41
+
42
+ def stringify(
43
+ value: JSONValue,
44
+ replacer: JSONReplacer | None = None,
45
+ space: int | str | None = None,
46
+ ) -> str:
47
+ """Convert a JavaScript value to a JSON string.
48
+
49
+ Args:
50
+ value: The value to convert to JSON.
51
+ replacer: Optional array of property names/indices, or function (key: str, value: Any) -> Any
52
+ that controls which properties are included. Return undefined to omit.
53
+ space: Optional indentation. Number for spaces per level, or string (up to 10 chars).
54
+
55
+ Returns:
56
+ The JSON string representation of the value.
57
+ """
58
+ ...
59
+
60
+
61
+ # Self-register this module as a JS builtin
62
+ _register_js_module(name="JSON")
pulse/js/map.py ADDED
@@ -0,0 +1,84 @@
1
+ """
2
+ JavaScript Map builtin module.
3
+
4
+ Usage:
5
+ import pulse.js.map as Map
6
+ Map() # -> new Map()
7
+ Map([["a", 1]]) # -> new Map([["a", 1]])
8
+
9
+ from pulse.js.map import Map
10
+ Map() # -> new Map()
11
+ """
12
+
13
+ from collections.abc import Callable as _Callable
14
+ from collections.abc import Iterable as _Iterable
15
+ from typing import Any as _Any
16
+ from typing import Generic as _Generic
17
+ from typing import TypeVar as _TypeVar
18
+
19
+ from pulse.transpiler.js_module import register_js_module as _register_js_module
20
+
21
+ K = _TypeVar("K")
22
+ V = _TypeVar("V")
23
+
24
+
25
+ class Map(_Generic[K, V]):
26
+ """JavaScript Map - a collection of keyed data items.
27
+
28
+ Map[K, V] preserves insertion order and allows keys of any type.
29
+ """
30
+
31
+ def __init__(self, iterable: _Iterable[tuple[K, V]] | None = None) -> None: ...
32
+
33
+ @property
34
+ def size(self) -> int:
35
+ """The number of key/value pairs in the Map."""
36
+ ...
37
+
38
+ def clear(self) -> None:
39
+ """Remove all key/value pairs."""
40
+ ...
41
+
42
+ def delete(self, key: K) -> bool:
43
+ """Remove a key and its value. Returns True if the key existed."""
44
+ ...
45
+
46
+ def get(self, key: K) -> V | None:
47
+ """Return the value for a key, or None if not present."""
48
+ ...
49
+
50
+ def has(self, key: K) -> bool:
51
+ """Return True if the key exists in the Map."""
52
+ ...
53
+
54
+ def set(self, key: K, value: V) -> "Map[K, V]":
55
+ """Set a key/value pair. Returns the Map for chaining."""
56
+ ...
57
+
58
+ def forEach(
59
+ self,
60
+ callback: _Callable[[V, K, "Map[K, V]"], None],
61
+ thisArg: _Any | None = None,
62
+ ) -> None:
63
+ """Execute a function for each key/value pair."""
64
+ ...
65
+
66
+ def keys(self) -> _Iterable[K]:
67
+ """Return an iterator of keys."""
68
+ ...
69
+
70
+ def values(self) -> _Iterable[V]:
71
+ """Return an iterator of values."""
72
+ ...
73
+
74
+ def entries(self) -> _Iterable[tuple[K, V]]:
75
+ """Return an iterator of [key, value] pairs."""
76
+ ...
77
+
78
+ def __iter__(self) -> _Iterable[tuple[K, V]]:
79
+ """Iterate over [key, value] pairs."""
80
+ ...
81
+
82
+
83
+ # Self-register this module as a JS builtin in global scope
84
+ _register_js_module(name="Map", global_scope=True)
pulse/js/math.py ADDED
@@ -0,0 +1,66 @@
1
+ """
2
+ JavaScript Math builtin module.
3
+
4
+ Usage:
5
+ import pulse.js.math as Math
6
+ Math.PI # -> Math.PI
7
+ Math.floor(3.7) # -> Math.floor(3.7)
8
+
9
+ from pulse.js.math import PI, floor
10
+ PI # -> Math.PI
11
+ floor(3.7) # -> Math.floor(3.7)
12
+ """
13
+
14
+ from pulse.transpiler.js_module import register_js_module as _register_js_module
15
+
16
+ # Constants (type stubs for IDE support)
17
+ PI: float
18
+ E: float
19
+ LN2: float
20
+ LN10: float
21
+ LOG2E: float
22
+ LOG10E: float
23
+ SQRT1_2: float
24
+ SQRT2: float
25
+
26
+
27
+ # Methods (type stubs for IDE support)
28
+ def abs(x: float) -> float: ...
29
+ def acos(x: float) -> float: ...
30
+ def acosh(x: float) -> float: ...
31
+ def asin(x: float) -> float: ...
32
+ def asinh(x: float) -> float: ...
33
+ def atan(x: float) -> float: ...
34
+ def atan2(y: float, x: float) -> float: ...
35
+ def atanh(x: float) -> float: ...
36
+ def cbrt(x: float) -> float: ...
37
+ def ceil(x: float) -> int: ...
38
+ def clz32(x: int) -> int: ...
39
+ def cos(x: float) -> float: ...
40
+ def cosh(x: float) -> float: ...
41
+ def exp(x: float) -> float: ...
42
+ def expm1(x: float) -> float: ...
43
+ def floor(x: float) -> int: ...
44
+ def fround(x: float) -> float: ...
45
+ def hypot(*values: float) -> float: ...
46
+ def imul(x: int, y: int) -> int: ...
47
+ def log(x: float) -> float: ...
48
+ def log10(x: float) -> float: ...
49
+ def log1p(x: float) -> float: ...
50
+ def log2(x: float) -> float: ...
51
+ def max(*values: float) -> float: ...
52
+ def min(*values: float) -> float: ...
53
+ def pow(x: float, y: float) -> float: ...
54
+ def random() -> float: ...
55
+ def round(x: float) -> int: ...
56
+ def sign(x: float) -> int: ...
57
+ def sin(x: float) -> float: ...
58
+ def sinh(x: float) -> float: ...
59
+ def sqrt(x: float) -> float: ...
60
+ def tan(x: float) -> float: ...
61
+ def tanh(x: float) -> float: ...
62
+ def trunc(x: float) -> int: ...
63
+
64
+
65
+ # Self-register this module as a JS builtin
66
+ _register_js_module(name="Math")
pulse/js/navigator.py ADDED
@@ -0,0 +1,76 @@
1
+ """Browser navigator global object.
2
+
3
+ Usage:
4
+ from pulse.js import navigator, console
5
+ console.log(navigator.userAgent)
6
+ """
7
+
8
+ from typing import Any as _Any
9
+
10
+ from pulse.js._types import Clipboard as _Clipboard
11
+ from pulse.transpiler.js_module import register_js_module as _register_js_module
12
+
13
+ # User agent and browser info
14
+ userAgent: str
15
+ language: str
16
+ languages: list[str]
17
+ platform: str
18
+ vendor: str
19
+ appName: str
20
+ appVersion: str
21
+
22
+ # Online status
23
+ onLine: bool
24
+
25
+ # Hardware info
26
+ hardwareConcurrency: int
27
+ maxTouchPoints: int
28
+ deviceMemory: float | None # May not be available in all browsers
29
+
30
+ # Cookies
31
+ cookieEnabled: bool
32
+
33
+ # PDF viewer
34
+ pdfViewerEnabled: bool
35
+
36
+ # Clipboard API
37
+ clipboard: _Clipboard
38
+
39
+ # Media devices (typed as _Any for simplicity)
40
+ mediaDevices: _Any
41
+
42
+ # Permissions (typed as _Any for simplicity)
43
+ permissions: _Any
44
+
45
+ # Service worker (typed as _Any for simplicity)
46
+ serviceWorker: _Any
47
+
48
+ # Geolocation (typed as _Any for simplicity)
49
+ geolocation: _Any
50
+
51
+ # Storage
52
+ storage: _Any
53
+
54
+
55
+ # Methods
56
+ def vibrate(pattern: int | list[int]) -> bool:
57
+ """Vibrate the device. Returns True if vibration is supported."""
58
+ ...
59
+
60
+
61
+ def share(data: dict[str, str]) -> _Any:
62
+ """Share data via the Web Share API. Returns a Promise."""
63
+ ...
64
+
65
+
66
+ def sendBeacon(url: str, data: str | bytes | _Any | None = None) -> bool:
67
+ """Send data to a URL asynchronously. Returns True if successful."""
68
+ ...
69
+
70
+
71
+ def canShare(data: dict[str, str] | None = None) -> bool:
72
+ """Check if data can be shared via the Web Share API."""
73
+ ...
74
+
75
+
76
+ _register_js_module(name="navigator")
pulse/js/number.py ADDED
@@ -0,0 +1,54 @@
1
+ """
2
+ JavaScript Number builtin module.
3
+
4
+ Usage:
5
+ import pulse.js.number as Number
6
+ Number.isFinite(42) # -> Number.isFinite(42)
7
+ Number.MAX_SAFE_INTEGER # -> Number.MAX_SAFE_INTEGER
8
+
9
+ from pulse.js.number import isFinite, EPSILON
10
+ isFinite(42) # -> Number.isFinite(42)
11
+ EPSILON # -> Number.EPSILON
12
+ """
13
+
14
+ from typing import Any as _Any
15
+ from typing import ClassVar as _ClassVar
16
+
17
+ from pulse.transpiler.js_module import register_js_module as _register_js_module
18
+
19
+
20
+ class Number:
21
+ """JavaScript Number constructor and namespace."""
22
+
23
+ def __init__(self, value: _Any) -> None: ...
24
+
25
+ EPSILON: _ClassVar[float]
26
+ MAX_SAFE_INTEGER: _ClassVar[int]
27
+ MAX_VALUE: _ClassVar[float]
28
+ MIN_SAFE_INTEGER: _ClassVar[int]
29
+ MIN_VALUE: _ClassVar[float]
30
+ NaN: _ClassVar[float]
31
+ NEGATIVE_INFINITY: _ClassVar[float]
32
+ POSITIVE_INFINITY: _ClassVar[float]
33
+
34
+ @staticmethod
35
+ def isFinite(value: float) -> bool: ...
36
+
37
+ @staticmethod
38
+ def isInteger(value: float) -> bool: ...
39
+
40
+ @staticmethod
41
+ def isNaN(value: float) -> bool: ...
42
+
43
+ @staticmethod
44
+ def isSafeInteger(value: float) -> bool: ...
45
+
46
+ @staticmethod
47
+ def parseFloat(string: str) -> float: ...
48
+
49
+ @staticmethod
50
+ def parseInt(string: str, radix: int = 10) -> int: ...
51
+
52
+
53
+ # Self-register this module as a JS builtin
54
+ _register_js_module(name="Number")