pywire 0.1.0__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 (104) hide show
  1. pywire/__init__.py +2 -0
  2. pywire/cli/__init__.py +1 -0
  3. pywire/cli/generators.py +48 -0
  4. pywire/cli/main.py +309 -0
  5. pywire/cli/tui.py +563 -0
  6. pywire/cli/validate.py +26 -0
  7. pywire/client/.prettierignore +8 -0
  8. pywire/client/.prettierrc +7 -0
  9. pywire/client/build.mjs +73 -0
  10. pywire/client/eslint.config.js +46 -0
  11. pywire/client/package.json +39 -0
  12. pywire/client/pnpm-lock.yaml +2971 -0
  13. pywire/client/src/core/app.ts +263 -0
  14. pywire/client/src/core/dom-updater.test.ts +78 -0
  15. pywire/client/src/core/dom-updater.ts +321 -0
  16. pywire/client/src/core/index.ts +5 -0
  17. pywire/client/src/core/transport-manager.test.ts +179 -0
  18. pywire/client/src/core/transport-manager.ts +159 -0
  19. pywire/client/src/core/transports/base.ts +122 -0
  20. pywire/client/src/core/transports/http.ts +142 -0
  21. pywire/client/src/core/transports/index.ts +13 -0
  22. pywire/client/src/core/transports/websocket.ts +97 -0
  23. pywire/client/src/core/transports/webtransport.ts +149 -0
  24. pywire/client/src/dev/dev-app.ts +93 -0
  25. pywire/client/src/dev/error-trace.test.ts +97 -0
  26. pywire/client/src/dev/error-trace.ts +76 -0
  27. pywire/client/src/dev/index.ts +4 -0
  28. pywire/client/src/dev/status-overlay.ts +63 -0
  29. pywire/client/src/events/handler.test.ts +318 -0
  30. pywire/client/src/events/handler.ts +454 -0
  31. pywire/client/src/pywire.core.ts +22 -0
  32. pywire/client/src/pywire.dev.ts +27 -0
  33. pywire/client/tsconfig.json +17 -0
  34. pywire/client/vitest.config.ts +15 -0
  35. pywire/compiler/__init__.py +6 -0
  36. pywire/compiler/ast_nodes.py +304 -0
  37. pywire/compiler/attributes/__init__.py +6 -0
  38. pywire/compiler/attributes/base.py +24 -0
  39. pywire/compiler/attributes/conditional.py +37 -0
  40. pywire/compiler/attributes/events.py +55 -0
  41. pywire/compiler/attributes/form.py +37 -0
  42. pywire/compiler/attributes/loop.py +75 -0
  43. pywire/compiler/attributes/reactive.py +34 -0
  44. pywire/compiler/build.py +28 -0
  45. pywire/compiler/build_artifacts.py +342 -0
  46. pywire/compiler/codegen/__init__.py +5 -0
  47. pywire/compiler/codegen/attributes/__init__.py +6 -0
  48. pywire/compiler/codegen/attributes/base.py +19 -0
  49. pywire/compiler/codegen/attributes/events.py +35 -0
  50. pywire/compiler/codegen/directives/__init__.py +6 -0
  51. pywire/compiler/codegen/directives/base.py +16 -0
  52. pywire/compiler/codegen/directives/path.py +53 -0
  53. pywire/compiler/codegen/generator.py +2341 -0
  54. pywire/compiler/codegen/template.py +2178 -0
  55. pywire/compiler/directives/__init__.py +7 -0
  56. pywire/compiler/directives/base.py +20 -0
  57. pywire/compiler/directives/component.py +33 -0
  58. pywire/compiler/directives/context.py +93 -0
  59. pywire/compiler/directives/layout.py +49 -0
  60. pywire/compiler/directives/no_spa.py +24 -0
  61. pywire/compiler/directives/path.py +71 -0
  62. pywire/compiler/directives/props.py +88 -0
  63. pywire/compiler/exceptions.py +19 -0
  64. pywire/compiler/interpolation/__init__.py +6 -0
  65. pywire/compiler/interpolation/base.py +28 -0
  66. pywire/compiler/interpolation/jinja.py +272 -0
  67. pywire/compiler/parser.py +750 -0
  68. pywire/compiler/paths.py +29 -0
  69. pywire/compiler/preprocessor.py +43 -0
  70. pywire/core/wire.py +119 -0
  71. pywire/py.typed +0 -0
  72. pywire/runtime/__init__.py +7 -0
  73. pywire/runtime/aioquic_server.py +194 -0
  74. pywire/runtime/app.py +889 -0
  75. pywire/runtime/compile_error_page.py +195 -0
  76. pywire/runtime/debug.py +203 -0
  77. pywire/runtime/dev_server.py +434 -0
  78. pywire/runtime/dev_server.py.broken +268 -0
  79. pywire/runtime/error_page.py +64 -0
  80. pywire/runtime/error_renderer.py +23 -0
  81. pywire/runtime/escape.py +23 -0
  82. pywire/runtime/files.py +40 -0
  83. pywire/runtime/helpers.py +97 -0
  84. pywire/runtime/http_transport.py +253 -0
  85. pywire/runtime/loader.py +272 -0
  86. pywire/runtime/logging.py +72 -0
  87. pywire/runtime/page.py +384 -0
  88. pywire/runtime/pydantic_integration.py +52 -0
  89. pywire/runtime/router.py +229 -0
  90. pywire/runtime/server.py +25 -0
  91. pywire/runtime/style_collector.py +31 -0
  92. pywire/runtime/upload_manager.py +76 -0
  93. pywire/runtime/validation.py +449 -0
  94. pywire/runtime/websocket.py +665 -0
  95. pywire/runtime/webtransport_handler.py +195 -0
  96. pywire/templates/error/404.html +11 -0
  97. pywire/templates/error/500.html +38 -0
  98. pywire/templates/error/base.html +207 -0
  99. pywire/templates/error/compile_error.html +31 -0
  100. pywire-0.1.0.dist-info/METADATA +50 -0
  101. pywire-0.1.0.dist-info/RECORD +104 -0
  102. pywire-0.1.0.dist-info/WHEEL +4 -0
  103. pywire-0.1.0.dist-info/entry_points.txt +2 -0
  104. pywire-0.1.0.dist-info/licenses/LICENSE +201 -0
@@ -0,0 +1,195 @@
1
+ """WebTransport handler using ASGI standard.
2
+
3
+ Handles 'webtransport' scope type from Hypercorn.
4
+ """
5
+
6
+ import json
7
+ from typing import Any, Dict, Set
8
+
9
+ from pywire.runtime.page import BasePage
10
+
11
+
12
+ class WebTransportHandler:
13
+ """Handles WebTransport connections."""
14
+
15
+ def __init__(self, app: Any) -> None:
16
+ self.app = app
17
+ # Store active sessions/connections
18
+ # For WebTransport, the 'scope' is the connection identifier
19
+ self.active_connections: Set[Any] = set()
20
+
21
+ # Map connection -> current page instance
22
+ self.connection_pages: Dict[Any, BasePage] = {}
23
+
24
+ async def handle(self, scope: dict[str, Any], receive: Any, send: Any) -> None:
25
+ """Handle ASGI webtransport scope."""
26
+ print("DEBUG: WebTransport handler started")
27
+ # Active streams buffer: stream_id -> bytes
28
+ streams: Dict[int, bytearray] = {}
29
+
30
+ # 1. Wait for connection request
31
+ try:
32
+ message = await receive()
33
+ print(f"DEBUG: WebTransport received initial message: {message['type']}")
34
+ if message["type"] != "webtransport.connect":
35
+ print(f"DEBUG: Unexpected message type: {message['type']}")
36
+ return
37
+ except Exception as e:
38
+ print(f"DEBUG: Error receiving connect message: {e}")
39
+ return
40
+
41
+ # 2. Accept connection
42
+ await send({"type": "webtransport.accept"})
43
+ print("DEBUG: WebTransport connection accepted")
44
+
45
+ # Register connection (using the receive channel as ID or scope object)
46
+ # Since scope is mutable dictionary, we use its id() or just the object if stable
47
+ connection_id = id(scope)
48
+ self.active_connections.add(connection_id)
49
+
50
+ try:
51
+ while True:
52
+ message = await receive()
53
+ msg_type = message["type"]
54
+ # print(f"DEBUG: Received WT message: {msg_type}")
55
+
56
+ if msg_type == "webtransport.stream.connect":
57
+ # New bidirectional stream opened by client
58
+ stream_id = message["stream_id"]
59
+ streams[stream_id] = bytearray()
60
+ # print(f"DEBUG: Stream {stream_id} connected")
61
+
62
+ elif msg_type == "webtransport.stream.receive":
63
+ stream_id = message["stream_id"]
64
+ data = message.get("data", b"")
65
+
66
+ if stream_id not in streams:
67
+ # Stream might have been accepted implicitly or we missed connect
68
+ streams[stream_id] = bytearray()
69
+
70
+ streams[stream_id].extend(data)
71
+
72
+ # Check if stream is finished (some impls use 'more_body', others 'fin')
73
+ # Hypercorn uses 'more_body' (True if more coming)
74
+ more_body = message.get("more_body", False)
75
+
76
+ if not more_body:
77
+ # Full message received
78
+ payload = streams[stream_id]
79
+ del streams[stream_id] # Clear buffer
80
+
81
+ # Process message
82
+ try:
83
+ json_data = json.loads(payload.decode("utf-8"))
84
+ await self._handle_message(
85
+ json_data, scope, send, stream_id
86
+ )
87
+ except Exception as e:
88
+ print(f"WebTransport message error: {e}")
89
+
90
+ elif msg_type == "webtransport.disconnect":
91
+ break
92
+
93
+ except Exception as e:
94
+ print(f"WebTransport handler error: {e}")
95
+ finally:
96
+ self.active_connections.discard(connection_id)
97
+ if connection_id in self.connection_pages:
98
+ del self.connection_pages[connection_id]
99
+
100
+ async def _handle_message(
101
+ self, data: dict[str, Any], scope: dict[str, Any], send: Any, stream_id: int
102
+ ) -> None:
103
+ """Handle decoded JSON message."""
104
+ msg_type = data.get("type")
105
+ connection_id = id(scope)
106
+
107
+ if msg_type == "event":
108
+ # Handle event
109
+ if connection_id in self.connection_pages:
110
+ page = self.connection_pages[connection_id]
111
+ handler_name = data.get("handler")
112
+ event_data = data.get("data", {})
113
+
114
+ try:
115
+ if handler_name and isinstance(handler_name, str):
116
+ # Execute handler
117
+ response = await page.handle_event(handler_name, event_data)
118
+ else:
119
+ raise ValueError("Invalid handler name")
120
+
121
+ # If response is HTML, send update
122
+ if hasattr(response, "body"):
123
+ html = bytes(response.body).decode("utf-8")
124
+ response_data = {"type": "update", "html": html}
125
+ await self._send_response(send, stream_id, response_data)
126
+
127
+ except Exception as e:
128
+ # Send error response (no print - response is sufficient)
129
+ await self._send_response(
130
+ send, stream_id, {"type": "error", "error": str(e)}
131
+ )
132
+
133
+ elif msg_type == "init":
134
+ # Initialize page for this connection (similar to WS)
135
+ # Parse path and instantiate page
136
+ path = data.get("path", "/")
137
+ match = self.app.router.match(path)
138
+ if match:
139
+ page_class, params, variant_name = match
140
+ # Mock request object? Or extract from scope
141
+ # We need a Request-like object for Page init
142
+ from starlette.requests import Request
143
+
144
+ request = Request(scope)
145
+ query = dict(request.query_params)
146
+
147
+ # Build path info dict
148
+ path_info = {}
149
+ if hasattr(page_class, "__routes__"):
150
+ for name in page_class.__routes__.keys():
151
+ path_info[name] = name == variant_name
152
+ elif hasattr(page_class, "__route__"):
153
+ path_info["main"] = True
154
+
155
+ # Build URL helper
156
+ from pywire.runtime.router import URLHelper
157
+
158
+ url_helper = None
159
+ if hasattr(page_class, "__routes__"):
160
+ url_helper = URLHelper(page_class.__routes__)
161
+
162
+ page = page_class(
163
+ request, params, query, path=path_info, url=url_helper
164
+ )
165
+ if hasattr(self.app, "get_user"):
166
+ page.user = self.app.get_user(request)
167
+
168
+ self.connection_pages[connection_id] = page
169
+
170
+ async def _send_response(
171
+ self, send: Any, stream_id: int, data: dict[str, Any]
172
+ ) -> None:
173
+ """Send response back on the same stream."""
174
+ payload = json.dumps(data).encode("utf-8")
175
+ await send(
176
+ {
177
+ "type": "webtransport.stream.send",
178
+ "stream_id": stream_id,
179
+ "data": payload,
180
+ "finish": True, # Close the stream after sending
181
+ }
182
+ )
183
+
184
+ async def broadcast_reload(self) -> None:
185
+ """Broadcast reload to all active WebTransport connections."""
186
+ # For broadcast, we must initiate a NEW stream for each connection
187
+ # But we don't have reference to 'send' callable for each connection here!
188
+ # The 'send' is only available inside the 'handle' loop.
189
+
190
+ # This is a problem with the simple loop approach.
191
+ # We need a way to inject messages into the handle loops.
192
+ pass
193
+
194
+ # Solution: Use an asyncio.Queue for each connection, and have the handle loop
195
+ # wait for EITHER 'receive()' OR 'queue.get()'.
@@ -0,0 +1,11 @@
1
+ {% extends "error/base.html" %}
2
+
3
+ {% block title %}{{ title }} - PyWire{% endblock %}
4
+
5
+ {% block header %}{{ title }}{% endblock %}
6
+
7
+ {% block content %}
8
+ <div class="error-location error-404">
9
+ <div class="exc-msg">{{ message }}</div>
10
+ </div>
11
+ {% endblock %}
@@ -0,0 +1,38 @@
1
+ {% extends "error/base.html" %}
2
+
3
+ {% block title %}{{ exc_type }}{% endblock %}
4
+
5
+ {% block header %}{{ exc_type }}{% endblock %}
6
+
7
+ {% block content %}
8
+ <div class="exc-msg">{{ exc_msg }}</div>
9
+
10
+ {% if is_framework_error %}
11
+ <div class="alert alert-warning">
12
+ <strong>Potential Framework Bug</strong>
13
+ <p>This error seems to originate from within PyWire. If you believe this is a bug,
14
+ please <a href="{{ github_url }}" target="_blank" class="issue-link">open an issue
15
+ on GitHub</a>.</p>
16
+ </div>
17
+ {% endif %}
18
+
19
+ <h2>Traceback</h2>
20
+ <div class="frames">
21
+ {% for frame in frames %}
22
+ <div class="frame {% if frame.is_user_code %}frame-user{% else %}frame-vendor{% endif %}">
23
+ <div class="frame-header">
24
+ <span class="func">{{ frame.func_name }}</span>
25
+ <span class="file">{{ frame.short_filename }}:{{ frame.lineno }}</span>
26
+ </div>
27
+ <div class="code-context">
28
+ {% for line in frame.context %}
29
+ <div class="{% if line.is_current %}line-current{% else %}line{% endif %}">
30
+ <span class="line-num">{{ line.num }}</span>
31
+ <span class="code">{{ line.content }}</span>
32
+ </div>
33
+ {% endfor %}
34
+ </div>
35
+ </div>
36
+ {% endfor %}
37
+ </div>
38
+ {% endblock %}
@@ -0,0 +1,207 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+
4
+ <head>
5
+ <title>{% block title %}PyWire Error{% endblock %}</title>
6
+ <style>
7
+ :root {
8
+ --bg-color: #1a1a1a;
9
+ --text-color: #e0e0e0;
10
+ --accent-color: #ff6b6b;
11
+ --secondary-color: #aaa;
12
+ --code-bg: #222;
13
+ --border-color: #333;
14
+ --highlight-bg: #3c1e1e;
15
+ --highlight-text: #ffcccc;
16
+ --highlight-border: #ff6b6b;
17
+ --file-color: #ffd43b;
18
+ }
19
+
20
+ body {
21
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
22
+ background: var(--bg-color);
23
+ color: var(--text-color);
24
+ margin: 0;
25
+ padding: 20px;
26
+ line-height: 1.5;
27
+ }
28
+
29
+ .container {
30
+ max-width: 1000px;
31
+ margin: 0 auto;
32
+ }
33
+
34
+ .header-block {
35
+ border-bottom: 1px solid var(--border-color);
36
+ padding-bottom: 20px;
37
+ margin-bottom: 20px;
38
+ }
39
+
40
+ h1 {
41
+ color: var(--accent-color);
42
+ font-size: 24px;
43
+ margin-bottom: 5px;
44
+ margin-top: 0;
45
+ }
46
+
47
+ h3 {
48
+ color: var(--secondary-color);
49
+ font-size: 16px;
50
+ margin-top: 30px;
51
+ margin-bottom: 10px;
52
+ }
53
+
54
+ .exc-msg {
55
+ font-size: 18px;
56
+ color: #fff;
57
+ margin-bottom: 20px;
58
+ font-weight: 500;
59
+ white-space: pre-wrap;
60
+ font-family: monospace;
61
+ }
62
+
63
+ /* Code Context */
64
+ .error-location {
65
+ background: #2d2d2d;
66
+ border-radius: 8px;
67
+ padding: 15px;
68
+ margin-bottom: 20px;
69
+ border-left: 4px solid var(--accent-color);
70
+ }
71
+
72
+ .file-info {
73
+ color: var(--file-color);
74
+ font-family: monospace;
75
+ font-size: 14px;
76
+ margin-bottom: 10px;
77
+ }
78
+
79
+ .code-context {
80
+ padding: 10px 0;
81
+ background: var(--code-bg);
82
+ font-family: "Fira Code", monospace;
83
+ font-size: 13px;
84
+ overflow-x: auto;
85
+ border-radius: 4px;
86
+ }
87
+
88
+ .line {
89
+ padding: 2px 15px;
90
+ color: #888;
91
+ display: flex;
92
+ }
93
+
94
+ .line-current {
95
+ padding: 2px 15px;
96
+ background: var(--highlight-bg);
97
+ color: var(--highlight-text);
98
+ display: flex;
99
+ border-left: 3px solid var(--highlight-border);
100
+ }
101
+
102
+ .line-num {
103
+ width: 40px;
104
+ text-align: right;
105
+ margin-right: 15px;
106
+ opacity: 0.5;
107
+ user-select: none;
108
+ }
109
+
110
+ .code {
111
+ white-space: pre;
112
+ }
113
+
114
+ /* Traceback */
115
+ .traceback-section {
116
+ margin-top: 20px;
117
+ }
118
+
119
+ .traceback {
120
+ background: var(--code-bg);
121
+ padding: 15px;
122
+ border-radius: 8px;
123
+ font-size: 12px;
124
+ overflow-x: auto;
125
+ color: #ccc;
126
+ white-space: pre-wrap;
127
+ word-break: break-word;
128
+ }
129
+
130
+ .frames {
131
+ display: flex;
132
+ flex-direction: column;
133
+ gap: 15px;
134
+ }
135
+
136
+ .frame {
137
+ background: #2d2d2d;
138
+ border-radius: 8px;
139
+ overflow: hidden;
140
+ border: 1px solid var(--border-color);
141
+ }
142
+
143
+ .frame-user {
144
+ border-left: 4px solid #69db7c;
145
+ }
146
+
147
+ .frame-vendor {
148
+ border-left: 4px solid #74c0fc;
149
+ opacity: 0.8;
150
+ }
151
+
152
+ .frame-header {
153
+ padding: 10px 15px;
154
+ background: var(--border-color);
155
+ display: flex;
156
+ justify-content: space-between;
157
+ font-family: monospace;
158
+ font-size: 14px;
159
+ }
160
+
161
+ .func {
162
+ color: var(--file-color);
163
+ font-weight: bold;
164
+ }
165
+
166
+ .file {
167
+ color: var(--secondary-color);
168
+ }
169
+
170
+ /* Alerts */
171
+ .alert {
172
+ background: #3d2800;
173
+ border: 1px solid #fcc419;
174
+ color: #fcc419;
175
+ padding: 15px;
176
+ border-radius: 8px;
177
+ margin-bottom: 20px;
178
+ }
179
+
180
+ .issue-link {
181
+ color: var(--file-color);
182
+ text-decoration: underline;
183
+ }
184
+
185
+ /* 404 Specifics */
186
+ .error-404 {
187
+ border-left: 6px solid var(--accent-color);
188
+ }
189
+ </style>
190
+ </head>
191
+
192
+ <body>
193
+ <div class="container">
194
+ <div class="header-block">
195
+ <h1>{% block header %}{% endblock %}</h1>
196
+ </div>
197
+
198
+ {% block content %}{% endblock %}
199
+
200
+ </div>
201
+ <!-- Client Script -->
202
+ {% if script_url %}
203
+ <script src="{{ script_url }}"></script>
204
+ {% endif %}
205
+ </body>
206
+
207
+ </html>
@@ -0,0 +1,31 @@
1
+ {% extends "error/base.html" %}
2
+
3
+ {% block title %}PyWire Syntax Error{% endblock %}
4
+
5
+ {% block header %}PyWire Syntax Error{% endblock %}
6
+
7
+ {% block content %}
8
+ <div class="error-location">
9
+ <div class="file-info">{{ file_display }}{% if error_line %}:{{ error_line }}{% endif %}</div>
10
+ <div class="exc-msg">{{ error_message }}</div>
11
+ </div>
12
+
13
+ {% if context_lines %}
14
+ <div class="code-context">
15
+ {% for line in context_lines %}
16
+ <div class="{% if line.is_current %}line-current{% else %}line{% endif %}">
17
+ <span class="line-num">{{ line.num }}</span>
18
+ <span class="code">{{ line.content }}</span>
19
+ </div>
20
+ {% endfor %}
21
+ </div>
22
+ {% endif %}
23
+
24
+ {% if traceback_text %}
25
+ <div class="traceback-section">
26
+ <h3>Full Traceback</h3>
27
+ <pre class="traceback">{{ traceback_text }}</pre>
28
+ </div>
29
+ {% endif %}
30
+
31
+ {% endblock %}
@@ -0,0 +1,50 @@
1
+ Metadata-Version: 2.4
2
+ Name: pywire
3
+ Version: 0.1.0
4
+ Summary: HTML-over-the-wire Python web framework
5
+ Author-email: Reece Holmdahl <reece@pywire.dev>
6
+ License-File: LICENSE
7
+ Requires-Python: >=3.11
8
+ Requires-Dist: jinja2>=3.1.0
9
+ Requires-Dist: lxml>=4.9.0
10
+ Requires-Dist: msgpack>=1.0.0
11
+ Requires-Dist: pydantic>=2.0.0
12
+ Requires-Dist: rich-click>=1.9.6
13
+ Requires-Dist: starlette>=0.35.0
14
+ Requires-Dist: textual>=7.4.0
15
+ Requires-Dist: uvicorn[standard]>=0.27.0
16
+ Requires-Dist: watchfiles>=0.21.0
17
+ Provides-Extra: dev
18
+ Requires-Dist: black>=23.0.0; extra == 'dev'
19
+ Requires-Dist: coverage>=7.3.0; extra == 'dev'
20
+ Requires-Dist: httpx>=0.23.0; extra == 'dev'
21
+ Requires-Dist: msgpack-types; extra == 'dev'
22
+ Requires-Dist: mypy>=1.7.0; extra == 'dev'
23
+ Requires-Dist: pytest-asyncio>=0.21.0; extra == 'dev'
24
+ Requires-Dist: pytest-cov>=4.1.0; extra == 'dev'
25
+ Requires-Dist: pytest>=7.4.0; extra == 'dev'
26
+ Requires-Dist: ruff>=0.1.0; extra == 'dev'
27
+ Requires-Dist: tox-uv>=1.0.0; extra == 'dev'
28
+ Requires-Dist: tox>=4.0.0; extra == 'dev'
29
+ Provides-Extra: http3
30
+ Requires-Dist: aioquic>=0.9.0; extra == 'http3'
31
+ Requires-Dist: hypercorn>=0.14.0; extra == 'http3'
32
+ Description-Content-Type: text/markdown
33
+
34
+ # pywire Core
35
+
36
+ The core framework for pywire.
37
+
38
+ <!-- SUPPORT_MESSAGE_TEMPLATE_START -->
39
+ ## ❤️ Support pywire
40
+
41
+ If pywire is helping you build, consider supporting the project. Donations cover documentation hosting, CI/CD runners, and the caffeine required for development.
42
+
43
+ [![GitHub Sponsor](https://img.shields.io/badge/Sponsor-pywire-ea4aaa?style=for-the-badge&logo=github-sponsors)](https://github.com/sponsors/pywire)
44
+ [![Ko-Fi](https://img.shields.io/badge/Ko--fi-reecelikesramen-ff5e5b?style=for-the-badge&logo=ko-fi)](https://ko-fi.com/reecelikesramen)
45
+
46
+ ### Why sponsor?
47
+ * 🚀 Faster development of the core framework.
48
+ * 📖 Better docs and community examples.
49
+ * 🔧 Integration research.
50
+ <!-- SUPPORT_MESSAGE_TEMPLATE_END -->
@@ -0,0 +1,104 @@
1
+ pywire/__init__.py,sha256=Vnf8jVQs1hz9PGw9qtkvz_5SGhuLpasPeDFAuDixH7w,78
2
+ pywire/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
+ pywire/cli/__init__.py,sha256=SNDCMbWNEM6_UNFDVB666srR-6Ak_HKLAX2Umy2O85I,18
4
+ pywire/cli/generators.py,sha256=5tgXdhGJua9PhR8FkOxWaK9vHvOuFHdXOf3qamB7f3Q,1009
5
+ pywire/cli/main.py,sha256=cj3zBsem5Me3j3rWeLF1wUeTNGM_akX56FgWGhBuiS0,9082
6
+ pywire/cli/tui.py,sha256=USInBXWUUc8BpihqTdtnRrPJtW3U9c7hFBMRup0mkGs,21714
7
+ pywire/cli/validate.py,sha256=CPUQjFpoPcwpvAx3HvQ8Vq_w_suJ07n3oOhXKmKFwB4,769
8
+ pywire/client/.prettierignore,sha256=TKRNv7wugFj9ig2T6SdoCZxMi_l9X-ninE8j72JdFzM,87
9
+ pywire/client/.prettierrc,sha256=NwmnhqxkL9ATCDtQVOrgKBHtpN8L9niFT2j_SYmImzw,107
10
+ pywire/client/build.mjs,sha256=IDiYOfeHoKy7jwlIJHT5Xo2GfxWcI9-p_jTZaHI1mWk,1944
11
+ pywire/client/eslint.config.js,sha256=QxkvVaKeMnqSkPGDSjxQFvgfcyPWMbzct0zeMnpzQ4o,992
12
+ pywire/client/package.json,sha256=K4pQlex2QKefhkANWbpoBZhpOoveRACp6BkjmWKMPsk,1130
13
+ pywire/client/pnpm-lock.yaml,sha256=fPLYdbqVkvTTX6QheVLy0_32RHwWn90M9v6pAfondKw,78073
14
+ pywire/client/tsconfig.json,sha256=ZQkpS8rFm9HGjOrklve2YNuELCa4oqPnc5n02GoCuKQ,420
15
+ pywire/client/vitest.config.ts,sha256=8W1fK9f_o1_8k46oi223IlZlKSSkiyM_EdVHDxmNNRs,393
16
+ pywire/client/src/pywire.core.ts,sha256=iozCpa5g_N5xa7DNpUxW3M7Ov_NGNfxYnPdgmdisfMc,614
17
+ pywire/client/src/pywire.dev.ts,sha256=G0FOQOimUY-J8H_cFmYSoc9tSrv6TvwiDKD3EcUA-Q0,674
18
+ pywire/client/src/core/app.ts,sha256=WYfxWuSqVkA38VoPr8Cm59xXzW2tC_Bk62rgUopA36k,7401
19
+ pywire/client/src/core/dom-updater.test.ts,sha256=PsiK0EL3P2JYdCpjitZoZpPc2oOi0DJqjV-uOEw8FWo,2571
20
+ pywire/client/src/core/dom-updater.ts,sha256=rL80LVC__240bgU_PjpJDEnzmxx2wMDG_kqQK9Q6Q7A,9687
21
+ pywire/client/src/core/index.ts,sha256=tT_R-J_TtM_Fovm103J2w6cB-oiQZc9r1wViZ7wuV3A,215
22
+ pywire/client/src/core/transport-manager.test.ts,sha256=UbQrYgSXXj1j99mfCeO49rUU5U3grmNglfq28bQapbc,5534
23
+ pywire/client/src/core/transport-manager.ts,sha256=6_SLITzqnhGnwwvwYG5opd_y-1kVcpQEjMa_pUbAUhg,4433
24
+ pywire/client/src/core/transports/base.ts,sha256=fjgIUc1vQqPd3XHtbQ9S8rOKmDP8mrSg9fx4sIc_rqY,2825
25
+ pywire/client/src/core/transports/http.ts,sha256=4ySlDl7KXgyJbnWF6wB_yxyuvJRBfXeB16PbFucZ3RY,4070
26
+ pywire/client/src/core/transports/index.ts,sha256=CZZPHIOR7bRQmtrbDAvIXXR8O58VI38sEonCVubEkZQ,295
27
+ pywire/client/src/core/transports/websocket.ts,sha256=hVSykmEv2zCd1nITQfUHe1QhPiuIBOyYcB31iAH-r6c,2615
28
+ pywire/client/src/core/transports/webtransport.ts,sha256=PCsfC2DKWDPzlssjP4z6QmkfI44Tuuy3PVIeJQSF80M,4077
29
+ pywire/client/src/dev/dev-app.ts,sha256=8VvWzo9k7PH-rppn9VqEGNyn8uHVKDTY3w0NNFR7yjk,2494
30
+ pywire/client/src/dev/error-trace.test.ts,sha256=11zvMqrOpDrdQXYtqvffUPStPxQcUOYA-3jWNfMFcPE,3332
31
+ pywire/client/src/dev/error-trace.ts,sha256=g8mZmqG6jlIZ5lBFrBdC-c74dxcvkoUzDgZxesF2uqg,2465
32
+ pywire/client/src/dev/index.ts,sha256=-jQKnDjX_ANGlRrVuRRlwqocIMzUYNbN5aZIeo8SFhs,162
33
+ pywire/client/src/dev/status-overlay.ts,sha256=S6atJzQ7JqHXMBpuf2gIzXlOLyySV1vnJVXuhfp3jpU,1664
34
+ pywire/client/src/events/handler.test.ts,sha256=M8kmzf5cHY8NvlOaJR3B-2lMyNc96mwAYpgJH_DsEH0,10289
35
+ pywire/client/src/events/handler.ts,sha256=VxTjGQZ88tgdePJ6K3rwNbPgDptgi1On-tyQ1_uS4Tw,13788
36
+ pywire/compiler/__init__.py,sha256=7tIdJ8qi5lcj7R7scIwIiE-Tmi7YZknThZArIdo95MQ,177
37
+ pywire/compiler/ast_nodes.py,sha256=vdHtoJQvoxF_I0RmV6gs1_zd6AhyY4dCpHmqgh73Gi8,8276
38
+ pywire/compiler/build.py,sha256=OB-tmsSOtnNcteUvD01MSaeTI4_lBsUHVe4xITYWa_g,800
39
+ pywire/compiler/build_artifacts.py,sha256=OxYgooTzy95DRwaAC26s-noaevvVwVaix-KYAQOtDM0,11102
40
+ pywire/compiler/exceptions.py,sha256=KyvpMI17oiVSz3h2osDaa4uCMNPKSTCr1DhVYDVU85o,540
41
+ pywire/compiler/parser.py,sha256=VFgZs_YkyVlRuu-jzcXqKXfu_jHzN5-ZE6RBupGWbVE,29425
42
+ pywire/compiler/paths.py,sha256=9TLKH-xnTBmthdpQcCDJ7UrGlIyLAuGDaVrc4qI6TgA,739
43
+ pywire/compiler/preprocessor.py,sha256=AyMrfLpjFF-yvMYQn67sCe_G3OSeK4-Wrdh0I_0RAfU,1509
44
+ pywire/compiler/attributes/__init__.py,sha256=umzZ03Mmw-LrBBNue-itAVORKhcpG-f-LzKSVH4lCGs,208
45
+ pywire/compiler/attributes/base.py,sha256=3eJh7bmi2Y33VRPnD90-s9QMEdfbuNKIxv6F7AU_pK8,697
46
+ pywire/compiler/attributes/conditional.py,sha256=R746Sz-rz3-nLr-Yj71q8yNAz1-eF_5Eg8cjKXwWlAo,1328
47
+ pywire/compiler/attributes/events.py,sha256=o6m59fCPfcuCJJp6rPdIVFV4BFni8IVluhVa4c494Q4,1747
48
+ pywire/compiler/attributes/form.py,sha256=5NC8PsBP7xfSKvyV5ANLShgSl6Eszq_fHZ5NG-VCzcw,1100
49
+ pywire/compiler/attributes/loop.py,sha256=1UL_eBayJOxkDJrO1mhvNnA6UnVs7-riW51XFDqX6Tk,2621
50
+ pywire/compiler/attributes/reactive.py,sha256=vz0TXRsp_mA9iNh1WcWpiA5g6ZpHNMYiUZB-i9B_tJE,1273
51
+ pywire/compiler/codegen/__init__.py,sha256=RoW5xrX7mYycSxHPnS0-J46IwWGIiWF9EeJYkx1p4LI,121
52
+ pywire/compiler/codegen/generator.py,sha256=dZlXjarfNJ7xL3qvSK9XlnsiU6i-IYjyT1Tttt3uqHw,92851
53
+ pywire/compiler/codegen/template.py,sha256=jvhmY9zrMymnq44RsDRqjCeifxNz_Z3d91026Hfw3QM,90713
54
+ pywire/compiler/codegen/attributes/__init__.py,sha256=0wHMipg7PG2bpRjfmLkjIyYWwZTHbSJCevMoZSpmyFs,236
55
+ pywire/compiler/codegen/attributes/base.py,sha256=pGZHCEZ6d3NTWwQMZ_ZKxofz2aEWDw5Kwm_axL7Gi2g,577
56
+ pywire/compiler/codegen/attributes/events.py,sha256=OTIcvreDty4DVEGcuuqmpjD_QREZJe1imcdrB7Hl65o,1354
57
+ pywire/compiler/codegen/directives/__init__.py,sha256=4TD0QmOKduxkTWPuXb1GDcp8nPEzsVLvwWf__xvwp3E,232
58
+ pywire/compiler/codegen/directives/base.py,sha256=4QodSoVsxJvhZ4oO7Phk_jgy-AopZsMmxH8ixnhypiQ,392
59
+ pywire/compiler/codegen/directives/path.py,sha256=QmFJszBZvXTFOrpHrDwmAubfW7eoFFKNN5OAJ6yqMvs,1751
60
+ pywire/compiler/directives/__init__.py,sha256=6QioQwB0EMVlybvwZZFQO10Kayuc0SgZTs-mL_vnQMM,295
61
+ pywire/compiler/directives/base.py,sha256=luBrDyNPzkcFvhzlIIbfv66po7Ep0PuHQ_jMFNahXjo,581
62
+ pywire/compiler/directives/component.py,sha256=Ez2jT6L1yjWwy3_PVMeITgjplrCblozsh7XR2N-rm8U,1029
63
+ pywire/compiler/directives/context.py,sha256=OhZ1eDsiEU_d8jNbWhkIT5e4Jo245fQwnpaXwLr0Jzk,3453
64
+ pywire/compiler/directives/layout.py,sha256=MEF4IeV2x9QPcEyLfc6-MvKlVqq-BeBlzoWG9GuCPWA,1435
65
+ pywire/compiler/directives/no_spa.py,sha256=kuQPZr6IKWLYMgApNV8ruLmFIOvPUDEOdJuCdBKGFdU,749
66
+ pywire/compiler/directives/path.py,sha256=KGVPb4kgFWs4zXtHJ2PweYTnT27iw19bhZjMoksM0ok,2388
67
+ pywire/compiler/directives/props.py,sha256=loeXOtt1zYrtEIGBUtPah37DEbloiN5VAi2s98zOGvY,3440
68
+ pywire/compiler/interpolation/__init__.py,sha256=A-v9HVAIUB-QX6j5FFyGxkSVwNzzD49qHryKNLrIRuI,233
69
+ pywire/compiler/interpolation/base.py,sha256=33wIfZVMNpIl_ZPXzkWYdOiPX3jWzZ4xb0UGbjXEpyw,781
70
+ pywire/compiler/interpolation/jinja.py,sha256=HJEkRE0aB-YEiw7sZ6agS_zRUQlo19uj0bX21WOW8MQ,9831
71
+ pywire/core/wire.py,sha256=ZgjoALx_B8i4ibt8rdY0XjNLVrfZcHLQh0urWl-iwIE,3899
72
+ pywire/runtime/__init__.py,sha256=441hDvBWfYni7BtKlVXHEm2FWahK0MR3sIpkPkIE1zk,207
73
+ pywire/runtime/aioquic_server.py,sha256=n9GuK81xtc6PUT54uMhqMlGXn8NrH-ryZ6Rl9qQWt7M,6685
74
+ pywire/runtime/app.py,sha256=h50bmMll0wUixAYWq1FUUMBlt-zjnlqNC4K_3BqC4xY,35785
75
+ pywire/runtime/compile_error_page.py,sha256=sqPTqG9dv_e2uBgYuAgOcDHx0D3Gwq2liFbBRrz4_iY,8205
76
+ pywire/runtime/debug.py,sha256=6U-Ot1defe7eDXaAxNip4NtRPQjtjS9LNCUnk_pmmeQ,7416
77
+ pywire/runtime/dev_server.py,sha256=QQ2tr7bgSdM1yDwfW7h_-x7r2RZ4qsd5A59GAvbceu0,16096
78
+ pywire/runtime/dev_server.py.broken,sha256=cAPPLFNe6qJ-KouATG3FfIabGd-o3qCqlMGqQQF03sc,11030
79
+ pywire/runtime/error_page.py,sha256=fnE8Qjf1a7fjLOys6G2uDsK0VYGMZg2yGWgDagk39j0,2497
80
+ pywire/runtime/error_renderer.py,sha256=HNS9seD5gX4jE3L4GPsJsyxvpMTiiNo3E43trUNiuX0,701
81
+ pywire/runtime/escape.py,sha256=w-_TcFLG0heyenDaituVx2oir_80WjGBWnnHZe5I8Wk,517
82
+ pywire/runtime/files.py,sha256=RJjDhCaoKjk_x3B_71rA4EhqVjNLGU_fuwg4Cq894nY,1316
83
+ pywire/runtime/helpers.py,sha256=jY3bDE7hHjPaVlLmWrGvQPDEtCYFU9Alt8j0dvSZgNQ,3039
84
+ pywire/runtime/http_transport.py,sha256=uS3Or4uo581XSVqRB1VsEfa7Mbqr4RK5Rk0pGb-z4p8,9401
85
+ pywire/runtime/loader.py,sha256=rKsHYb8dx-WkxBYClHR4Gztoy1N88kLYt5CCnvA0gIg,9664
86
+ pywire/runtime/logging.py,sha256=wEdxro6YxvdnjwALwzMr60As2JMj_J4DIsSL8JZScvM,2286
87
+ pywire/runtime/page.py,sha256=tEKPUfQFQpw3T20sWsjlXLfmDr2TlNmesc_jDwcK748,14288
88
+ pywire/runtime/pydantic_integration.py,sha256=Fwu45Km5SHS10ZqIxCK-Ps8hBPlVY6rHzkkP7Mh8KEs,1800
89
+ pywire/runtime/router.py,sha256=wHr0iyAQ3_jGzMPpyxNKE9rLWD3oiQM-JAtkxX4h7SI,8172
90
+ pywire/runtime/server.py,sha256=hzG1NACfjasTmNZd-imORTLNxMCD-JYgkfjIRyQ3GJE,746
91
+ pywire/runtime/style_collector.py,sha256=H1Crk1DO1wfch2Ed4i4_sHANH5QX_HGGGlaJr7MRiIY,1147
92
+ pywire/runtime/upload_manager.py,sha256=n7WRwpzHqnW1CZsoshxq7YumNMFrG7Hs5CiEHP-0W4k,2346
93
+ pywire/runtime/validation.py,sha256=Y1_nI_dFKy3_7aXN-gCubUBhBlzgEtNEkdS1DH81tEI,16199
94
+ pywire/runtime/websocket.py,sha256=7sNawtxC0l_eJBEjI80DGnUqei7Ol0RX8Y7Yz3_wNxw,26949
95
+ pywire/runtime/webtransport_handler.py,sha256=GZIzTlv5IyEmIq2eumxFTu1XgWaPkiRFujG1vBjQNsE,7841
96
+ pywire/templates/error/404.html,sha256=cZavWjs-aXoSRpEy-8D0rulP7g5Sn31MsIXmkZUQnnA,256
97
+ pywire/templates/error/500.html,sha256=545CPh1XyhN-zdSAD8R-6Cuos6hLC--TXJy4EQHrpjM,1282
98
+ pywire/templates/error/base.html,sha256=nlBlBIg-VFAWtTLWky7c2igNVsFjj9M8Wyw3v3_4KGQ,4863
99
+ pywire/templates/error/compile_error.html,sha256=FMqU3HXYWMeiXG3eTAysNpAHH-AOPx2Apbz8f_HBvtw,837
100
+ pywire-0.1.0.dist-info/METADATA,sha256=AIOkF-E5UkYqWCgRRRer-c9P8v5NJLb4PaEecsVMfcE,1905
101
+ pywire-0.1.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
102
+ pywire-0.1.0.dist-info/entry_points.txt,sha256=L0n4cNLZIocOo6lG05uOoUN1GHgA_uQOHvapDAhULBI,47
103
+ pywire-0.1.0.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
104
+ pywire-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.28.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ pywire = pywire.cli.main:cli