workpeg 0.97.0__tar.gz → 0.97.2__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 (74) hide show
  1. {workpeg-0.97.0/src/workpeg.egg-info → workpeg-0.97.2}/PKG-INFO +1 -1
  2. {workpeg-0.97.0 → workpeg-0.97.2}/pyproject.toml +1 -1
  3. {workpeg-0.97.0 → workpeg-0.97.2}/src/workpeg/ui/app.py +23 -3
  4. {workpeg-0.97.0 → workpeg-0.97.2}/src/workpeg/ui/compiler/ast_parser.py +12 -2
  5. {workpeg-0.97.0 → workpeg-0.97.2}/src/workpeg/ui/create.py +3 -3
  6. {workpeg-0.97.0 → workpeg-0.97.2}/src/workpeg/ui/runtime/ai.js +1 -1
  7. {workpeg-0.97.0 → workpeg-0.97.2}/src/workpeg/ui/runtime/scaffold.js +22 -0
  8. {workpeg-0.97.0 → workpeg-0.97.2}/src/workpeg/ui/runtime/workpeg-runtime.js +16 -0
  9. {workpeg-0.97.0 → workpeg-0.97.2/src/workpeg.egg-info}/PKG-INFO +1 -1
  10. {workpeg-0.97.0 → workpeg-0.97.2}/LICENSE +0 -0
  11. {workpeg-0.97.0 → workpeg-0.97.2}/MANIFEST.in +0 -0
  12. {workpeg-0.97.0 → workpeg-0.97.2}/README.md +0 -0
  13. {workpeg-0.97.0 → workpeg-0.97.2}/setup.cfg +0 -0
  14. {workpeg-0.97.0 → workpeg-0.97.2}/src/workpeg/__init__.py +0 -0
  15. {workpeg-0.97.0 → workpeg-0.97.2}/src/workpeg/build.py +0 -0
  16. {workpeg-0.97.0 → workpeg-0.97.2}/src/workpeg/cdn.py +0 -0
  17. {workpeg-0.97.0 → workpeg-0.97.2}/src/workpeg/cli.py +0 -0
  18. {workpeg-0.97.0 → workpeg-0.97.2}/src/workpeg/config.py +0 -0
  19. {workpeg-0.97.0 → workpeg-0.97.2}/src/workpeg/context.py +0 -0
  20. {workpeg-0.97.0 → workpeg-0.97.2}/src/workpeg/create_new.py +0 -0
  21. {workpeg-0.97.0 → workpeg-0.97.2}/src/workpeg/run.py +0 -0
  22. {workpeg-0.97.0 → workpeg-0.97.2}/src/workpeg/runtime.py +0 -0
  23. {workpeg-0.97.0 → workpeg-0.97.2}/src/workpeg/submit.py +0 -0
  24. {workpeg-0.97.0 → workpeg-0.97.2}/src/workpeg/templates/__init__.py +0 -0
  25. {workpeg-0.97.0 → workpeg-0.97.2}/src/workpeg/templates/functions/Dockerfile +0 -0
  26. {workpeg-0.97.0 → workpeg-0.97.2}/src/workpeg/templates/functions/LICENSE +0 -0
  27. {workpeg-0.97.0 → workpeg-0.97.2}/src/workpeg/templates/functions/README.md +0 -0
  28. {workpeg-0.97.0 → workpeg-0.97.2}/src/workpeg/templates/functions/app/__init__.py +0 -0
  29. {workpeg-0.97.0 → workpeg-0.97.2}/src/workpeg/templates/functions/app/main.py +0 -0
  30. {workpeg-0.97.0 → workpeg-0.97.2}/src/workpeg/templates/functions/requirements.txt +0 -0
  31. {workpeg-0.97.0 → workpeg-0.97.2}/src/workpeg/ui/__init__.py +0 -0
  32. {workpeg-0.97.0 → workpeg-0.97.2}/src/workpeg/ui/actions.py +0 -0
  33. {workpeg-0.97.0 → workpeg-0.97.2}/src/workpeg/ui/build.py +0 -0
  34. {workpeg-0.97.0 → workpeg-0.97.2}/src/workpeg/ui/compiler/__init__.py +0 -0
  35. {workpeg-0.97.0 → workpeg-0.97.2}/src/workpeg/ui/compiler/ir.py +0 -0
  36. {workpeg-0.97.0 → workpeg-0.97.2}/src/workpeg/ui/compiler/js_generator.py +0 -0
  37. {workpeg-0.97.0 → workpeg-0.97.2}/src/workpeg/ui/fake_cloud.py +0 -0
  38. {workpeg-0.97.0 → workpeg-0.97.2}/src/workpeg/ui/minify.py +0 -0
  39. {workpeg-0.97.0 → workpeg-0.97.2}/src/workpeg/ui/runtime/pegment.css +0 -0
  40. {workpeg-0.97.0 → workpeg-0.97.2}/src/workpeg/ui/scaffold.py +0 -0
  41. {workpeg-0.97.0 → workpeg-0.97.2}/src/workpeg/ui/serve.py +0 -0
  42. {workpeg-0.97.0 → workpeg-0.97.2}/src/workpeg/ui/state.py +0 -0
  43. {workpeg-0.97.0 → workpeg-0.97.2}/src/workpeg/ui/validators.py +0 -0
  44. {workpeg-0.97.0 → workpeg-0.97.2}/src/workpeg/ui/watcher.py +0 -0
  45. {workpeg-0.97.0 → workpeg-0.97.2}/src/workpeg/ui/widgets/__init__.py +0 -0
  46. {workpeg-0.97.0 → workpeg-0.97.2}/src/workpeg/ui/widgets/base.py +0 -0
  47. {workpeg-0.97.0 → workpeg-0.97.2}/src/workpeg/ui/widgets/canvas.py +0 -0
  48. {workpeg-0.97.0 → workpeg-0.97.2}/src/workpeg/ui/widgets/display.py +0 -0
  49. {workpeg-0.97.0 → workpeg-0.97.2}/src/workpeg/ui/widgets/forms.py +0 -0
  50. {workpeg-0.97.0 → workpeg-0.97.2}/src/workpeg/ui/widgets/futures.py +0 -0
  51. {workpeg-0.97.0 → workpeg-0.97.2}/src/workpeg/ui/widgets/gestures.py +0 -0
  52. {workpeg-0.97.0 → workpeg-0.97.2}/src/workpeg/ui/widgets/interactive.py +0 -0
  53. {workpeg-0.97.0 → workpeg-0.97.2}/src/workpeg/ui/widgets/layout.py +0 -0
  54. {workpeg-0.97.0 → workpeg-0.97.2}/src/workpeg/utils.py +0 -0
  55. {workpeg-0.97.0 → workpeg-0.97.2}/src/workpeg.egg-info/SOURCES.txt +0 -0
  56. {workpeg-0.97.0 → workpeg-0.97.2}/src/workpeg.egg-info/dependency_links.txt +0 -0
  57. {workpeg-0.97.0 → workpeg-0.97.2}/src/workpeg.egg-info/entry_points.txt +0 -0
  58. {workpeg-0.97.0 → workpeg-0.97.2}/src/workpeg.egg-info/requires.txt +0 -0
  59. {workpeg-0.97.0 → workpeg-0.97.2}/src/workpeg.egg-info/top_level.txt +0 -0
  60. {workpeg-0.97.0 → workpeg-0.97.2}/tests/test_build.py +0 -0
  61. {workpeg-0.97.0 → workpeg-0.97.2}/tests/test_cdn.py +0 -0
  62. {workpeg-0.97.0 → workpeg-0.97.2}/tests/test_cdn_publish.py +0 -0
  63. {workpeg-0.97.0 → workpeg-0.97.2}/tests/test_changelog.py +0 -0
  64. {workpeg-0.97.0 → workpeg-0.97.2}/tests/test_changelog_guard.py +0 -0
  65. {workpeg-0.97.0 → workpeg-0.97.2}/tests/test_cli.py +0 -0
  66. {workpeg-0.97.0 → workpeg-0.97.2}/tests/test_config.py +0 -0
  67. {workpeg-0.97.0 → workpeg-0.97.2}/tests/test_context.py +0 -0
  68. {workpeg-0.97.0 → workpeg-0.97.2}/tests/test_create_new.py +0 -0
  69. {workpeg-0.97.0 → workpeg-0.97.2}/tests/test_release_script.py +0 -0
  70. {workpeg-0.97.0 → workpeg-0.97.2}/tests/test_run.py +0 -0
  71. {workpeg-0.97.0 → workpeg-0.97.2}/tests/test_runtime.py +0 -0
  72. {workpeg-0.97.0 → workpeg-0.97.2}/tests/test_submit.py +0 -0
  73. {workpeg-0.97.0 → workpeg-0.97.2}/tests/test_utils.py +0 -0
  74. {workpeg-0.97.0 → workpeg-0.97.2}/tests/test_watch_pipeline.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: workpeg
3
- Version: 0.97.0
3
+ Version: 0.97.2
4
4
  Summary: Workpeg function runtime and SDK
5
5
  Author-email: Workpeg <support@workpeg.com>
6
6
  License: MIT
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "workpeg"
7
- version = "0.97.0"
7
+ version = "0.97.2"
8
8
  description = "Workpeg function runtime and SDK"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
@@ -712,6 +712,26 @@ class _Workpeg:
712
712
  """
713
713
  return self._unavailable("get_tables")
714
714
 
715
+ def first_business(self) -> Any:
716
+ """Resolve the signed-in user's primary business — its ``uuid`` string,
717
+ or ``""`` when the user has none.
718
+
719
+ Meant for a bare ``/`` landing that redirects into a business::
720
+
721
+ class Landing(App):
722
+ async def on_mount(self):
723
+ biz = await workpeg.first_business()
724
+ if biz:
725
+ navigate("/b/" + biz)
726
+
727
+ The businesses list is a *session* endpoint the ``workpeg.*`` allowlist
728
+ excludes (only tables, rows and the CDN are reachable), so — unlike
729
+ ``workpeg.get`` — the runtime resolves this one credentialed and
730
+ allowlist-exempt, the same chrome path :class:`Authenticated` uses for
731
+ ``/v1/users/me/``. Compiles to ``wpFirstBusiness``; never runs in Python.
732
+ """
733
+ return self._unavailable("first_business")
734
+
715
735
  # ``*_protected`` variants additionally send the session cookie
716
736
  # (``credentials: "include"``) — the authenticated request, for endpoints
717
737
  # behind a login (the recreation of the React ``fetchProtected`` helper)::
@@ -755,9 +775,9 @@ class _Cookie:
755
775
  if biz:
756
776
  navigate(f"/{biz}")
757
777
  else:
758
- rows = await workpeg.get_protected("/v1/businesses/")
759
- if rows:
760
- navigate(f"/{rows[0]['uuid']}")
778
+ first = await workpeg.first_business()
779
+ if first:
780
+ navigate(f"/{first}")
761
781
 
762
782
  They compile to the runtime's cookie helpers (name/value percent-encoded, so
763
783
  neither can inject extra cookie attributes); never executed in Python.
@@ -74,6 +74,15 @@ _WORKPEG_TABLE_HELPERS = {
74
74
  "delete_row": (2, 2, "workpeg.delete_row(table_uuid, row_id)"),
75
75
  }
76
76
 
77
+ # Curated session/chrome helpers on workpeg.* — no URL, no arbitrary reach.
78
+ # ``first_business`` resolves the signed-in user's primary business uuid: the
79
+ # businesses list is a session endpoint the peg allowlist excludes, so the
80
+ # runtime fetches it credentialed (like Authenticated's /v1/users/me/) rather
81
+ # than through workpeg.get. For the bare "/" landing → redirect to /b/<uuid>.
82
+ _WORKPEG_SESSION_HELPERS = {
83
+ "first_business": (0, 0, "workpeg.first_business()"),
84
+ }
85
+
77
86
 
78
87
  def _is_workpeg_host(url: str) -> bool:
79
88
  """True when *url* targets a Workpeg-owned host — ``workpeg`` as the
@@ -4107,8 +4116,9 @@ class ASTParser:
4107
4116
  # Tables & Rows helpers (get_tables, add_row, …) take no URL — the
4108
4117
  # business id is read from the page URL at runtime, and the runtime
4109
4118
  # builds the endpoint path from the args.
4110
- if method in _WORKPEG_TABLE_HELPERS:
4111
- lo, hi, usage = _WORKPEG_TABLE_HELPERS[method]
4119
+ if method in _WORKPEG_TABLE_HELPERS or method in _WORKPEG_SESSION_HELPERS:
4120
+ lo, hi, usage = (_WORKPEG_TABLE_HELPERS.get(method)
4121
+ or _WORKPEG_SESSION_HELPERS[method])
4112
4122
  # get_rows windows large tables server-side; the two keywords are
4113
4123
  # the standard way to fetch from an unbounded table. The response
4114
4124
  # stays a bare array either way (no pagination envelope).
@@ -52,9 +52,9 @@ class Landing(App):
52
52
  if cached:
53
53
  navigate("/b/" + cached)
54
54
  else:
55
- businesses = await workpeg.get_protected("/v1/businesses/")
56
- if businesses:
57
- navigate("/b/" + businesses[0]["uuid"])
55
+ biz = await workpeg.first_business()
56
+ if biz:
57
+ navigate("/b/" + biz)
58
58
 
59
59
  def build(self):
60
60
  return Authenticated(
@@ -2922,7 +2922,7 @@
2922
2922
  R + " .wpai-tok-number{color:#b5730a}",
2923
2923
  R + " .wpai-tok-fn{color:#3f7bd6}",
2924
2924
  R + " .wpai-tok-const{color:#c0663a}",
2925
- R + " .chat-message__bubble a{color:var(--color-brand-secondary,var(--color-brand-primary));text-decoration:underline}",
2925
+ R + " .chat-message__bubble a{color:var(--color-text-link);text-decoration:underline}",
2926
2926
  R + " .chat-message__actions{display:flex;align-items:center;gap:4px;margin-top:4px;opacity:0;transition:opacity .15s ease}",
2927
2927
  R + " .chat-message--ai:hover .chat-message__actions{opacity:1}",
2928
2928
  R + " .chat-message--ai.wpai-streaming .chat-message__actions{opacity:0}",
@@ -409,12 +409,26 @@
409
409
  return row;
410
410
  }
411
411
 
412
+ // A full-screen "Signing out…" cover, shown the instant Log out is clicked so
413
+ // the DELETE round-trip + redirect never reads as an unresponsive pause. It
414
+ // stays until the redirect navigates away, so there's nothing to remove.
415
+ function showLogoutOverlay() {
416
+ if (typeof document === "undefined" || !document.body) return;
417
+ if (document.getElementById("wps-logout-overlay")) return; // idempotent
418
+ var ov = el("div", "wps-logout-overlay", { attrs: {
419
+ id: "wps-logout-overlay", role: "status", "aria-label": "Signing out" } });
420
+ ov.appendChild(el("div", "wps-spin"));
421
+ ov.appendChild(el("div", "wps-logout-label", { text: "Signing out…" }));
422
+ document.body.appendChild(ov);
423
+ }
424
+
412
425
  // Log out — mirrors space's handlelogOut: DELETE the token (the server drops
413
426
  // the HttpOnly access/refresh cookies), wipe the readable auth cookies, then
414
427
  // bounce to the accounts login (the same redirect the auth gate uses). A
415
428
  // direct credentialed fetch — the workpeg.* client's allowlist (tables /
416
429
  // rows / CDN) intentionally won't reach /users/token/.
417
430
  function logout() {
431
+ showLogoutOverlay(); // immediate feedback before the async pause
418
432
  var api = String(WP().apiBase || "https://api.workpeg.com")
419
433
  .replace(/\/+$/, "");
420
434
  var finish = function () {
@@ -904,6 +918,14 @@
904
918
  "border-top-color:var(--color-text-tertiary);",
905
919
  "animation:wps-spin 0.8s linear infinite}",
906
920
  "@keyframes wps-spin{to{transform:rotate(360deg)}}",
921
+ /* the "Signing out…" cover — an instant full-screen loader on Log out */
922
+ ".wps-logout-overlay{position:fixed;inset:0;z-index:2147483000;",
923
+ "display:flex;flex-direction:column;align-items:center;",
924
+ "justify-content:center;gap:14px;background:var(--color-bg-default);",
925
+ "animation:wps-logout-fade 0.12s ease}",
926
+ ".wps-logout-overlay .wps-spin{width:28px;height:28px;border-width:3px}",
927
+ ".wps-logout-label{color:var(--color-text-secondary);font-size:14px}",
928
+ "@keyframes wps-logout-fade{from{opacity:0}to{opacity:1}}",
907
929
 
908
930
  /* the user / business switcher */
909
931
  ".wps-user{display:flex;align-items:center;width:100%;",
@@ -270,6 +270,22 @@
270
270
  return Workpeg.wpRequest("DELETE", url, data, true);
271
271
  };
272
272
 
273
+ // The signed-in user's businesses is a session/chrome endpoint — the peg
274
+ // client's allowlist deliberately excludes it (as it does /v1/users/me/), so
275
+ // it's resolved HERE, credentialed and allowlist-exempt (a raw Workpeg._request,
276
+ // not wpRequest), exactly like Authenticated's /v1/users/me/ check. It's the
277
+ // one businesses call the chrome doesn't already own — the bare "/" landing,
278
+ // which runs before scaffold.js mounts. Returns the first business's uuid ("" if
279
+ // the user has none), for a redirect to /b/<uuid>.
280
+ Workpeg.wpFirstBusiness = function () {
281
+ var url = Workpeg.apiBase.replace(/\/+$/, "") + "/v1/businesses/";
282
+ return Workpeg._ensureFreshSession().then(function () {
283
+ return Workpeg._request("GET", url, undefined, {}, true);
284
+ }).then(function (list) {
285
+ return (list && list[0] && list[0].uuid) || "";
286
+ });
287
+ };
288
+
273
289
  // The active business id, read from the URL. Pages live under "/b/<id>/...",
274
290
  // so the id is the path segment right after a leading "b" ("" if the page
275
291
  // isn't business-scoped).
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: workpeg
3
- Version: 0.97.0
3
+ Version: 0.97.2
4
4
  Summary: Workpeg function runtime and SDK
5
5
  Author-email: Workpeg <support@workpeg.com>
6
6
  License: MIT
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes