tempestweb 0.3.0__tar.gz → 0.4.0__tar.gz

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 (115) hide show
  1. {tempestweb-0.3.0 → tempestweb-0.4.0}/CHANGELOG.md +16 -0
  2. {tempestweb-0.3.0 → tempestweb-0.4.0}/PKG-INFO +1 -1
  3. {tempestweb-0.3.0 → tempestweb-0.4.0}/pyproject.toml +1 -1
  4. {tempestweb-0.3.0 → tempestweb-0.4.0}/tempestweb/cli/commands/build.py +85 -6
  5. {tempestweb-0.3.0 → tempestweb-0.4.0}/.gitignore +0 -0
  6. {tempestweb-0.3.0 → tempestweb-0.4.0}/README.md +0 -0
  7. {tempestweb-0.3.0 → tempestweb-0.4.0}/client/constants.js +0 -0
  8. {tempestweb-0.3.0 → tempestweb-0.4.0}/client/dom.js +0 -0
  9. {tempestweb-0.3.0 → tempestweb-0.4.0}/client/events.js +0 -0
  10. {tempestweb-0.3.0 → tempestweb-0.4.0}/client/livereload.js +0 -0
  11. {tempestweb-0.3.0 → tempestweb-0.4.0}/client/native/audio.js +0 -0
  12. {tempestweb-0.3.0 → tempestweb-0.4.0}/client/native/camera.js +0 -0
  13. {tempestweb-0.3.0 → tempestweb-0.4.0}/client/native/clipboard.js +0 -0
  14. {tempestweb-0.3.0 → tempestweb-0.4.0}/client/native/file.js +0 -0
  15. {tempestweb-0.3.0 → tempestweb-0.4.0}/client/native/geolocation.js +0 -0
  16. {tempestweb-0.3.0 → tempestweb-0.4.0}/client/native/http.js +0 -0
  17. {tempestweb-0.3.0 → tempestweb-0.4.0}/client/native/index.js +0 -0
  18. {tempestweb-0.3.0 → tempestweb-0.4.0}/client/native/install.js +0 -0
  19. {tempestweb-0.3.0 → tempestweb-0.4.0}/client/native/notifications.js +0 -0
  20. {tempestweb-0.3.0 → tempestweb-0.4.0}/client/native/onnx.js +0 -0
  21. {tempestweb-0.3.0 → tempestweb-0.4.0}/client/native/share.js +0 -0
  22. {tempestweb-0.3.0 → tempestweb-0.4.0}/client/native/storage.js +0 -0
  23. {tempestweb-0.3.0 → tempestweb-0.4.0}/client/offline/store.js +0 -0
  24. {tempestweb-0.3.0 → tempestweb-0.4.0}/client/offline/sync.js +0 -0
  25. {tempestweb-0.3.0 → tempestweb-0.4.0}/client/push/web-push-client.js +0 -0
  26. {tempestweb-0.3.0 → tempestweb-0.4.0}/client/pwa/install-prompt.js +0 -0
  27. {tempestweb-0.3.0 → tempestweb-0.4.0}/client/pwa/manifest.js +0 -0
  28. {tempestweb-0.3.0 → tempestweb-0.4.0}/client/router.js +0 -0
  29. {tempestweb-0.3.0 → tempestweb-0.4.0}/client/style.js +0 -0
  30. {tempestweb-0.3.0 → tempestweb-0.4.0}/client/sw/register.js +0 -0
  31. {tempestweb-0.3.0 → tempestweb-0.4.0}/client/sw/sw.js +0 -0
  32. {tempestweb-0.3.0 → tempestweb-0.4.0}/client/tempestweb.js +0 -0
  33. {tempestweb-0.3.0 → tempestweb-0.4.0}/client/transport-sse.js +0 -0
  34. {tempestweb-0.3.0 → tempestweb-0.4.0}/client/transport-wasm.js +0 -0
  35. {tempestweb-0.3.0 → tempestweb-0.4.0}/client/transport-ws.js +0 -0
  36. {tempestweb-0.3.0 → tempestweb-0.4.0}/client/transport.js +0 -0
  37. {tempestweb-0.3.0 → tempestweb-0.4.0}/client/virtualize.js +0 -0
  38. {tempestweb-0.3.0 → tempestweb-0.4.0}/tempestweb/__init__.py +0 -0
  39. {tempestweb-0.3.0 → tempestweb-0.4.0}/tempestweb/_core/__init__.py +0 -0
  40. {tempestweb-0.3.0 → tempestweb-0.4.0}/tempestweb/cli/__init__.py +0 -0
  41. {tempestweb-0.3.0 → tempestweb-0.4.0}/tempestweb/cli/commands/__init__.py +0 -0
  42. {tempestweb-0.3.0 → tempestweb-0.4.0}/tempestweb/cli/commands/dev.py +0 -0
  43. {tempestweb-0.3.0 → tempestweb-0.4.0}/tempestweb/cli/commands/new.py +0 -0
  44. {tempestweb-0.3.0 → tempestweb-0.4.0}/tempestweb/cli/commands/run.py +0 -0
  45. {tempestweb-0.3.0 → tempestweb-0.4.0}/tempestweb/cli/config.py +0 -0
  46. {tempestweb-0.3.0 → tempestweb-0.4.0}/tempestweb/cli/loader.py +0 -0
  47. {tempestweb-0.3.0 → tempestweb-0.4.0}/tempestweb/cli/main.py +0 -0
  48. {tempestweb-0.3.0 → tempestweb-0.4.0}/tempestweb/cli/scaffold.py +0 -0
  49. {tempestweb-0.3.0 → tempestweb-0.4.0}/tempestweb/components/__init__.py +0 -0
  50. {tempestweb-0.3.0 → tempestweb-0.4.0}/tempestweb/components/fields.py +0 -0
  51. {tempestweb-0.3.0 → tempestweb-0.4.0}/tempestweb/components/forms.py +0 -0
  52. {tempestweb-0.3.0 → tempestweb-0.4.0}/tempestweb/core/__init__.py +0 -0
  53. {tempestweb-0.3.0 → tempestweb-0.4.0}/tempestweb/core/constants.py +0 -0
  54. {tempestweb-0.3.0 → tempestweb-0.4.0}/tempestweb/devserver/__init__.py +0 -0
  55. {tempestweb-0.3.0 → tempestweb-0.4.0}/tempestweb/devserver/http.py +0 -0
  56. {tempestweb-0.3.0 → tempestweb-0.4.0}/tempestweb/devserver/reload.py +0 -0
  57. {tempestweb-0.3.0 → tempestweb-0.4.0}/tempestweb/devserver/watcher.py +0 -0
  58. {tempestweb-0.3.0 → tempestweb-0.4.0}/tempestweb/native/__init__.py +0 -0
  59. {tempestweb-0.3.0 → tempestweb-0.4.0}/tempestweb/native/audio.py +0 -0
  60. {tempestweb-0.3.0 → tempestweb-0.4.0}/tempestweb/native/bridges.py +0 -0
  61. {tempestweb-0.3.0 → tempestweb-0.4.0}/tempestweb/native/camera.py +0 -0
  62. {tempestweb-0.3.0 → tempestweb-0.4.0}/tempestweb/native/clipboard.py +0 -0
  63. {tempestweb-0.3.0 → tempestweb-0.4.0}/tempestweb/native/dispatch.py +0 -0
  64. {tempestweb-0.3.0 → tempestweb-0.4.0}/tempestweb/native/file.py +0 -0
  65. {tempestweb-0.3.0 → tempestweb-0.4.0}/tempestweb/native/geolocation.py +0 -0
  66. {tempestweb-0.3.0 → tempestweb-0.4.0}/tempestweb/native/http.py +0 -0
  67. {tempestweb-0.3.0 → tempestweb-0.4.0}/tempestweb/native/install.py +0 -0
  68. {tempestweb-0.3.0 → tempestweb-0.4.0}/tempestweb/native/notifications.py +0 -0
  69. {tempestweb-0.3.0 → tempestweb-0.4.0}/tempestweb/native/onnx.py +0 -0
  70. {tempestweb-0.3.0 → tempestweb-0.4.0}/tempestweb/native/share.py +0 -0
  71. {tempestweb-0.3.0 → tempestweb-0.4.0}/tempestweb/native/storage.py +0 -0
  72. {tempestweb-0.3.0 → tempestweb-0.4.0}/tempestweb/observability/__init__.py +0 -0
  73. {tempestweb-0.3.0 → tempestweb-0.4.0}/tempestweb/observability/auth.py +0 -0
  74. {tempestweb-0.3.0 → tempestweb-0.4.0}/tempestweb/observability/error_boundary.py +0 -0
  75. {tempestweb-0.3.0 → tempestweb-0.4.0}/tempestweb/observability/feature_flags.py +0 -0
  76. {tempestweb-0.3.0 → tempestweb-0.4.0}/tempestweb/observability/logger.py +0 -0
  77. {tempestweb-0.3.0 → tempestweb-0.4.0}/tempestweb/observability/telemetry.py +0 -0
  78. {tempestweb-0.3.0 → tempestweb-0.4.0}/tempestweb/pwa/__init__.py +0 -0
  79. {tempestweb-0.3.0 → tempestweb-0.4.0}/tempestweb/pwa/icons.py +0 -0
  80. {tempestweb-0.3.0 → tempestweb-0.4.0}/tempestweb/pwa/manifest.py +0 -0
  81. {tempestweb-0.3.0 → tempestweb-0.4.0}/tempestweb/pwa/pyodide_vendor.py +0 -0
  82. {tempestweb-0.3.0 → tempestweb-0.4.0}/tempestweb/runtime/__init__.py +0 -0
  83. {tempestweb-0.3.0 → tempestweb-0.4.0}/tempestweb/runtime/events.py +0 -0
  84. {tempestweb-0.3.0 → tempestweb-0.4.0}/tempestweb/runtime/serialize.py +0 -0
  85. {tempestweb-0.3.0 → tempestweb-0.4.0}/tempestweb/runtime/session.py +0 -0
  86. {tempestweb-0.3.0 → tempestweb-0.4.0}/tempestweb/runtime/wasm.py +0 -0
  87. {tempestweb-0.3.0 → tempestweb-0.4.0}/tempestweb/runtime/wasm_main.py +0 -0
  88. {tempestweb-0.3.0 → tempestweb-0.4.0}/tempestweb/server/__init__.py +0 -0
  89. {tempestweb-0.3.0 → tempestweb-0.4.0}/tempestweb/server/app.py +0 -0
  90. {tempestweb-0.3.0 → tempestweb-0.4.0}/tempestweb/server/webpush.py +0 -0
  91. {tempestweb-0.3.0 → tempestweb-0.4.0}/tempestweb/transports/__init__.py +0 -0
  92. {tempestweb-0.3.0 → tempestweb-0.4.0}/tempestweb/transports/base.py +0 -0
  93. {tempestweb-0.3.0 → tempestweb-0.4.0}/tempestweb/transports/sse.py +0 -0
  94. {tempestweb-0.3.0 → tempestweb-0.4.0}/tempestweb/transports/wasm.py +0 -0
  95. {tempestweb-0.3.0 → tempestweb-0.4.0}/tempestweb/transports/websocket.py +0 -0
  96. {tempestweb-0.3.0 → tempestweb-0.4.0}/tests/client/dom.test.js +0 -0
  97. {tempestweb-0.3.0 → tempestweb-0.4.0}/tests/client/events.test.js +0 -0
  98. {tempestweb-0.3.0 → tempestweb-0.4.0}/tests/client/mount.test.js +0 -0
  99. {tempestweb-0.3.0 → tempestweb-0.4.0}/tests/client/native-onnx-file.test.js +0 -0
  100. {tempestweb-0.3.0 → tempestweb-0.4.0}/tests/client/native.test.js +0 -0
  101. {tempestweb-0.3.0 → tempestweb-0.4.0}/tests/client/offline-store.test.js +0 -0
  102. {tempestweb-0.3.0 → tempestweb-0.4.0}/tests/client/offline-sync.test.js +0 -0
  103. {tempestweb-0.3.0 → tempestweb-0.4.0}/tests/client/pwa-install-prompt.test.js +0 -0
  104. {tempestweb-0.3.0 → tempestweb-0.4.0}/tests/client/pwa-manifest.test.js +0 -0
  105. {tempestweb-0.3.0 → tempestweb-0.4.0}/tests/client/router.test.js +0 -0
  106. {tempestweb-0.3.0 → tempestweb-0.4.0}/tests/client/setup.js +0 -0
  107. {tempestweb-0.3.0 → tempestweb-0.4.0}/tests/client/smoke.test.js +0 -0
  108. {tempestweb-0.3.0 → tempestweb-0.4.0}/tests/client/style.test.js +0 -0
  109. {tempestweb-0.3.0 → tempestweb-0.4.0}/tests/client/sw-register.test.js +0 -0
  110. {tempestweb-0.3.0 → tempestweb-0.4.0}/tests/client/sw-strategies.test.js +0 -0
  111. {tempestweb-0.3.0 → tempestweb-0.4.0}/tests/client/transport-sse.test.js +0 -0
  112. {tempestweb-0.3.0 → tempestweb-0.4.0}/tests/client/transport-wasm.test.js +0 -0
  113. {tempestweb-0.3.0 → tempestweb-0.4.0}/tests/client/transport-ws.test.js +0 -0
  114. {tempestweb-0.3.0 → tempestweb-0.4.0}/tests/client/virtualize.test.js +0 -0
  115. {tempestweb-0.3.0 → tempestweb-0.4.0}/tests/client/web-push-client.test.js +0 -0
@@ -4,6 +4,22 @@ All notable changes to **tempestweb** are documented here. Format follows
4
4
  [Keep a Changelog](https://keepachangelog.com/); this project adheres to semantic
5
5
  versioning.
6
6
 
7
+ ## [0.4.0] — 2026-06-13
8
+
9
+ ### Added
10
+
11
+ - **`[wasm].modules` resolves from the installed environment** — each entry is now
12
+ resolved in two steps: a vendored copy beside `app.py`
13
+ (`<project>/<module>/`) still wins, but when none exists the module is pulled
14
+ straight from the project's `.venv` `site-packages` via `importlib`. A
15
+ dependency you `uv add` no longer has to be cloned and committed at the repo
16
+ root to make it into the wasm bundle — just list it in `modules`. A name that is
17
+ neither vendored nor importable fails the build with a clear message.
18
+ Backward compatible: existing vendored layouts build unchanged. A stale project
19
+ directory holding only `__pycache__` (real source deleted, bytecode lingering)
20
+ no longer shadows the installed package and silently bundles nothing — it falls
21
+ through to the installed copy.
22
+
7
23
  ## [0.3.0] — 2026-06-13
8
24
 
9
25
  ### Added
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: tempestweb
3
- Version: 0.3.0
3
+ Version: 0.4.0
4
4
  Summary: Build web apps in typed Python — one tree, a DOM renderer, two execution modes (WASM + server).
5
5
  Author-email: Mauricio Benjamin <mauricio.benjamin@reloverelations.com>
6
6
  License: MIT
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "tempestweb"
3
- version = "0.3.0"
3
+ version = "0.4.0"
4
4
  description = "Build web apps in typed Python — one tree, a DOM renderer, two execution modes (WASM + server)."
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.11"
@@ -23,6 +23,7 @@ WebSocket transport — ``python server.py`` (or ``uvicorn server:app``) serves
23
23
  from __future__ import annotations
24
24
 
25
25
  import hashlib
26
+ import importlib.util
26
27
  import json
27
28
  import shutil
28
29
  import zipfile
@@ -236,6 +237,80 @@ def _zip_tree(
236
237
  archive.write(path, str(path.relative_to(root)))
237
238
 
238
239
 
240
+ def _is_vendored(candidate: Path) -> bool:
241
+ """Tell whether ``candidate`` is a usable vendored module/package.
242
+
243
+ A single file counts. A directory counts only if it holds at least one
244
+ bundlable file (anything outside ``__pycache__``) — so a stale directory
245
+ left holding only ``__pycache__`` after the real source was deleted does
246
+ **not** shadow the installed package and silently bundle nothing.
247
+
248
+ Args:
249
+ candidate: The ``project_root/module`` path to test.
250
+
251
+ Returns:
252
+ ``True`` if the path is a file or a directory with real content.
253
+ """
254
+ if candidate.is_file():
255
+ return True
256
+ if not candidate.is_dir():
257
+ return False
258
+ return any(
259
+ path.is_file() and "__pycache__" not in path.parts
260
+ for path in candidate.rglob("*")
261
+ )
262
+
263
+
264
+ def _resolve_module(module: str, project_root: Path | None) -> tuple[Path, str]:
265
+ """Resolve a ``[wasm].modules`` entry to its ``(root, top)`` for bundling.
266
+
267
+ Resolution order:
268
+
269
+ 1. A **vendored copy** under ``project_root`` (``project_root/module``) that
270
+ carries real content — preserves the historical behavior where a copy
271
+ sitting beside ``app.py`` wins. A stale directory holding only
272
+ ``__pycache__`` is skipped (see :func:`_is_vendored`).
273
+ 2. An **installed** package or module on ``sys.path`` (resolved via
274
+ ``importlib``) — so a dependency declared in the project's environment
275
+ (e.g. an ``uv``-managed ``.venv``) is pulled straight from site-packages
276
+ with no vendored copy committed to the repository.
277
+
278
+ Args:
279
+ module: The top-level module or package name from ``[wasm].modules``.
280
+ project_root: The project directory, when available.
281
+
282
+ Returns:
283
+ A ``(root, top)`` pair where ``root`` is the parent directory archive
284
+ entries are made relative to and ``top`` is the file or directory name
285
+ under it — fed straight into :func:`_zip_tree`.
286
+
287
+ Raises:
288
+ BuildError: If the module is neither vendored under ``project_root`` nor
289
+ importable from the current environment.
290
+ """
291
+ if project_root is not None and _is_vendored(project_root / module):
292
+ return project_root, module
293
+
294
+ try:
295
+ spec = importlib.util.find_spec(module)
296
+ except (ImportError, ValueError):
297
+ spec = None
298
+ if spec is not None:
299
+ locations = list(spec.submodule_search_locations or ())
300
+ if locations:
301
+ package_dir = Path(locations[0]).resolve()
302
+ return package_dir.parent, module
303
+ if spec.origin and spec.origin not in ("built-in", "frozen"):
304
+ origin = Path(spec.origin).resolve()
305
+ return origin.parent, origin.name
306
+
307
+ vendored = f"{project_root / module}" if project_root is not None else "<none>"
308
+ raise BuildError(
309
+ f"wasm module {module!r} not found: no vendored copy at {vendored} "
310
+ f"and not importable from the current environment"
311
+ )
312
+
313
+
239
314
  def _zip_package(
240
315
  dest: Path,
241
316
  *,
@@ -256,11 +331,16 @@ def _zip_package(
256
331
  dest: The ``.zip`` path to write.
257
332
  project_root: The project directory the ``modules`` are relative to.
258
333
  Required when ``modules`` is non-empty.
259
- modules: Project-relative names (files or package dirs) to bundle next to
260
- ``app.py`` (e.g. ``("famacha",)``).
334
+ modules: Names (files or package dirs) to bundle next to ``app.py``
335
+ (e.g. ``("famacha",)``). Each is resolved by :func:`_resolve_module`:
336
+ a vendored copy under ``project_root`` wins, otherwise the module is
337
+ pulled from the installed environment (site-packages) via importlib —
338
+ so a dependency declared in the project's ``.venv`` need not be
339
+ vendored into the repository.
261
340
 
262
341
  Raises:
263
- BuildError: If an expected package part or a declared module is missing.
342
+ BuildError: If an expected package part is missing, or a declared module
343
+ is neither vendored nor importable.
264
344
  """
265
345
  tempestweb_root = _package_dir().parent
266
346
  tempest_core_root = _tempest_core_dir().parent
@@ -268,9 +348,8 @@ def _zip_package(
268
348
  _zip_tree(archive, tempestweb_root, "tempestweb", _WASM_PACKAGE_PARTS)
269
349
  _zip_tree(archive, tempest_core_root, "tempest_core", None)
270
350
  for module in modules:
271
- if project_root is None:
272
- raise BuildError("project_root is required to bundle wasm modules")
273
- _zip_tree(archive, project_root, module, None)
351
+ root, top = _resolve_module(module, project_root)
352
+ _zip_tree(archive, root, top, None)
274
353
 
275
354
 
276
355
  def _build_pwa(out: Path, client: Path, name: str, precache: tuple[str, ...]) -> None:
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes