fastlifeweb 0.26.0__py3-none-any.whl → 0.26.2__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.
CHANGELOG.md CHANGED
@@ -1,3 +1,10 @@
1
+ ## 0.26.2 - Released on 2025-04-20
2
+ * Add a RedirectResponse class that is htmx friendly and P/R/G pattern friendly.
3
+ * Fix documentation generation.
4
+
5
+ ## 0.26.1 - Released on 2025-04-20
6
+ * Add new helpers for the webtestclient element.
7
+
1
8
  ## 0.26.0 - Released on 2025-03-08
2
9
  * Add a new method to the configuration to customize the request class.
3
10
  See [Configurator.set_request_factory](#fastlife.config.configurator.GenericConfigurator.set_request_factory)
fastlife/__init__.py CHANGED
@@ -2,8 +2,6 @@ from importlib import metadata
2
2
 
3
3
  __version__ = metadata.version("fastlifeweb")
4
4
 
5
- from fastapi import Response
6
- from fastapi.responses import RedirectResponse
7
5
 
8
6
  from .adapters.fastapi.form import form_model
9
7
  from .adapters.fastapi.localizer import Localizer
@@ -26,6 +24,7 @@ from .config import (
26
24
  from .domain.model.asgi import ASGIRequest, ASGIResponse
27
25
  from .domain.model.form import FormModel
28
26
  from .domain.model.request import GenericRequest
27
+ from .domain.model.response import RedirectResponse, Response
29
28
  from .domain.model.security_policy import (
30
29
  Allowed,
31
30
  Anonymous,
fastlife/assets/dist.css CHANGED
@@ -1,4 +1,5 @@
1
- /*! tailwindcss v4.0.9 | MIT License | https://tailwindcss.com */
1
+ /*! tailwindcss v4.1.4 | MIT License | https://tailwindcss.com */
2
+ @layer properties;
2
3
  @layer theme, base, components, utilities;
3
4
  @layer theme {
4
5
  :root, :host {
@@ -6,20 +7,20 @@
6
7
  'Noto Color Emoji';
7
8
  --font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New',
8
9
  monospace;
9
- --color-red-50: oklch(0.971 0.013 17.38);
10
- --color-red-400: oklch(0.704 0.191 22.216);
11
- --color-red-700: oklch(0.505 0.213 27.518);
12
- --color-orange-500: oklch(0.705 0.213 47.604);
13
- --color-neutral-50: oklch(0.985 0 0);
14
- --color-neutral-100: oklch(0.97 0 0);
15
- --color-neutral-200: oklch(0.922 0 0);
16
- --color-neutral-300: oklch(0.87 0 0);
17
- --color-neutral-400: oklch(0.708 0 0);
18
- --color-neutral-500: oklch(0.556 0 0);
19
- --color-neutral-600: oklch(0.439 0 0);
20
- --color-neutral-700: oklch(0.371 0 0);
21
- --color-neutral-800: oklch(0.269 0 0);
22
- --color-neutral-900: oklch(0.205 0 0);
10
+ --color-red-50: oklch(97.1% 0.013 17.38);
11
+ --color-red-400: oklch(70.4% 0.191 22.216);
12
+ --color-red-700: oklch(50.5% 0.213 27.518);
13
+ --color-orange-500: oklch(70.5% 0.213 47.604);
14
+ --color-neutral-50: oklch(98.5% 0 0);
15
+ --color-neutral-100: oklch(97% 0 0);
16
+ --color-neutral-200: oklch(92.2% 0 0);
17
+ --color-neutral-300: oklch(87% 0 0);
18
+ --color-neutral-400: oklch(70.8% 0 0);
19
+ --color-neutral-500: oklch(55.6% 0 0);
20
+ --color-neutral-600: oklch(43.9% 0 0);
21
+ --color-neutral-700: oklch(37.1% 0 0);
22
+ --color-neutral-800: oklch(26.9% 0 0);
23
+ --color-neutral-900: oklch(20.5% 0 0);
23
24
  --color-white: #fff;
24
25
  --spacing: 0.25rem;
25
26
  --text-sm: 0.875rem;
@@ -48,11 +49,7 @@
48
49
  --default-transition-duration: 150ms;
49
50
  --default-transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
50
51
  --default-font-family: var(--font-sans);
51
- --default-font-feature-settings: var(--font-sans--font-feature-settings);
52
- --default-font-variation-settings: var(--font-sans--font-variation-settings);
53
52
  --default-mono-font-family: var(--font-mono);
54
- --default-mono-font-feature-settings: var(--font-mono--font-feature-settings);
55
- --default-mono-font-variation-settings: var(--font-mono--font-variation-settings);
56
53
  }
57
54
  }
58
55
  @layer base {
@@ -66,14 +63,11 @@
66
63
  line-height: 1.5;
67
64
  -webkit-text-size-adjust: 100%;
68
65
  tab-size: 4;
69
- font-family: var( --default-font-family, ui-sans-serif, system-ui, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji' );
66
+ font-family: var(--default-font-family, ui-sans-serif, system-ui, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji');
70
67
  font-feature-settings: var(--default-font-feature-settings, normal);
71
68
  font-variation-settings: var(--default-font-variation-settings, normal);
72
69
  -webkit-tap-highlight-color: transparent;
73
70
  }
74
- body {
75
- line-height: inherit;
76
- }
77
71
  hr {
78
72
  height: 0;
79
73
  color: inherit;
@@ -96,7 +90,7 @@
96
90
  font-weight: bolder;
97
91
  }
98
92
  code, kbd, samp, pre {
99
- font-family: var( --default-mono-font-family, ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New', monospace );
93
+ font-family: var(--default-mono-font-family, ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New', monospace);
100
94
  font-feature-settings: var(--default-mono-font-feature-settings, normal);
101
95
  font-variation-settings: var(--default-mono-font-variation-settings, normal);
102
96
  font-size: 1em;
@@ -162,7 +156,14 @@
162
156
  }
163
157
  ::placeholder {
164
158
  opacity: 1;
165
- color: color-mix(in oklab, currentColor 50%, transparent);
159
+ }
160
+ @supports (not (-webkit-appearance: -apple-pay-button)) or (contain-intrinsic-size: 1px) {
161
+ ::placeholder {
162
+ color: currentcolor;
163
+ @supports (color: color-mix(in lab, red, red)) {
164
+ color: color-mix(in oklab, currentcolor 50%, transparent);
165
+ }
166
+ }
166
167
  }
167
168
  textarea {
168
169
  resize: vertical;
@@ -215,24 +216,6 @@
215
216
  .static {
216
217
  position: static;
217
218
  }
218
- .container {
219
- width: 100%;
220
- @media (width >= 40rem) {
221
- max-width: 40rem;
222
- }
223
- @media (width >= 48rem) {
224
- max-width: 48rem;
225
- }
226
- @media (width >= 64rem) {
227
- max-width: 64rem;
228
- }
229
- @media (width >= 80rem) {
230
- max-width: 80rem;
231
- }
232
- @media (width >= 96rem) {
233
- max-width: 96rem;
234
- }
235
- }
236
219
  .m-3 {
237
220
  margin: calc(var(--spacing) * 3);
238
221
  }
@@ -263,9 +246,6 @@
263
246
  .inline {
264
247
  display: inline;
265
248
  }
266
- .table {
267
- display: table;
268
- }
269
249
  .h-4 {
270
250
  height: calc(var(--spacing) * 4);
271
251
  }
@@ -303,7 +283,7 @@
303
283
  rotate: 90deg;
304
284
  }
305
285
  .transform {
306
- transform: var(--tw-rotate-x) var(--tw-rotate-y) var(--tw-rotate-z) var(--tw-skew-x) var(--tw-skew-y);
286
+ transform: var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,);
307
287
  }
308
288
  .cursor-pointer {
309
289
  cursor: pointer;
@@ -494,18 +474,19 @@
494
474
  }
495
475
  .focus\:ring-2 {
496
476
  &:focus {
497
- --tw-ring-shadow: var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color, currentColor);
477
+ --tw-ring-shadow: var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color, currentcolor);
498
478
  box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
499
479
  }
500
480
  }
501
481
  .focus\:ring-4 {
502
482
  &:focus {
503
- --tw-ring-shadow: var(--tw-ring-inset,) 0 0 0 calc(4px + var(--tw-ring-offset-width)) var(--tw-ring-color, currentColor);
483
+ --tw-ring-shadow: var(--tw-ring-inset,) 0 0 0 calc(4px + var(--tw-ring-offset-width)) var(--tw-ring-color, currentcolor);
504
484
  box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
505
485
  }
506
486
  }
507
487
  .focus\:outline-hidden {
508
488
  &:focus {
489
+ --tw-outline-style: none;
509
490
  outline-style: none;
510
491
  @media (forced-colors: active) {
511
492
  outline: 2px solid transparent;
@@ -618,27 +599,22 @@
618
599
  @property --tw-rotate-x {
619
600
  syntax: "*";
620
601
  inherits: false;
621
- initial-value: rotateX(0);
622
602
  }
623
603
  @property --tw-rotate-y {
624
604
  syntax: "*";
625
605
  inherits: false;
626
- initial-value: rotateY(0);
627
606
  }
628
607
  @property --tw-rotate-z {
629
608
  syntax: "*";
630
609
  inherits: false;
631
- initial-value: rotateZ(0);
632
610
  }
633
611
  @property --tw-skew-x {
634
612
  syntax: "*";
635
613
  inherits: false;
636
- initial-value: skewX(0);
637
614
  }
638
615
  @property --tw-skew-y {
639
616
  syntax: "*";
640
617
  inherits: false;
641
- initial-value: skewY(0);
642
618
  }
643
619
  @property --tw-space-y-reverse {
644
620
  syntax: "*";
@@ -707,6 +683,19 @@
707
683
  syntax: "*";
708
684
  inherits: false;
709
685
  }
686
+ @property --tw-drop-shadow-color {
687
+ syntax: "*";
688
+ inherits: false;
689
+ }
690
+ @property --tw-drop-shadow-alpha {
691
+ syntax: "<percentage>";
692
+ inherits: false;
693
+ initial-value: 100%;
694
+ }
695
+ @property --tw-drop-shadow-size {
696
+ syntax: "*";
697
+ inherits: false;
698
+ }
710
699
  @property --tw-duration {
711
700
  syntax: "*";
712
701
  inherits: false;
@@ -720,6 +709,11 @@
720
709
  syntax: "*";
721
710
  inherits: false;
722
711
  }
712
+ @property --tw-shadow-alpha {
713
+ syntax: "<percentage>";
714
+ inherits: false;
715
+ initial-value: 100%;
716
+ }
723
717
  @property --tw-inset-shadow {
724
718
  syntax: "*";
725
719
  inherits: false;
@@ -729,6 +723,11 @@
729
723
  syntax: "*";
730
724
  inherits: false;
731
725
  }
726
+ @property --tw-inset-shadow-alpha {
727
+ syntax: "<percentage>";
728
+ inherits: false;
729
+ initial-value: 100%;
730
+ }
732
731
  @property --tw-ring-color {
733
732
  syntax: "*";
734
733
  inherits: false;
@@ -766,3 +765,48 @@
766
765
  inherits: false;
767
766
  initial-value: 0 0 #0000;
768
767
  }
768
+ @layer properties {
769
+ @supports ((-webkit-hyphens: none) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color:rgb(from red r g b)))) {
770
+ *, ::before, ::after, ::backdrop {
771
+ --tw-rotate-x: initial;
772
+ --tw-rotate-y: initial;
773
+ --tw-rotate-z: initial;
774
+ --tw-skew-x: initial;
775
+ --tw-skew-y: initial;
776
+ --tw-space-y-reverse: 0;
777
+ --tw-border-style: solid;
778
+ --tw-leading: initial;
779
+ --tw-font-weight: initial;
780
+ --tw-tracking: initial;
781
+ --tw-outline-style: solid;
782
+ --tw-blur: initial;
783
+ --tw-brightness: initial;
784
+ --tw-contrast: initial;
785
+ --tw-grayscale: initial;
786
+ --tw-hue-rotate: initial;
787
+ --tw-invert: initial;
788
+ --tw-opacity: initial;
789
+ --tw-saturate: initial;
790
+ --tw-sepia: initial;
791
+ --tw-drop-shadow: initial;
792
+ --tw-drop-shadow-color: initial;
793
+ --tw-drop-shadow-alpha: 100%;
794
+ --tw-drop-shadow-size: initial;
795
+ --tw-duration: initial;
796
+ --tw-shadow: 0 0 #0000;
797
+ --tw-shadow-color: initial;
798
+ --tw-shadow-alpha: 100%;
799
+ --tw-inset-shadow: 0 0 #0000;
800
+ --tw-inset-shadow-color: initial;
801
+ --tw-inset-shadow-alpha: 100%;
802
+ --tw-ring-color: initial;
803
+ --tw-ring-shadow: 0 0 #0000;
804
+ --tw-inset-ring-color: initial;
805
+ --tw-inset-ring-shadow: 0 0 #0000;
806
+ --tw-ring-inset: initial;
807
+ --tw-ring-offset-width: 0px;
808
+ --tw-ring-offset-color: #fff;
809
+ --tw-ring-offset-shadow: 0 0 #0000;
810
+ }
811
+ }
812
+ }
@@ -6,7 +6,7 @@
6
6
 
7
7
  ::
8
8
 
9
- <Form :hx-post="true">
9
+ <Form hx-post>
10
10
  <Input name="name" placeholder="Bob" />
11
11
  <Button>Submit</Button>
12
12
  </Form>
@@ -177,7 +177,7 @@ class GenericConfigurator(Generic[TRegistry]):
177
177
  """
178
178
 
179
179
  # register our main template renderer at then end, to ensure that
180
- # if settings have been manipulated, everythins is taken into account.
180
+ # if settings have been manipulated, everything is taken into account.
181
181
  self.add_renderer(
182
182
  self.registry.settings.jinjax_file_ext,
183
183
  resolve("fastlife.adapters.jinjax.renderer:JinjaxEngine")(
@@ -0,0 +1,40 @@
1
+ from collections.abc import Mapping
2
+ from urllib.parse import quote
3
+
4
+ from starlette.background import BackgroundTask
5
+ from starlette.datastructures import URL
6
+ from starlette.responses import Response
7
+
8
+
9
+ class RedirectResponse(Response):
10
+ """
11
+ A redirect response for Post/Redirect/Get pattern.
12
+
13
+ The starlette default value status code is 307, which means that it is used
14
+ as a way to replay the same query which is definitly not the most used case
15
+ in web applications.
16
+
17
+ This is why the redirect response here is using 303 see other which
18
+ ensure a GET request will be made for the redirection.
19
+
20
+ A new parameter hx_redirect exists in order to set the HX-Redirect header
21
+ to follow a browser redirection from an ajax query.
22
+ """
23
+
24
+ def __init__(
25
+ self,
26
+ url: str | URL,
27
+ hx_redirect: bool = False,
28
+ status_code: int = 303,
29
+ headers: Mapping[str, str] | None = None,
30
+ background: BackgroundTask | None = None,
31
+ ):
32
+ super().__init__(
33
+ content=b"", status_code=status_code, headers=headers, background=background
34
+ )
35
+ self.headers["HX-Redirect" if hx_redirect else "location"] = quote(
36
+ str(url), safe=":/%#?=@[]!$&'()*+,;"
37
+ )
38
+
39
+
40
+ __all__ = ["Response", "RedirectResponse"]
fastlife/testing/dom.py CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  import re
4
4
  from collections.abc import Iterator, Sequence
5
- from typing import TYPE_CHECKING
5
+ from typing import TYPE_CHECKING, Literal, overload
6
6
 
7
7
  import bs4
8
8
 
@@ -82,10 +82,19 @@ class Element:
82
82
  el = el.parent
83
83
  return None
84
84
 
85
- def by_text(self, text: str, *, node_name: str | None = None) -> "Element | None":
85
+ def by_text(
86
+ self, text: str, *, node_name: str | None = None, position: int | None = None
87
+ ) -> "Element | None":
86
88
  """Find the first element that match the text."""
87
89
  nodes = self.iter_all_by_text(text, node_name=node_name)
88
- return next(nodes, None)
90
+ ret = list(nodes)
91
+ if not ret:
92
+ return None
93
+ if position is None:
94
+ assert len(ret) == 1, f"Should have 1 element, got {len(ret)} in {self}"
95
+ else:
96
+ assert len(ret) > position, "Not enough element found"
97
+ return ret[position or 0]
89
98
 
90
99
  def iter_all_by_text(
91
100
  self, text: str, *, node_name: str | None = None
@@ -121,17 +130,60 @@ class Element:
121
130
  assert not isinstance(resp, bs4.NavigableString)
122
131
  return Element(self._client, resp) if resp else None
123
132
 
133
+ def by_id(self, id: str) -> "Element | None":
134
+ """Find the element having the given id."""
135
+ resp = self._tag.find_all(id=id)
136
+ assert not isinstance(resp, bs4.NavigableString)
137
+ if not resp:
138
+ return None
139
+ assert len(resp) == 1
140
+ return Element(self._client, resp[0]) if resp else None
141
+
142
+ @overload
143
+ def by_node_name(
144
+ self,
145
+ node_name: str,
146
+ *,
147
+ attrs: dict[str, str] | None = None,
148
+ multiple: Literal[False],
149
+ ) -> "Element": ...
150
+
151
+ @overload
152
+ def by_node_name(
153
+ self,
154
+ node_name: str,
155
+ *,
156
+ attrs: dict[str, str] | None = None,
157
+ multiple: Literal[True],
158
+ ) -> "list[Element]": ...
159
+
160
+ @overload
161
+ def by_node_name(
162
+ self,
163
+ node_name: str,
164
+ *,
165
+ attrs: dict[str, str] | None = None,
166
+ ) -> "list[Element]": ...
167
+
124
168
  def by_node_name(
125
- self, node_name: str, *, attrs: dict[str, str] | None = None
126
- ) -> list["Element"]:
169
+ self,
170
+ node_name: str,
171
+ *,
172
+ attrs: dict[str, str] | None = None,
173
+ multiple: bool = True,
174
+ ) -> "list[Element] | Element":
127
175
  """
128
176
  Return the list of elements with the given node_name.
129
177
 
130
178
  An optional set of attributes may given and must match if passed.
131
179
  """
132
- return [
180
+ ret = [
133
181
  Element(self._client, e) for e in self._tag.find_all(node_name, attrs or {})
134
182
  ]
183
+ if not multiple:
184
+ assert len(ret) == 1
185
+ return ret[0]
186
+ return ret
135
187
 
136
188
  def __repr__(self) -> str:
137
189
  return f"<{self.node_name}>"
@@ -79,9 +79,11 @@ class WebResponse:
79
79
  self._form = WebForm(self._client, self._origin, form)
80
80
  return self._form
81
81
 
82
- def by_text(self, text: str, *, node_name: str | None = None) -> Element | None:
82
+ def by_text(
83
+ self, text: str, *, node_name: str | None = None, position: int | None = None
84
+ ) -> Element | None:
83
85
  """Search a dom element by its text."""
84
- return self.html.by_text(text, node_name=node_name)
86
+ return self.html.by_text(text, node_name=node_name, position=position)
85
87
 
86
88
  def by_label_text(self, text: str) -> Element | None:
87
89
  """Search a dom element by its associated label text."""
@@ -156,9 +158,14 @@ class WebTestClient:
156
158
  method = "GET"
157
159
  headers = None
158
160
  content = None
161
+ url = resp.headers.get("location")
162
+ if "HX-Redirect" in resp.headers:
163
+ # Redirection requested to the browser from an AJAX request.
164
+ url = resp.headers["HX-Redirect"]
165
+ assert url, "Redirect response without a redirection"
159
166
  return self.request(
160
167
  method=method,
161
- url=resp.headers["location"],
168
+ url=url,
162
169
  content=content,
163
170
  headers=headers,
164
171
  max_redirects=max_redirects - 1,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: fastlifeweb
3
- Version: 0.26.0
3
+ Version: 0.26.2
4
4
  Summary: High-level web framework
5
5
  Author-Email: Guillaume Gauvrit <guillaume@gauvr.it>
6
6
  License: MIT
@@ -1,5 +1,5 @@
1
- CHANGELOG.md,sha256=H7i2AY7B5DYbr5ACg2YRaTCO8vWtQMGAMw7bkg2DHhA,8594
2
- fastlife/__init__.py,sha256=IZ9VQvlBduZ3WjfDk05rZNdCKNxWWqPaMvG0ZWtMTpU,2489
1
+ CHANGELOG.md,sha256=NqlUm-TificrXs2dKAL5-1_6i3Eo74MRksoaa0RBS1k,8830
2
+ fastlife/__init__.py,sha256=Fe8JiQyKIN1WGagUGFct-QBW8-Ku5vXhc_7BkFUGcWk,2475
3
3
  fastlife/adapters/__init__.py,sha256=imPD1hImpgrYkvUJRhHA5kVyGAua7VbP2WGkhSWKJT8,93
4
4
  fastlife/adapters/fastapi/__init__.py,sha256=1goV1FGFP04TGyskJBLKZam4Gvt1yoAvLMNs4ekWSSQ,243
5
5
  fastlife/adapters/fastapi/form.py,sha256=csxsDI6RK-g41pMwFhaVQCLDhF7dAZzgUp-VcrC3NFY,823
@@ -42,14 +42,14 @@ fastlife/adapters/jinjax/widgets/model.py,sha256=YBIEWa_6mnmrBnesXjLTrpJ4drUS2CI
42
42
  fastlife/adapters/jinjax/widgets/sequence.py,sha256=dVoHQmHloaRuU1Sd82b2jnO8WDfdwM2FaZlLCJCps1o,2566
43
43
  fastlife/adapters/jinjax/widgets/text.py,sha256=TfmlJU233aZWIl-4cmm-f-pFxp6ycHWHnbiluOvRDgM,3040
44
44
  fastlife/adapters/jinjax/widgets/union.py,sha256=roCoFA82dLjF1XFW6UYaV7SCQWdFsSAT8Ux7KEB6_Us,2602
45
- fastlife/assets/dist.css,sha256=BkGYK48Fmy7t3-KMKlpGnpjmpJpxQ4D6QUNlfmtVbU8,18432
45
+ fastlife/assets/dist.css,sha256=d2ez-igscOaeYtJQc2FQuXlN17cYX13sUansFdg_kdA,19753
46
46
  fastlife/assets/source.css,sha256=0KtDcsKHj9LOcqNR1iv9pACwNBaNWkieEDqqjkgNL_s,47
47
47
  fastlife/components/A.jinja,sha256=MDNJ2auIeYbpNeErvJdlGid4nIKfbi85ArmMgChsCJU,1384
48
48
  fastlife/components/Button.jinja,sha256=itKU-ct45XissU33yfmTekyHsNe00fr4RQL-e9cxbgU,2305
49
49
  fastlife/components/Checkbox.jinja,sha256=g62A1LR8TaN60h94pE2e5l9_eMmgnhVVE9HVCQtVVMo,748
50
50
  fastlife/components/CsrfToken.jinja,sha256=mS0q-3_hAevl_waWEPaN0QAYOBzMyzl-W1PSpEHUBA0,215
51
51
  fastlife/components/Details.jinja,sha256=NtQX-V3kcp1CV1GkrMkj5fc-KHPZHshWkrhXAZ8E3ms,736
52
- fastlife/components/Form.jinja,sha256=IJhkUwp_dF-BePi8okh-sUglXP6wJvtxNd-4PUFCwTY,2089
52
+ fastlife/components/Form.jinja,sha256=p4HNIpjDLJT0oH_Vbv6_lYV0tuG_2Xu5E2vZ-yJWrT8,2081
53
53
  fastlife/components/H1.jinja,sha256=fWZtTq34qN9gwGgDF91lkypxaXvGN_OTmUY7XUHIpw0,370
54
54
  fastlife/components/H2.jinja,sha256=o7Q-oR_zDtItV5A7QWfEo_LoMw6bR44YNBDQP3ao1bg,370
55
55
  fastlife/components/H3.jinja,sha256=cZHJTVER1LVYWAwP3sR-23Ktbk9WYlLgLnr2Dz_0oqU,370
@@ -1690,7 +1690,7 @@ fastlife/components/pydantic_form/FatalError.jinja,sha256=ADtQvmo-e-NmDcFM1E6wZV
1690
1690
  fastlife/components/pydantic_form/Hint.jinja,sha256=8leBpfMGDmalc_KAjr2paTojr_rwq-luS6m_1BGj7Tw,202
1691
1691
  fastlife/components/pydantic_form/Widget.jinja,sha256=PgguUpvhG6CY9AW6H8qQMjKqjlybjDCAaFFAOHzrzVQ,418
1692
1692
  fastlife/config/__init__.py,sha256=5qpuaVYqi-AS0GgsfggM6rFsSwXgrqrLBo9jH6dVroc,407
1693
- fastlife/config/configurator.py,sha256=200Vk4Tm7jv8XFWTFDYNUyuB9bDEcrJOnmtVyBItVVA,25188
1693
+ fastlife/config/configurator.py,sha256=2LkPXoarMTk40S7AVlxODPAckDllE0G8u27jtPmvGM8,25188
1694
1694
  fastlife/config/exceptions.py,sha256=9MdBnbfy-Aw-KaIFzju0Kh8Snk41-v9LqK2w48Tdy1s,1169
1695
1695
  fastlife/config/openapiextra.py,sha256=rYoerrn9sni2XwnO3gIWqaz7M0aDZPhVLjzqhDxue0o,514
1696
1696
  fastlife/config/resources.py,sha256=EcPTM25pnHcGFTtXjeZnWn5Mo_-8rhJ72HJ6rxnjPg8,8389
@@ -1701,6 +1701,7 @@ fastlife/domain/model/asgi.py,sha256=Cz45TZOtrh2pBVZr37aJ9jpnJH9BeNHrsvk9bq1nBc0
1701
1701
  fastlife/domain/model/csrf.py,sha256=BUiWK-S7rVciWHO1qTkM8e_KxzpF6gGC4MMJK1v6iDo,414
1702
1702
  fastlife/domain/model/form.py,sha256=JP6uumlZBYhiPxzcdxOsfsFm5BRfvkDFvlUCD6Vy8dI,3275
1703
1703
  fastlife/domain/model/request.py,sha256=HgUSnUu3q18e07y57PadN3pPQwYrIZS1YEhYkBZ_Zfg,2674
1704
+ fastlife/domain/model/response.py,sha256=Vsd2zYGGhH0D2DlfiKz1CX9OJZ_ZYoEv_-foMZpDFZo,1294
1704
1705
  fastlife/domain/model/security_policy.py,sha256=f9SLi54vvRU-KSPJ5K0unoqYpkxIyzuZjKf2Ylwf5Rg,4796
1705
1706
  fastlife/domain/model/template.py,sha256=z9oxdKme1hMPuvk7mBiKR_tuVY8TqH77aTYqMgvEGl8,876
1706
1707
  fastlife/domain/model/types.py,sha256=64jJKFAi5x0e3vr8naHU1m_as0Qy8MS-s9CG0z6K1qc,381
@@ -1727,14 +1728,14 @@ fastlife/shared_utils/infer.py,sha256=0GflLkaWJ-4LZ1Ig3moR-_o55wwJ_p_vJ4xo-yi3ly
1727
1728
  fastlife/shared_utils/resolver.py,sha256=Wb9cO2MWavpti63hju15xmwFMgaD5DsQaxikRpB39E8,3713
1728
1729
  fastlife/template_globals.py,sha256=bKcj6kSnQlzuOeoILA5oRxxzy6CsrBFMZv9S0w5XlTQ,9021
1729
1730
  fastlife/testing/__init__.py,sha256=VpxkS3Zp3t_hH8dBiLaGFGhsvt511dhBS_8fMoFXdmU,99
1730
- fastlife/testing/dom.py,sha256=dVzDoZokn-ii681UaEwAr-khM5KE-CHgXSSLSo24oH0,4489
1731
+ fastlife/testing/dom.py,sha256=q2GFrHWjwKMMTR0dsP3J-rXSxojZy8rOQ-07h2gfLKA,5869
1731
1732
  fastlife/testing/form.py,sha256=diiGfVMfNt19JTNUxlnbGfcbskR3ZMpk0Y-A57vfShc,7871
1732
1733
  fastlife/testing/session.py,sha256=LEFFbiR67_x_g-ioudkY0C7PycHdbDfaIaoo_G7GXQ8,2226
1733
- fastlife/testing/testclient.py,sha256=Id1tlA1ZapyW-8kUh2_U3lLteL64m3ERqOO7NAN7HEY,6922
1734
+ fastlife/testing/testclient.py,sha256=gqgHQalhrLLZ8eveN2HeuoG9ne8CwxCm-Ll4b7jo9Xo,7249
1734
1735
  fastlife/views/__init__.py,sha256=zG8gveL8e2zBdYx6_9jtZfpQ6qJT-MFnBY3xXkLwHZI,22
1735
1736
  fastlife/views/pydantic_form.py,sha256=o7EUItciAGL1OSaGNHo-3BTrYAk34GuWE7zGikjiAGY,1486
1736
- fastlifeweb-0.26.0.dist-info/METADATA,sha256=iCt6oKseeHBfQyuoiUuUXK4p-ls8QM4wzkA8JdiPXIk,3690
1737
- fastlifeweb-0.26.0.dist-info/WHEEL,sha256=thaaA2w1JzcGC48WYufAs8nrYZjJm8LqNfnXFOFyCC4,90
1738
- fastlifeweb-0.26.0.dist-info/entry_points.txt,sha256=6OYgBcLyFCUgeqLgnvMyOJxPCWzgy7se4rLPKtNonMs,34
1739
- fastlifeweb-0.26.0.dist-info/licenses/LICENSE,sha256=JFWuiKYRXKKMEAsX0aZp3hBcju-HYflJ2rwJAGwbCJo,1080
1740
- fastlifeweb-0.26.0.dist-info/RECORD,,
1737
+ fastlifeweb-0.26.2.dist-info/METADATA,sha256=btOasL9jb9IiIrj9yPnrSW60e3T8Xpxy2Abu0hBBcb8,3690
1738
+ fastlifeweb-0.26.2.dist-info/WHEEL,sha256=tSfRZzRHthuv7vxpI4aehrdN9scLjk-dCJkPLzkHxGg,90
1739
+ fastlifeweb-0.26.2.dist-info/entry_points.txt,sha256=6OYgBcLyFCUgeqLgnvMyOJxPCWzgy7se4rLPKtNonMs,34
1740
+ fastlifeweb-0.26.2.dist-info/licenses/LICENSE,sha256=JFWuiKYRXKKMEAsX0aZp3hBcju-HYflJ2rwJAGwbCJo,1080
1741
+ fastlifeweb-0.26.2.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: pdm-backend (2.4.3)
2
+ Generator: pdm-backend (2.4.4)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any