11from __future__ import annotations
22
33import json
4- from collections .abc import AsyncIterable , Iterable
4+ from collections .abc import AsyncIterable , Iterable , Mapping
55from itertools import chain
66from typing import Literal , Protocol , TypeAlias , Union , overload , runtime_checkable
77
88import datastar_py .consts as consts
9+ from datastar_py .attributes import _escape
910
1011SSE_HEADERS : dict [str , str ] = {
1112 "Cache-Control" : "no-cache" ,
@@ -46,12 +47,11 @@ def _send(
4647 event_id : str | None = None ,
4748 retry_duration : int | None = None ,
4849 ) -> DatastarEvent :
49- prefix = []
50+ prefix = [f"event: { event_type } " ]
51+
5052 if event_id :
5153 prefix .append (f"id: { event_id } " )
5254
53- prefix .append (f"event: { event_type } " )
54-
5555 if retry_duration and retry_duration != consts .DEFAULT_SSE_RETRY_DURATION :
5656 prefix .append (f"retry: { retry_duration } " )
5757
@@ -94,7 +94,7 @@ def patch_elements(
9494 if isinstance (elements , _HtmlProvider ):
9595 elements = elements .__html__ ()
9696 data_lines = []
97- if mode :
97+ if mode and mode != "outer" : # TODO: Should there be a constant for this?
9898 data_lines .append (f"{ consts .MODE_DATALINE_LITERAL } { mode } " )
9999 if selector :
100100 data_lines .append (f"{ consts .SELECTOR_DATALINE_LITERAL } { selector } " )
@@ -132,7 +132,7 @@ def remove_elements(
132132 @classmethod
133133 def patch_signals (
134134 cls ,
135- signals : dict ,
135+ signals : dict | str ,
136136 event_id : str | None = None ,
137137 only_if_missing : bool | None = None ,
138138 retry_duration : int | None = None ,
@@ -146,7 +146,13 @@ def patch_signals(
146146 f"{ consts .ONLY_IF_MISSING_DATALINE_LITERAL } { _js_bool (only_if_missing )} "
147147 )
148148
149- data_lines .append (f"{ consts .SIGNALS_DATALINE_LITERAL } { json .dumps (signals )} " )
149+ signals_str = (
150+ signals if isinstance (signals , str ) else json .dumps (signals , separators = ("," , ":" ))
151+ )
152+ data_lines .extend (
153+ f"{ consts .SIGNALS_DATALINE_LITERAL } { line } "
154+ for line in signals_str .splitlines ()
155+ )
150156
151157 return ServerSentEventGenerator ._send (
152158 consts .EventType .PATCH_SIGNALS , data_lines , event_id , retry_duration
@@ -157,15 +163,20 @@ def execute_script(
157163 cls ,
158164 script : str ,
159165 auto_remove : bool = True ,
160- attributes : list [str ] | None = None ,
166+ attributes : Mapping [ str , str ] | list [str ] | None = None ,
161167 event_id : str | None = None ,
162168 retry_duration : int | None = None ,
163169 ) -> DatastarEvent :
164170 attribute_string = ""
165171 if auto_remove :
166- attribute_string += " data-effect=' el.remove()'"
172+ attribute_string += ' data-effect=" el.remove()"'
167173 if attributes :
168- attribute_string += " " + " " .join (attributes )
174+ if isinstance (attributes , Mapping ):
175+ attribute_string += " " + " " .join (
176+ f'{ _escape (k )} ="{ _escape (v )} "' for k , v in attributes .items ()
177+ )
178+ else :
179+ attribute_string += " " + " " .join (attributes )
169180 script_tag = f"<script{ attribute_string } >{ script } </script>"
170181
171182 return ServerSentEventGenerator .patch_elements (
0 commit comments