pulse-framework 0.1.58__py3-none-any.whl → 0.1.59__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.
pulse/app.py CHANGED
@@ -823,8 +823,6 @@ class App:
823
823
  async def _handle_pulse_message(
824
824
  self, render: RenderSession, session: UserSession, msg: ClientPulseMessage
825
825
  ) -> None:
826
- print(f"[MSG] {msg}")
827
-
828
826
  async def _next() -> Ok[None]:
829
827
  if msg["type"] == "attach":
830
828
  render.attach(msg["path"], msg["routeInfo"])
pulse/cli/cmd.py CHANGED
@@ -398,6 +398,7 @@ def build_uvicorn_command(
398
398
  on_ready: Callable[[], None] | None = None,
399
399
  plain: bool = False,
400
400
  ) -> CommandSpec:
401
+ cwd = app_ctx.server_cwd or app_ctx.app_dir or Path.cwd()
401
402
  app_import = f"{app_ctx.module_name}:{app_ctx.app_var}.asgi_factory"
402
403
  args: list[str] = [
403
404
  sys.executable,
@@ -418,6 +419,12 @@ def build_uvicorn_command(
418
419
  args.extend(["--reload-dir", str(app_dir)])
419
420
  if web_root.exists():
420
421
  args.extend(["--reload-dir", str(web_root)])
422
+ pulse_dir = str(app_ctx.app.codegen.cfg.pulse_dir)
423
+ pulse_app_dir = web_root / "app" / pulse_dir
424
+ rel_path = Path(os.path.relpath(pulse_app_dir, cwd))
425
+ if not rel_path.is_absolute():
426
+ args.extend(["--reload-exclude", str(rel_path)])
427
+ args.extend(["--reload-exclude", str(rel_path / "**")])
421
428
 
422
429
  if app_ctx.app.env == "prod":
423
430
  args.extend(production_flags())
@@ -450,8 +457,6 @@ def build_uvicorn_command(
450
457
  if dev_secret:
451
458
  command_env[ENV_PULSE_SECRET] = dev_secret
452
459
 
453
- cwd = app_ctx.server_cwd or app_ctx.app_dir or Path.cwd()
454
-
455
460
  # Apply custom log config to filter noisy requests (dev/ci only)
456
461
  if app_ctx.app.env != "prod" and not verbose:
457
462
  import json
pulse/codegen/codegen.py CHANGED
@@ -1,5 +1,4 @@
1
1
  import logging
2
- import shutil
3
2
  from collections.abc import Sequence
4
3
  from dataclasses import dataclass
5
4
  from pathlib import Path
@@ -107,20 +106,26 @@ class CodegenConfig:
107
106
  return self.web_root / "app" / self.pulse_dir
108
107
 
109
108
 
110
- def write_file_if_changed(path: Path, content: str) -> Path:
109
+ def write_file_if_changed(path: Path, content: str | bytes) -> Path:
111
110
  """Write content to file only if it has changed."""
112
111
  if path.exists():
113
112
  try:
114
- current_content = path.read_text()
113
+ if isinstance(content, bytes):
114
+ current_content = path.read_bytes()
115
+ else:
116
+ current_content = path.read_text()
115
117
  if current_content == content:
116
118
  return path # Skip writing, content is the same
117
- except Exception:
118
- logging.warning(f"Can't read file {path.absolute()}")
119
+ except Exception as exc:
120
+ logging.warning("Can't read file %s: %s", path.absolute(), exc)
119
121
  # If we can't read the file for any reason, just write it
120
122
  pass
121
123
 
122
124
  path.parent.mkdir(exist_ok=True, parents=True)
123
- path.write_text(content)
125
+ if isinstance(content, bytes):
126
+ path.write_bytes(content)
127
+ else:
128
+ path.write_text(content)
124
129
  return path
125
130
 
126
131
 
@@ -202,9 +207,17 @@ class Codegen:
202
207
 
203
208
  # Copy file if source exists
204
209
  if asset.source_path.exists():
205
- shutil.copy2(asset.source_path, dest_path)
206
210
  self._copied_files.add(dest_path)
207
- logger.debug(f"Copied {asset.source_path} -> {dest_path}")
211
+ try:
212
+ content = asset.source_path.read_bytes()
213
+ except OSError as exc:
214
+ logger.warning(
215
+ "Can't read asset %s: %s",
216
+ asset.source_path,
217
+ exc,
218
+ )
219
+ continue
220
+ write_file_if_changed(dest_path, content)
208
221
 
209
222
  def generate_layout_tsx(
210
223
  self,
@@ -19,8 +19,7 @@ class LinkPath(TypedDict):
19
19
  hash: str
20
20
 
21
21
 
22
- # @react_component(Import("Link", "react-router", version="^7"))
23
- @react_component(Import("Link", "react-router"))
22
+ @react_component(Import("Link", "react-router", version="^7"))
24
23
  def Link(
25
24
  *children: Node,
26
25
  key: str | None = None,
@@ -69,8 +68,7 @@ def Link(
69
68
  ...
70
69
 
71
70
 
72
- # @react_component(Import("Outlet", "react-router", version="^7"))
73
- @react_component(Import("Outlet", "react-router"))
71
+ @react_component(Import("Outlet", "react-router", version="^7"))
74
72
  def Outlet(key: str | None = None) -> None:
75
73
  """Renders the matched child route's element.
76
74
 
pulse/js/react.py CHANGED
@@ -384,7 +384,7 @@ class _LazyComponentFactory(_Expr):
384
384
 
385
385
  ```python
386
386
  # At definition time (Python executes this)
387
- LazyChart = lazy(Import("Chart", "./Chart", lazy=True))
387
+ LazyChart = lazy(Import("./Chart", lazy=True))
388
388
 
389
389
  # As reference in transpiled code
390
390
  @javascript
pulse/react_component.py CHANGED
@@ -5,6 +5,7 @@ from __future__ import annotations
5
5
  from collections.abc import Callable
6
6
  from typing import Any, ParamSpec, overload
7
7
 
8
+ from pulse.js.react import lazy as react_lazy
8
9
  from pulse.transpiler.imports import Import
9
10
  from pulse.transpiler.nodes import Element, Expr, Jsx, Node
10
11
 
@@ -19,9 +20,20 @@ def default_signature(
19
20
  class ReactComponent(Jsx):
20
21
  """JSX wrapper for React components with runtime call support."""
21
22
 
22
- def __init__(self, expr: Expr) -> None:
23
+ def __init__(self, expr_or_src: Expr | str, *, lazy: bool = False) -> None:
24
+ if isinstance(expr_or_src, str):
25
+ if lazy:
26
+ expr: Expr = react_lazy(Import(expr_or_src, lazy=True))
27
+ else:
28
+ expr = Import(expr_or_src)
29
+ else:
30
+ if lazy:
31
+ raise TypeError(
32
+ "ReactComponent lazy only supported with a source string"
33
+ )
34
+ expr = expr_or_src
23
35
  if not isinstance(expr, Expr):
24
- raise TypeError("ReactComponent expects an Expr")
36
+ raise TypeError("ReactComponent expects an Expr or source string")
25
37
  if isinstance(expr, Jsx):
26
38
  expr = expr.expr
27
39
  super().__init__(expr)
@@ -36,10 +48,9 @@ def react_component(
36
48
  @overload
37
49
  def react_component(
38
50
  expr_or_name: str,
39
- src: str,
51
+ src: str | None = None,
40
52
  *,
41
53
  lazy: bool = False,
42
- is_default: bool = False,
43
54
  ) -> Callable[[Callable[P, Any]], Callable[P, Element]]: ...
44
55
 
45
56
 
@@ -48,24 +59,23 @@ def react_component(
48
59
  src: str | None = None,
49
60
  *,
50
61
  lazy: bool = False,
51
- is_default: bool = False,
52
62
  ) -> Callable[[Callable[P, Any]], Callable[P, Element]]:
53
63
  """Decorator for typed React component bindings."""
54
64
  if isinstance(expr_or_name, Expr):
55
65
  if src is not None:
56
66
  raise TypeError("react_component expects (expr) or (name, src)")
57
67
  if lazy:
58
- raise TypeError("react_component lazy only supported with (name, src)")
59
- if is_default:
60
- raise TypeError(
61
- "react_component is_default only supported with (name, src)"
62
- )
68
+ raise TypeError("react_component lazy only supported with string inputs")
63
69
  component = ReactComponent(expr_or_name)
64
70
  elif isinstance(expr_or_name, str):
65
71
  if src is None:
66
- raise TypeError("react_component expects (name, src)")
67
- kind = "default" if is_default else None
68
- component = ReactComponent(Import(expr_or_name, src, kind=kind, lazy=lazy))
72
+ component = ReactComponent(expr_or_name, lazy=lazy)
73
+ else:
74
+ imp = Import(expr_or_name, src, lazy=lazy)
75
+ if lazy:
76
+ component = ReactComponent(react_lazy(imp))
77
+ else:
78
+ component = ReactComponent(imp)
69
79
  else:
70
80
  raise TypeError("react_component expects an Expr or (name, src)")
71
81
 
pulse/render_session.py CHANGED
@@ -457,7 +457,6 @@ class RenderSession:
457
457
  def detach(self, path: str, *, timeout: float | None = None):
458
458
  """Client no longer wants updates. Queue briefly, then dispose."""
459
459
  path = ensure_absolute_path(path)
460
- print(f"Detaching '{path}'")
461
460
  mount = self.route_mounts.get(path)
462
461
  if not mount:
463
462
  return
@@ -11,7 +11,7 @@ For lazy-loaded React components, use Import(lazy=True) with React.lazy:
11
11
  from pulse.js.react import React, lazy
12
12
 
13
13
  # Low-level: Import(lazy=True) creates a factory, wrap with React.lazy
14
- factory = Import("Chart", "./Chart", kind="default", lazy=True)
14
+ factory = Import("./Chart", lazy=True)
15
15
  LazyChart = Jsx(React.lazy(factory))
16
16
 
17
17
  # High-level: lazy() helper combines both
@@ -154,14 +154,13 @@ class Import(Expr):
154
154
  useState = Import("useState", "react")
155
155
 
156
156
  # Default import: import React from "react"
157
- React = Import("React", "react", kind="default")
157
+ React = Import("react")
158
158
 
159
- # Namespace import: import * as utils from "./utils"
160
- utils = Import("utils", "./utils", kind="namespace")
159
+ # Namespace import: import * as React from "react"
160
+ React = Import("*", "react")
161
161
 
162
162
  # Side-effect import: import "./styles.css"
163
- Import("", "./styles.css", kind="side_effect")
164
- Import("./styles.css")
163
+ Import("./styles.css", side_effect=True)
165
164
 
166
165
  # Type-only import: import type { Props } from "./types"
167
166
  Props = Import("Props", "./types", is_type=True)
@@ -171,12 +170,12 @@ class Import(Expr):
171
170
  # Button("Click me", disabled=True) -> <Button_1 disabled={true}>Click me</Button_1>
172
171
 
173
172
  # Local file imports (relative or absolute paths)
174
- Import("", "./styles.css", kind="side_effect") # Local CSS
175
- utils = Import("utils", "./utils", kind="namespace") # Local JS (resolves extension)
176
- config = Import("config", "/absolute/path/config", kind="default") # Absolute path
173
+ Import("./styles.css", side_effect=True) # Local CSS
174
+ utils = Import("*", "./utils") # Local JS namespace (resolves extension)
175
+ config = Import("/absolute/path/config") # Absolute path default import
177
176
 
178
177
  # Lazy import (generates factory for code-splitting)
179
- Chart = Import("Chart", "./Chart", kind="default", lazy=True)
178
+ Chart = Import("./Chart", lazy=True)
180
179
  # Generates: const Chart_1 = () => import("./Chart")
181
180
  """
182
181
 
@@ -197,7 +196,7 @@ class Import(Expr):
197
196
  name: str,
198
197
  src: str | None = None,
199
198
  *,
200
- kind: ImportKind | None = None,
199
+ side_effect: bool = False,
201
200
  is_type: bool = False,
202
201
  lazy: bool = False,
203
202
  version: str | None = None,
@@ -205,13 +204,25 @@ class Import(Expr):
205
204
  _caller_depth: int = 2,
206
205
  ) -> None:
207
206
  if src is None:
208
- if kind not in (None, "side_effect"):
209
- raise TypeError(
210
- "Import single-argument form is reserved for side effect imports"
211
- )
207
+ if name == "*":
208
+ raise TypeError("Import('*') requires a source")
212
209
  src = name
213
- name = ""
214
- kind = "side_effect"
210
+ if side_effect:
211
+ name = ""
212
+ kind: ImportKind = "side_effect"
213
+ else:
214
+ kind = "default"
215
+ else:
216
+ if side_effect:
217
+ raise TypeError("side_effect imports cannot specify a name")
218
+ if name == "*":
219
+ name = src
220
+ kind = "namespace"
221
+ else:
222
+ if not name:
223
+ raise TypeError("Import(name, src) requires a non-empty name")
224
+ kind = "named"
225
+
215
226
  # Validate: lazy imports cannot be type-only
216
227
  if lazy and is_type:
217
228
  raise TranspileError("Import cannot be both lazy and type-only")
@@ -229,10 +240,6 @@ class Import(Expr):
229
240
  asset = register_local_asset(resolved)
230
241
  import_src = str(resolved)
231
242
 
232
- # Default kind to "named" if not specified
233
- if kind is None:
234
- kind = "named"
235
-
236
243
  self.name = name
237
244
  self.src = import_src
238
245
  self.kind = kind
@@ -160,8 +160,9 @@ class JsModule(Expr):
160
160
  if self.src is None:
161
161
  return Identifier(self.name)
162
162
 
163
- import_kind = "default" if self.kind == "default" else "named"
164
- return Import(self.name, self.src, kind=import_kind)
163
+ if self.kind == "default":
164
+ return Import(self.src)
165
+ return Import("*", self.src)
165
166
 
166
167
  def get_value(self, name: str) -> Member | Class | Jsx | Identifier | Import:
167
168
  """Get a member of this module as an expression.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: pulse-framework
3
- Version: 0.1.58
3
+ Version: 0.1.59
4
4
  Summary: Pulse - Full-stack framework for building real-time React applications in Python
5
5
  Requires-Dist: websockets>=12.0
6
6
  Requires-Dist: fastapi>=0.128.0
@@ -1,9 +1,9 @@
1
1
  pulse/__init__.py,sha256=_7WUYtxNI78XXdnVu0CFTxRInlCEaP1tGEf-WXMU48I,31993
2
2
  pulse/_examples.py,sha256=dFuhD2EVXsbvAeexoG57s4VuN4gWLaTMOEMNYvlPm9A,561
3
- pulse/app.py,sha256=G26TprJ566Dhzr_31QUR41WnOyBbRfJ6weVZ-sk21sU,35120
3
+ pulse/app.py,sha256=JqxDN1pR-DU_VScF7JXivJpWoHAfsmWdiNOWLoSd_rI,35095
4
4
  pulse/channel.py,sha256=sQrDLh3k9Z8CyJQkEHzKu4h-yR4XSTgAA3OCQax3Ciw,15766
5
5
  pulse/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
6
- pulse/cli/cmd.py,sha256=hlAZcuTlqE4qKY96mkNm9cpxqWO3Pm3tDWrPN0lnrE8,15653
6
+ pulse/cli/cmd.py,sha256=zh3Ah6c16cNg3o_v_If_S58Qe8rvxNe5M2VrTkwvDU8,15957
7
7
  pulse/cli/dependencies.py,sha256=ezFw-7YWnJcvB1k25b0nB_OaOaP-EKtleNtBE2oJUUk,4603
8
8
  pulse/cli/folder_lock.py,sha256=-AKld2iM91G0uHB3F5ARD0QAjOw0TmsYYGaFgy_V350,3477
9
9
  pulse/cli/helpers.py,sha256=XXRRXeGFgeq-jbp0QGFFVq_aGg_Kp7_AkYsTK8LfSdg,7810
@@ -15,8 +15,7 @@ pulse/cli/secrets.py,sha256=dNfQe6AzSYhZuWveesjCRHIbvaPd3-F9lEJ-kZA7ROw,921
15
15
  pulse/cli/uvicorn_log_config.py,sha256=f7ikDc5foXh3TmFMrnfnW8yev48ZAdlo8F4F_aMVoVk,2391
16
16
  pulse/code_analysis.py,sha256=NBba_7VtOxZYMyfku_p-bWkG0O_1pi1AxcaNyVM1nmY,1024
17
17
  pulse/codegen/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
18
- pulse/codegen/codegen.py,sha256=86zmG6QGJpNe1uZEATSbM-FeQby6BJOaDXxLndzVVbo,10038
19
- pulse/codegen/js.py,sha256=fm2aL3RdnL7KlgrXDkKbM0E6BkXYBkID0XlKTDAX26c,1521
18
+ pulse/codegen/codegen.py,sha256=Zw55vzevg_17hFtSi6KLl-EWSiABKRfZe6fB-cWpLAk,10330
20
19
  pulse/codegen/templates/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
21
20
  pulse/codegen/templates/layout.py,sha256=nmWPQcO9SRXc3mCCVLCmykreSF96TqQfdDY7dvUBxRg,4737
22
21
  pulse/codegen/templates/route.py,sha256=UjBrb3e_8tMkd1OjBjEsnYmK6PCQqOYZBWDuU59FcrI,9234
@@ -26,7 +25,7 @@ pulse/component.py,sha256=TVgV0dgNDf2Smt2xsJVD0-Wejsnqm14n-NxBzsdObjc,6227
26
25
  pulse/components/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
27
26
  pulse/components/for_.py,sha256=lrt1JHegf4OkBbL9nrMOy7zxmbuD8Kn11x32ZGS72lY,2390
28
27
  pulse/components/if_.py,sha256=5IOq3R70B-JdI-fvDNYDyAaSEtO8L5OaiqHp-jUn-Kw,2153
29
- pulse/components/react_router.py,sha256=4dmp2yPNneyodi1PtT9VuO36bsAplbZg_TKZILlEzqs,3038
28
+ pulse/components/react_router.py,sha256=Nl6juntLSowFc38q7g_VMdcc4ju6lj8DUhpNR2NuOKQ,2934
30
29
  pulse/context.py,sha256=odTQlOhVRIwNvtatrmPe_Fd8Zk0rMcbcqQHBxvWYH5o,2677
31
30
  pulse/cookies.py,sha256=ozfdBKExdbpeM5ileIA1z8BZA5hoUrZ5_iO9fIMrgRk,8768
32
31
  pulse/decorators.py,sha256=BtNaisiqaJvlCuoqBgqQWbeFklDYLDrO1o1MRzFlybY,9932
@@ -65,7 +64,7 @@ pulse/js/obj.py,sha256=75NlJ7gsaMAXyYGXEeG5E4QKuKCy3ls9mDexodwxPyw,2085
65
64
  pulse/js/object.py,sha256=95WvnGWgB-PL-D7l12UgdxNy_fxO5sJXool3Rx5ahUQ,4433
66
65
  pulse/js/promise.py,sha256=vBXcL-U9BuZN-q1jbYhyzQaOL2niDPw4LsD7q7Y_yco,4670
67
66
  pulse/js/pulse.py,sha256=m-LgqwhYygVBj7GzjeO-uo8fK5ThyVe7c3QvOJt_vc0,2962
68
- pulse/js/react.py,sha256=tf3pUXu1BQO1KF7yestW4gfzCjLiejfAZ78vf_53sfI,12135
67
+ pulse/js/react.py,sha256=YGbWJcvYIrF__UW4xW1v5HcaIdSSUuDRJWOPK5DQKGw,12126
69
68
  pulse/js/regexp.py,sha256=qO-3nmt7uGN7V_bwimPCN-2RSsPfE6YiY7G1MjoP3YY,1055
70
69
  pulse/js/set.py,sha256=omG3g-25GRHxgoKISSB4x-M8UDFlaXtFV9cSIpd5uB0,3017
71
70
  pulse/js/string.py,sha256=VsvDF_ve8R9QIiBdDotLP2KpCKwmpEfGgRQWckOCmHk,813
@@ -86,10 +85,10 @@ pulse/queries/mutation.py,sha256=fhEpOZ7CuHImH4Y02QapYdTJrwe6K52-keb0d67wmms,827
86
85
  pulse/queries/protocol.py,sha256=R8n238Ex9DbYIAVKB83a8FAPtnCiPNhWar-F01K2fTo,3345
87
86
  pulse/queries/query.py,sha256=GTZY6xvan4mC3GgLbkRnzo6lSvfRhriN2UznyjyiEWE,38682
88
87
  pulse/queries/store.py,sha256=Ct7a-h1-Cq07zEfe9vw-LM85Fm7jIJx7CLAIlsiznlU,3444
89
- pulse/react_component.py,sha256=ub29y26fx9F1JxYrvfjkQP9wAMKAX0SbZAZkGgap724,2064
88
+ pulse/react_component.py,sha256=8RLg4Bi7IcjqbnbEnp4hJpy8t1UsE7mG0UR1Q655LDk,2332
90
89
  pulse/reactive.py,sha256=FxxpH7NBtQr7G89iCVN7y1EG21f23GcRi1M-XIxcRQA,31280
91
90
  pulse/reactive_extensions.py,sha256=yQ1PpdAh4kMvll7R15T72FOg8NFdG_HGBsGc63dawYk,33754
92
- pulse/render_session.py,sha256=Sek343ViDdS2r8IhLUTyZvzrb10kXTr4zDMNig2TwnA,21371
91
+ pulse/render_session.py,sha256=9gfwuBZRCWuQMN_nFuaAi__1UPN3I3C1mKWtAXyA3-A,21340
93
92
  pulse/renderer.py,sha256=bH7MWaQB1BVk6s60yEgZhUIoAuXO9olY5IA8dUqc_64,16002
94
93
  pulse/request.py,sha256=N0oFOLiGxpbgSgxznjvu64lG3YyOcZPKC8JFyKx6X7w,6023
95
94
  pulse/routing.py,sha256=LzTITvGgaLI1w7qTDZjFwoBcWAb4O8Dz7AmXeTNYrFU,16903
@@ -99,13 +98,13 @@ pulse/test_helpers.py,sha256=4iO5Ymy3SMvSjh-UaAaSdqm1I_SAJMNjdY2iYVro5f8,436
99
98
  pulse/transpiler/__init__.py,sha256=wDDnzqxgHpp_OLtcgyrJEg2jVoTnFIe3SSSTOsMDW8w,4700
100
99
  pulse/transpiler/assets.py,sha256=FHielogI5NrFwst5H94E49YWYFX7Tp1rwJHCcowT3P0,1974
101
100
  pulse/transpiler/builtins.py,sha256=QZrow7XJ2wxGMAE-mgZmaUD03egOnXCbikOg8yMx9vQ,30807
102
- pulse/transpiler/dynamic_import.py,sha256=wyIA-QzRi2-1cdzxGuFJleVKyVp5dEud6M-eP9SA_EY,3464
101
+ pulse/transpiler/dynamic_import.py,sha256=JECOyJMfusKYms7uwyHIqv4QabDnnPMweMqSXhp2m-4,3439
103
102
  pulse/transpiler/emit_context.py,sha256=GyK6VdsBSTVIewQRhBagaV0hlqLTlPZ1i8EAZGi8SaY,1321
104
103
  pulse/transpiler/errors.py,sha256=LSBjLBnMglbl2D94p9JR4y-3jDefk6iHSlUVBaBOTu4,2823
105
104
  pulse/transpiler/function.py,sha256=Pf5eoyzKHpn3d2EvCCYtARMnn_ixpAl7IZmY1fhmDIk,17036
106
105
  pulse/transpiler/id.py,sha256=CdgA1NndBpZjv0Hp4XiYbKn7wi-x4zWsFSjEiViKxVk,434
107
- pulse/transpiler/imports.py,sha256=DKrF-Wn0VkvEoJt7YZw4eOCf2kxbI9kqc3HwYcZGoik,9667
108
- pulse/transpiler/js_module.py,sha256=EWX8ZZ248H6kMQs9u6zsve3SPLy4lzf9F1cNEDdY1bA,11193
106
+ pulse/transpiler/imports.py,sha256=BW2y6Ftvn21hUl-A40gnW6toBAZ0tc4gsnyEXroyDFU,9702
107
+ pulse/transpiler/js_module.py,sha256=81aiJmf-BW8MGEgFggYrZ3m1FmOHShaxpR0Y5VVFQmg,11160
109
108
  pulse/transpiler/modules/__init__.py,sha256=JGi3CuZoF4sug4dNhQg3MFhpEQqnXec4xRJM2cHNP3c,1184
110
109
  pulse/transpiler/modules/asyncio.py,sha256=kWMuFU2vZbqutCM_EXJMvy5SdlB66XiT0czs8lELj_o,1584
111
110
  pulse/transpiler/modules/json.py,sha256=Zxe8dsaQ0Eoq3yHUiJeKEx6ibiN36HCT61ScFkLFCeY,676
@@ -121,7 +120,7 @@ pulse/types/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
121
120
  pulse/types/event_handler.py,sha256=psQCydj-WEtBcFU5JU4mDwvyzkW8V2O0g_VFRU2EOHI,1618
122
121
  pulse/user_session.py,sha256=nsnsMgqq2xGJZLpbHRMHUHcLrElMP8WcA4gjGMrcoBk,10208
123
122
  pulse/version.py,sha256=711vaM1jVIQPgkisGgKZqwmw019qZIsc_QTae75K2pg,1895
124
- pulse_framework-0.1.58.dist-info/WHEEL,sha256=eh7sammvW2TypMMMGKgsM83HyA_3qQ5Lgg3ynoecH3M,79
125
- pulse_framework-0.1.58.dist-info/entry_points.txt,sha256=i7aohd3QaPu5IcuGKKvsQQEiMYMe5HcF56QEsaLVO64,46
126
- pulse_framework-0.1.58.dist-info/METADATA,sha256=qDQAGM7VEqb_nqq4VVUDPwMBXxAWjlMPeaPSZtScBDs,8300
127
- pulse_framework-0.1.58.dist-info/RECORD,,
123
+ pulse_framework-0.1.59.dist-info/WHEEL,sha256=eh7sammvW2TypMMMGKgsM83HyA_3qQ5Lgg3ynoecH3M,79
124
+ pulse_framework-0.1.59.dist-info/entry_points.txt,sha256=i7aohd3QaPu5IcuGKKvsQQEiMYMe5HcF56QEsaLVO64,46
125
+ pulse_framework-0.1.59.dist-info/METADATA,sha256=IjfeExD3KNWxSW7wIExnhmRsUMghIRRFbf67NGkTMVI,8300
126
+ pulse_framework-0.1.59.dist-info/RECORD,,
pulse/codegen/js.py DELETED
@@ -1,74 +0,0 @@
1
- # Placeholders for the WIP JS compilation feature
2
- # NOTE: This module is deprecated. Use pulse.transpiler instead.
3
-
4
- from collections.abc import Callable
5
- from typing import Generic, TypeVar, TypeVarTuple
6
-
7
- from pulse.transpiler.imports import Import
8
-
9
- Args = TypeVarTuple("Args")
10
- R = TypeVar("R")
11
-
12
-
13
- class JsFunction(Generic[*Args, R]):
14
- "A transpiled JS function (deprecated - use pulse.transpiler.function.JsFunction)"
15
-
16
- name: str
17
- hint: Callable[[*Args], R]
18
-
19
- def __init__(
20
- self,
21
- name: str,
22
- hint: Callable[[*Args], R],
23
- ) -> None:
24
- self.name = name
25
- self.hint = hint
26
-
27
- def __call__(self, *args: *Args) -> R: ...
28
-
29
-
30
- class ExternalJsFunction(Generic[*Args, R]):
31
- "An imported JS function (deprecated - use pulse.transpiler.imports.Import)"
32
-
33
- import_: Import
34
- hint: Callable[[*Args], R]
35
- _prop: str | None
36
-
37
- def __init__(
38
- self,
39
- name: str,
40
- src: str,
41
- *,
42
- prop: str | None = None,
43
- is_default: bool,
44
- hint: Callable[[*Args], R],
45
- ) -> None:
46
- kind = "default" if is_default else "named"
47
- self.import_ = Import(name, src, kind=kind)
48
- self._prop = prop
49
- self.hint = hint
50
-
51
- @property
52
- def name(self) -> str:
53
- return self.import_.name
54
-
55
- @property
56
- def src(self) -> str:
57
- return self.import_.src
58
-
59
- @property
60
- def is_default(self) -> bool:
61
- return self.import_.is_default
62
-
63
- @property
64
- def prop(self) -> str | None:
65
- return self._prop
66
-
67
- @property
68
- def expr(self) -> str:
69
- base = self.import_.js_name
70
- if self._prop:
71
- return f"{base}.{self._prop}"
72
- return base
73
-
74
- def __call__(self, *args: *Args) -> R: ...