create-prisma-php-app 4.0.0-alpha.19 → 4.0.0-alpha.20
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.
- package/dist/.htaccess +54 -41
- package/dist/bootstrap.php +133 -88
- package/dist/index.js +167 -72
- package/dist/settings/auto-swagger-docs.ts +196 -95
- package/dist/settings/bs-config.ts +56 -58
- package/dist/settings/restart-mcp.ts +58 -0
- package/dist/settings/restart-websocket.ts +44 -45
- package/dist/settings/utils.ts +240 -0
- package/dist/src/Lib/Headers/Boom.php +152 -168
- package/dist/src/Lib/IncludeTracker.php +1 -1
- package/dist/src/Lib/MCP/WeatherTools.php +104 -0
- package/dist/src/Lib/MCP/mcp-server.php +80 -0
- package/dist/src/Lib/MainLayout.php +19 -4
- package/dist/src/Lib/Middleware/CorsMiddleware.php +158 -0
- package/dist/src/Lib/PHPX/Fragment.php +32 -0
- package/dist/src/Lib/PHPX/PHPX.php +119 -5
- package/dist/src/Lib/PHPX/TemplateCompiler.php +78 -21
- package/dist/src/Lib/PHPX/TwMerge.php +305 -154
- package/dist/src/Lib/PHPX/TypeCoercer.php +5 -5
- package/dist/src/Lib/Request.php +4 -1
- package/dist/src/Lib/Security/RateLimiter.php +33 -0
- package/dist/src/Lib/Validator.php +20 -6
- package/dist/src/Lib/Websocket/websocket-server.php +105 -14
- package/dist/src/app/index.php +24 -5
- package/dist/src/app/js/index.js +1 -1
- package/package.json +1 -1
- package/dist/settings/restart-websocket.bat +0 -28
- package/dist/src/app/assets/images/prisma-php-black.svg +0 -6
|
@@ -6,189 +6,340 @@ namespace Lib\PHPX;
|
|
|
6
6
|
|
|
7
7
|
class TwMerge
|
|
8
8
|
{
|
|
9
|
-
private static $
|
|
10
|
-
//
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
9
|
+
private static array $classGroups = [
|
|
10
|
+
// Layout
|
|
11
|
+
'aspect' => ['aspect-auto', 'aspect-square', 'aspect-video', '/^aspect-\[.+\]$/'],
|
|
12
|
+
'container' => ['container'],
|
|
13
|
+
'columns' => ['/^columns-(\d+|auto|\[.+\])$/'],
|
|
14
|
+
'break-after' => ['/^break-after-(auto|avoid|all|avoid-page|page|left|right|column)$/'],
|
|
15
|
+
'break-before' => ['/^break-before-(auto|avoid|all|avoid-page|page|left|right|column)$/'],
|
|
16
|
+
'break-inside' => ['/^break-inside-(auto|avoid|avoid-page|avoid-column)$/'],
|
|
17
|
+
'box-decoration' => ['/^box-decoration-(clone|slice)$/'],
|
|
18
|
+
'box' => ['/^box-(border|content)$/'],
|
|
19
|
+
'display' => ['block', 'inline-block', 'inline', 'flex', 'inline-flex', 'table', 'inline-table', 'table-caption', 'table-cell', 'table-column', 'table-column-group', 'table-footer-group', 'table-header-group', 'table-row-group', 'table-row', 'flow-root', 'grid', 'inline-grid', 'contents', 'list-item', 'hidden'],
|
|
20
|
+
'float' => ['/^float-(right|left|none)$/'],
|
|
21
|
+
'clear' => ['/^clear-(left|right|both|none)$/'],
|
|
22
|
+
'isolation' => ['isolate', 'isolation-auto'],
|
|
23
|
+
'object-fit' => ['/^object-(contain|cover|fill|none|scale-down)$/'],
|
|
24
|
+
'object-position' => ['/^object-(bottom|center|left|left-bottom|left-top|right|right-bottom|right-top|top|\[.+\])$/'],
|
|
25
|
+
'overflow' => ['/^overflow-(auto|hidden|clip|visible|scroll)$/'],
|
|
26
|
+
'overflow-x' => ['/^overflow-x-(auto|hidden|clip|visible|scroll)$/'],
|
|
27
|
+
'overflow-y' => ['/^overflow-y-(auto|hidden|clip|visible|scroll)$/'],
|
|
28
|
+
'overscroll' => ['/^overscroll-(auto|contain|none)$/'],
|
|
29
|
+
'overscroll-x' => ['/^overscroll-x-(auto|contain|none)$/'],
|
|
30
|
+
'overscroll-y' => ['/^overscroll-y-(auto|contain|none)$/'],
|
|
31
|
+
'position' => ['static', 'fixed', 'absolute', 'relative', 'sticky'],
|
|
32
|
+
'inset' => ['/^inset-(\d+(\.\d+)?|auto|\[.+\]|px|full)$/', '/^-inset-(\d+(\.\d+)?|\[.+\])$/'],
|
|
33
|
+
'inset-x' => ['/^inset-x-(\d+(\.\d+)?|auto|\[.+\]|px|full)$/', '/^-inset-x-(\d+(\.\d+)?|\[.+\])$/'],
|
|
34
|
+
'inset-y' => ['/^inset-y-(\d+(\.\d+)?|auto|\[.+\]|px|full)$/', '/^-inset-y-(\d+(\.\d+)?|\[.+\])$/'],
|
|
35
|
+
'top' => ['/^top-(\d+(\.\d+)?|auto|\[.+\]|px|full)$/', '/^-top-(\d+(\.\d+)?|\[.+\])$/'],
|
|
36
|
+
'right' => ['/^right-(\d+(\.\d+)?|auto|\[.+\]|px|full)$/', '/^-right-(\d+(\.\d+)?|\[.+\])$/'],
|
|
37
|
+
'bottom' => ['/^bottom-(\d+(\.\d+)?|auto|\[.+\]|px|full)$/', '/^-bottom-(\d+(\.\d+)?|\[.+\])$/'],
|
|
38
|
+
'left' => ['/^left-(\d+(\.\d+)?|auto|\[.+\]|px|full)$/', '/^-left-(\d+(\.\d+)?|\[.+\])$/'],
|
|
39
|
+
'visibility' => ['visible', 'invisible', 'collapse'],
|
|
40
|
+
'z' => ['/^z-(\d+|auto|\[.+\])$/', '/^-z-(\d+|\[.+\])$/'],
|
|
41
|
+
|
|
42
|
+
// Flexbox & Grid
|
|
43
|
+
'flex-basis' => ['/^basis-(\d+(\.\d+)?\/\d+|\d+(\.\d+)?|auto|px|full|\[.+\])$/'],
|
|
44
|
+
'flex-direction' => ['/^flex-(row|row-reverse|col|col-reverse)$/'],
|
|
45
|
+
'flex-wrap' => ['/^flex-(wrap|wrap-reverse|nowrap)$/'],
|
|
46
|
+
'flex' => ['/^flex-(1|auto|initial|none|\[.+\])$/'],
|
|
47
|
+
'flex-grow' => ['/^grow(-0|\[.+\])?$/'],
|
|
48
|
+
'flex-shrink' => ['/^shrink(-0|\[.+\])?$/'],
|
|
49
|
+
'order' => ['/^order-(\d+|first|last|none|\[.+\])$/'],
|
|
50
|
+
'grid-template-columns' => ['/^grid-cols-(\d+|none|subgrid|\[.+\])$/'],
|
|
51
|
+
'grid-column' => ['/^col-(auto|span-(\d+|full)|\[.+\])$/'],
|
|
52
|
+
'grid-column-start' => ['/^col-start-(\d+|auto|\[.+\])$/'],
|
|
53
|
+
'grid-column-end' => ['/^col-end-(\d+|auto|\[.+\])$/'],
|
|
54
|
+
'grid-template-rows' => ['/^grid-rows-(\d+|none|subgrid|\[.+\])$/'],
|
|
55
|
+
'grid-row' => ['/^row-(auto|span-(\d+|full)|\[.+\])$/'],
|
|
56
|
+
'grid-row-start' => ['/^row-start-(\d+|auto|\[.+\])$/'],
|
|
57
|
+
'grid-row-end' => ['/^row-end-(\d+|auto|\[.+\])$/'],
|
|
58
|
+
'grid-auto-flow' => ['/^grid-flow-(row|col|dense|row-dense|col-dense)$/'],
|
|
59
|
+
'grid-auto-columns' => ['/^auto-cols-(auto|min|max|fr|\[.+\])$/'],
|
|
60
|
+
'grid-auto-rows' => ['/^auto-rows-(auto|min|max|fr|\[.+\])$/'],
|
|
61
|
+
'gap' => ['/^gap-(\d+(\.\d+)?|px|\[.+\])$/'],
|
|
62
|
+
'gap-x' => ['/^gap-x-(\d+(\.\d+)?|px|\[.+\])$/'],
|
|
63
|
+
'gap-y' => ['/^gap-y-(\d+(\.\d+)?|px|\[.+\])$/'],
|
|
64
|
+
'justify-content' => ['/^justify-(start|end|center|between|around|evenly)$/'],
|
|
65
|
+
'justify-items' => ['/^justify-items-(start|end|center|stretch)$/'],
|
|
66
|
+
'justify-self' => ['/^justify-self-(auto|start|end|center|stretch)$/'],
|
|
67
|
+
'align-content' => ['/^content-(center|start|end|between|around|evenly|baseline|stretch)$/'],
|
|
68
|
+
'align-items' => ['/^items-(start|end|center|baseline|stretch)$/'],
|
|
69
|
+
'align-self' => ['/^self-(auto|start|end|center|stretch|baseline)$/'],
|
|
70
|
+
'place-content' => ['/^place-content-(center|start|end|between|around|evenly|baseline|stretch)$/'],
|
|
71
|
+
'place-items' => ['/^place-items-(start|end|center|baseline|stretch)$/'],
|
|
72
|
+
'place-self' => ['/^place-self-(auto|start|end|center|stretch)$/'],
|
|
73
|
+
|
|
74
|
+
// Spacing
|
|
75
|
+
'p' => ['/^p-(\d+(\.\d+)?|px|\[.+\])$/'],
|
|
76
|
+
'px' => ['/^px-(\d+(\.\d+)?|px|\[.+\])$/'],
|
|
77
|
+
'py' => ['/^py-(\d+(\.\d+)?|px|\[.+\])$/'],
|
|
78
|
+
'pt' => ['/^pt-(\d+(\.\d+)?|px|\[.+\])$/'],
|
|
79
|
+
'pr' => ['/^pr-(\d+(\.\d+)?|px|\[.+\])$/'],
|
|
80
|
+
'pb' => ['/^pb-(\d+(\.\d+)?|px|\[.+\])$/'],
|
|
81
|
+
'pl' => ['/^pl-(\d+(\.\d+)?|px|\[.+\])$/'],
|
|
82
|
+
'm' => ['/^-?m-(\d+(\.\d+)?|px|auto|\[.+\])$/'],
|
|
83
|
+
'mx' => ['/^-?mx-(\d+(\.\d+)?|px|auto|\[.+\])$/'],
|
|
84
|
+
'my' => ['/^-?my-(\d+(\.\d+)?|px|auto|\[.+\])$/'],
|
|
85
|
+
'mt' => ['/^-?mt-(\d+(\.\d+)?|px|auto|\[.+\])$/'],
|
|
86
|
+
'mr' => ['/^-?mr-(\d+(\.\d+)?|px|auto|\[.+\])$/'],
|
|
87
|
+
'mb' => ['/^-?mb-(\d+(\.\d+)?|px|auto|\[.+\])$/'],
|
|
88
|
+
'ml' => ['/^-?ml-(\d+(\.\d+)?|px|auto|\[.+\])$/'],
|
|
89
|
+
'space-x' => ['/^-?space-x-(\d+(\.\d+)?|px|reverse|\[.+\])$/'],
|
|
90
|
+
'space-y' => ['/^-?space-y-(\d+(\.\d+)?|px|reverse|\[.+\])$/'],
|
|
91
|
+
|
|
92
|
+
// Sizing
|
|
93
|
+
'w' => ['/^w-(?!$).+$/'],
|
|
94
|
+
'min-w' => ['/^min-w-(?!$).+$/'],
|
|
95
|
+
'max-w' => ['/^max-w-(?!$).+$/'],
|
|
96
|
+
'h' => ['/^h-(?!$).+$/'],
|
|
97
|
+
'min-h' => ['/^min-h-(?!$).+$/'],
|
|
98
|
+
'max-h' => ['/^max-h-(?!$).+$/'],
|
|
99
|
+
'size' => ['/^size-(?!$).+$/'],
|
|
100
|
+
|
|
101
|
+
// Typography
|
|
102
|
+
'font-family' => ['/^font-(sans|serif|mono|\[.+\])$/'],
|
|
103
|
+
'font-size' => ['/^text-(xs|sm|base|lg|xl|[2-9]xl|\[.+\])$/'],
|
|
104
|
+
'font-smoothing' => ['antialiased', 'subpixel-antialiased'],
|
|
105
|
+
'font-style' => ['italic', 'not-italic'],
|
|
106
|
+
'font-weight' => ['/^font-(thin|extralight|light|normal|medium|semibold|bold|extrabold|black|\[.+\])$/'],
|
|
107
|
+
'font-variant-numeric' => ['/^(normal-nums|ordinal|slashed-zero|lining-nums|oldstyle-nums|proportional-nums|tabular-nums|diagonal-fractions|stacked-fractions)$/'],
|
|
108
|
+
'letter-spacing' => ['/^tracking-(tighter|tight|normal|wide|wider|widest|\[.+\])$/'],
|
|
109
|
+
'line-clamp' => ['/^line-clamp-(\d+|none)$/'],
|
|
110
|
+
'line-height' => ['/^leading-(\d+(\.\d+)?|none|tight|snug|normal|relaxed|loose|\[.+\])$/'],
|
|
111
|
+
'list-image' => ['/^list-image-(none|\[.+\])$/'],
|
|
112
|
+
'list-style-position' => ['/^list-(inside|outside)$/'],
|
|
113
|
+
'list-style-type' => ['/^list-(none|disc|decimal|\[.+\])$/'],
|
|
114
|
+
'text-align' => ['/^text-(left|center|right|justify|start|end)$/'],
|
|
115
|
+
'text-color' => ['/^text-(?!xs$|sm$|base$|lg$|xl$|[2-9]xl$).+$/'],
|
|
116
|
+
'text-decoration' => ['underline', 'overline', 'line-through', 'no-underline'],
|
|
117
|
+
'text-decoration-color' => ['/^decoration-(?!auto$|from-font$|\d+$|px$).+$/'],
|
|
118
|
+
'text-decoration-style' => ['/^decoration-(solid|double|dotted|dashed|wavy)$/'],
|
|
119
|
+
'text-decoration-thickness' => ['/^decoration-(auto|from-font|\d+|px|\[.+\])$/'],
|
|
120
|
+
'text-underline-offset' => ['/^underline-offset-(auto|\d+|px|\[.+\])$/'],
|
|
121
|
+
'text-transform' => ['uppercase', 'lowercase', 'capitalize', 'normal-case'],
|
|
122
|
+
'text-overflow' => ['truncate', 'text-ellipsis', 'text-clip'],
|
|
123
|
+
'text-wrap' => ['/^text-(wrap|nowrap|balance|pretty)$/'],
|
|
124
|
+
'text-indent' => ['/^indent-(\d+(\.\d+)?|px|\[.+\])$/'],
|
|
125
|
+
'vertical-align' => ['/^align-(baseline|top|middle|bottom|text-top|text-bottom|sub|super|\[.+\])$/'],
|
|
126
|
+
'whitespace' => ['/^whitespace-(normal|nowrap|pre|pre-line|pre-wrap|break-spaces)$/'],
|
|
127
|
+
'word-break' => ['/^break-(normal|words|all|keep)$/'],
|
|
128
|
+
'hyphens' => ['/^hyphens-(none|manual|auto)$/'],
|
|
129
|
+
|
|
130
|
+
// Backgrounds
|
|
131
|
+
'bg-attachment' => ['/^bg-(fixed|local|scroll)$/'],
|
|
132
|
+
'bg-clip' => ['/^bg-clip-(border|padding|content|text)$/'],
|
|
133
|
+
'bg-color' => ['/^bg-(?!fixed$|local$|scroll$|clip-|origin-|no-repeat$|repeat|auto$|cover$|contain$|none$|gradient-to-).+$/'],
|
|
134
|
+
'bg-origin' => ['/^bg-origin-(border|padding|content)$/'],
|
|
135
|
+
'bg-position' => ['/^bg-(bottom|center|left|left-bottom|left-top|right|right-bottom|right-top|top|\[.+\])$/'],
|
|
136
|
+
'bg-repeat' => ['/^bg-(no-repeat|repeat|repeat-x|repeat-y|repeat-round|repeat-space)$/'],
|
|
137
|
+
'bg-size' => ['/^bg-(auto|cover|contain|\[.+\])$/'],
|
|
138
|
+
'bg-image' => ['/^bg-(none|gradient-to-(t|tr|r|br|b|bl|l|tl)|\[.+\])$/'],
|
|
139
|
+
'gradient-from' => ['/^from-.+$/'],
|
|
140
|
+
'gradient-via' => ['/^via-.+$/'],
|
|
141
|
+
'gradient-to' => ['/^to-.+$/'],
|
|
142
|
+
|
|
143
|
+
// Borders
|
|
144
|
+
'rounded' => ['/^rounded(-(\w+))?(-(\d+(\.\d+)?|px|full|\[.+\]))?$/'],
|
|
145
|
+
'border-w-all' => ['/^border(-(\d+|px|\[.+\]))?$/', '/^border-0$/'],
|
|
146
|
+
'border-w-x' => ['/^border-x(-(\d+|px|\[.+\]))?$/'],
|
|
147
|
+
'border-w-y' => ['/^border-y(-(\d+|px|\[.+\]))?$/'],
|
|
148
|
+
'border-w-t' => ['/^border-t(-(\d+|px|\[.+\]))?$/'],
|
|
149
|
+
'border-w-r' => ['/^border-r(-(\d+|px|\[.+\]))?$/'],
|
|
150
|
+
'border-w-b' => ['/^border-b(-(\d+|px|\[.+\]))?$/'],
|
|
151
|
+
'border-w-l' => ['/^border-l(-(\d+|px|\[.+\]))?$/'],
|
|
152
|
+
'border-color' => ['/^border(-[trbl])?-.+$/'],
|
|
153
|
+
'border-style' => ['/^border-(solid|dashed|dotted|double|hidden|none)$/'],
|
|
154
|
+
'divide-x' => ['/^divide-x(-(\d+|px|reverse|\[.+\]))?$/'],
|
|
155
|
+
'divide-y' => ['/^divide-y(-(\d+|px|reverse|\[.+\]))?$/'],
|
|
156
|
+
'divide-color' => ['/^divide-.+$/'],
|
|
157
|
+
'divide-style' => ['/^divide-(solid|dashed|dotted|double|none)$/'],
|
|
158
|
+
'outline-w' => ['/^outline(-(\d+|px|\[.+\]))?$/'],
|
|
159
|
+
'outline-color' => ['/^outline-.+$/'],
|
|
160
|
+
'outline-style' => ['/^outline-(none|solid|dashed|dotted|double)$/'],
|
|
161
|
+
'outline-offset' => ['/^outline-offset-(\d+|px|\[.+\])$/'],
|
|
162
|
+
'ring-w' => ['/^ring(-(\d+|px|inset|\[.+\]))?$/'],
|
|
163
|
+
'ring-color' => ['/^ring-.+$/'],
|
|
164
|
+
'ring-offset-w' => ['/^ring-offset-(\d+|px|\[.+\])$/'],
|
|
165
|
+
'ring-offset-color' => ['/^ring-offset-.+$/'],
|
|
166
|
+
|
|
167
|
+
// Effects
|
|
168
|
+
'shadow' => ['/^shadow(-(\w+|\[.+\]))?$/'],
|
|
169
|
+
'shadow-color' => ['/^shadow-.+$/'],
|
|
170
|
+
'opacity' => ['/^opacity-(\d+|\[.+\])$/'],
|
|
171
|
+
'mix-blend' => ['/^mix-blend-(normal|multiply|screen|overlay|darken|lighten|color-dodge|color-burn|hard-light|soft-light|difference|exclusion|hue|saturation|color|luminosity|plus-lighter)$/'],
|
|
172
|
+
'bg-blend' => ['/^bg-blend-(normal|multiply|screen|overlay|darken|lighten|color-dodge|color-burn|hard-light|soft-light|difference|exclusion|hue|saturation|color|luminosity)$/'],
|
|
173
|
+
|
|
174
|
+
// Filters
|
|
175
|
+
'blur' => ['/^blur(-(\w+|\[.+\]))?$/'],
|
|
176
|
+
'brightness' => ['/^brightness-(\d+|\[.+\])$/'],
|
|
177
|
+
'contrast' => ['/^contrast-(\d+|\[.+\])$/'],
|
|
178
|
+
'drop-shadow' => ['/^drop-shadow(-(\w+|\[.+\]))?$/'],
|
|
179
|
+
'grayscale' => ['/^grayscale(-(\d+|\[.+\]))?$/'],
|
|
180
|
+
'hue-rotate' => ['/^hue-rotate-(\d+|\[.+\])$/'],
|
|
181
|
+
'invert' => ['/^invert(-(\d+|\[.+\]))?$/'],
|
|
182
|
+
'saturate' => ['/^saturate-(\d+|\[.+\])$/'],
|
|
183
|
+
'sepia' => ['/^sepia(-(\d+|\[.+\]))?$/'],
|
|
184
|
+
'backdrop-blur' => ['/^backdrop-blur(-(\w+|\[.+\]))?$/'],
|
|
185
|
+
'backdrop-brightness' => ['/^backdrop-brightness-(\d+|\[.+\])$/'],
|
|
186
|
+
'backdrop-contrast' => ['/^backdrop-contrast-(\d+|\[.+\])$/'],
|
|
187
|
+
'backdrop-grayscale' => ['/^backdrop-grayscale(-(\d+|\[.+\]))?$/'],
|
|
188
|
+
'backdrop-hue-rotate' => ['/^backdrop-hue-rotate-(\d+|\[.+\])$/'],
|
|
189
|
+
'backdrop-invert' => ['/^backdrop-invert(-(\d+|\[.+\]))?$/'],
|
|
190
|
+
'backdrop-opacity' => ['/^backdrop-opacity-(\d+|\[.+\])$/'],
|
|
191
|
+
'backdrop-saturate' => ['/^backdrop-saturate-(\d+|\[.+\])$/'],
|
|
192
|
+
'backdrop-sepia' => ['/^backdrop-sepia(-(\d+|\[.+\]))?$/'],
|
|
193
|
+
|
|
194
|
+
// Transitions & Animation
|
|
195
|
+
'transition-property' => ['/^transition(-(\w+|\[.+\]))?$/'],
|
|
196
|
+
'transition-duration' => ['/^duration-(\d+|\[.+\])$/'],
|
|
197
|
+
'transition-timing' => ['/^ease-(linear|in|out|in-out|\[.+\])$/'],
|
|
198
|
+
'transition-delay' => ['/^delay-(\d+|\[.+\])$/'],
|
|
199
|
+
'animate' => ['/^animate-(none|spin|ping|pulse|bounce|\[.+\])$/'],
|
|
200
|
+
|
|
201
|
+
// Transforms
|
|
202
|
+
'scale' => ['/^scale(-[xy])?-(\d+|\[.+\])$/'],
|
|
203
|
+
'rotate' => ['/^-?rotate-(\d+|\[.+\])$/'],
|
|
204
|
+
'translate-x' => ['/^-?translate-x-(\d+(\.\d+)?\/\d+|\d+(\.\d+)?|px|full|\[.+\])$/'],
|
|
205
|
+
'translate-y' => ['/^-?translate-y-(\d+(\.\d+)?\/\d+|\d+(\.\d+)?|px|full|\[.+\])$/'],
|
|
206
|
+
'skew-x' => ['/^-?skew-x-(\d+|\[.+\])$/'],
|
|
207
|
+
'skew-y' => ['/^-?skew-y-(\d+|\[.+\])$/'],
|
|
208
|
+
'transform-origin' => ['/^origin-(center|top|top-right|right|bottom-right|bottom|bottom-left|left|top-left|\[.+\])$/'],
|
|
209
|
+
|
|
210
|
+
// Interactivity
|
|
211
|
+
'accent' => ['/^accent-.+$/'],
|
|
212
|
+
'appearance' => ['/^appearance-(none|auto)$/'],
|
|
213
|
+
'cursor' => ['/^cursor-(auto|default|pointer|wait|text|move|help|not-allowed|none|context-menu|progress|cell|crosshair|vertical-text|alias|copy|no-drop|grab|grabbing|all-scroll|col-resize|row-resize|n-resize|e-resize|s-resize|w-resize|ne-resize|nw-resize|se-resize|sw-resize|ew-resize|ns-resize|nesw-resize|nwse-resize|zoom-in|zoom-out|\[.+\])$/'],
|
|
214
|
+
'caret-color' => ['/^caret-.+$/'],
|
|
215
|
+
'pointer-events' => ['/^pointer-events-(none|auto)$/'],
|
|
216
|
+
'resize' => ['/^resize(-none|-y|-x)?$/'],
|
|
217
|
+
'scroll-behavior' => ['/^scroll-(auto|smooth)$/'],
|
|
218
|
+
'scroll-m' => ['/^scroll-m[trbl]?x?y?-(\d+(\.\d+)?|px|\[.+\])$/'],
|
|
219
|
+
'scroll-p' => ['/^scroll-p[trbl]?x?y?-(\d+(\.\d+)?|px|\[.+\])$/'],
|
|
220
|
+
'scroll-snap-align' => ['/^snap-(start|end|center|align-none)$/'],
|
|
221
|
+
'scroll-snap-stop' => ['/^snap-(normal|always)$/'],
|
|
222
|
+
'scroll-snap-type' => ['/^snap-(none|x|y|both|mandatory|proximity)$/'],
|
|
223
|
+
'touch' => ['/^touch-(auto|none|pan-x|pan-left|pan-right|pan-y|pan-up|pan-down|pinch-zoom|manipulation)$/'],
|
|
224
|
+
'user-select' => ['/^select-(none|text|all|auto)$/'],
|
|
225
|
+
'will-change' => ['/^will-change-(auto|scroll|contents|transform|\[.+\])$/'],
|
|
64
226
|
];
|
|
65
227
|
|
|
66
|
-
private static $
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
228
|
+
private static array $conflictingClassGroups = [
|
|
229
|
+
'overflow' => ['overflow-x', 'overflow-y'],
|
|
230
|
+
'overscroll' => ['overscroll-x', 'overscroll-y'],
|
|
231
|
+
'inset' => ['inset-x', 'inset-y', 'top', 'right', 'bottom', 'left'],
|
|
232
|
+
'inset-x' => ['right', 'left'],
|
|
233
|
+
'inset-y' => ['top', 'bottom'],
|
|
234
|
+
'flex' => ['basis', 'grow', 'shrink'],
|
|
235
|
+
'gap' => ['gap-x', 'gap-y'],
|
|
236
|
+
'p' => ['px', 'py', 'pt', 'pr', 'pb', 'pl'],
|
|
237
|
+
'px' => ['pr', 'pl'],
|
|
238
|
+
'py' => ['pt', 'pb'],
|
|
239
|
+
'm' => ['mx', 'my', 'mt', 'mr', 'mb', 'ml'],
|
|
240
|
+
'mx' => ['mr', 'ml'],
|
|
241
|
+
'my' => ['mt', 'mb'],
|
|
242
|
+
'font-size' => ['line-height'],
|
|
243
|
+
'bg-color' => ['bg-color'],
|
|
244
|
+
'text-color' => ['text-color'],
|
|
245
|
+
'fvn-normal' => ['fvn-ordinal', 'fvn-slashed-zero', 'fvn-figure', 'fvn-spacing', 'fvn-fraction'],
|
|
246
|
+
'rounded' => ['rounded-s', 'rounded-e', 'rounded-t', 'rounded-r', 'rounded-b', 'rounded-l', 'rounded-ss', 'rounded-se', 'rounded-ee', 'rounded-es', 'rounded-tl', 'rounded-tr', 'rounded-br', 'rounded-bl'],
|
|
247
|
+
'rounded-s' => ['rounded-ss', 'rounded-es'],
|
|
248
|
+
'rounded-e' => ['rounded-se', 'rounded-ee'],
|
|
249
|
+
'rounded-t' => ['rounded-tl', 'rounded-tr'],
|
|
250
|
+
'rounded-r' => ['rounded-tr', 'rounded-br'],
|
|
251
|
+
'rounded-b' => ['rounded-br', 'rounded-bl'],
|
|
252
|
+
'rounded-l' => ['rounded-tl', 'rounded-bl'],
|
|
253
|
+
'border-spacing' => ['border-spacing-x', 'border-spacing-y'],
|
|
254
|
+
'border-w-all' => [],
|
|
255
|
+
'border-w-x' => ['border-w-all'],
|
|
256
|
+
'border-w-y' => ['border-w-all'],
|
|
257
|
+
'border-w-t' => ['border-w-all', 'border-w-y'],
|
|
258
|
+
'border-w-r' => ['border-w-all', 'border-w-x'],
|
|
259
|
+
'border-w-b' => ['border-w-all', 'border-w-y'],
|
|
260
|
+
'border-w-l' => ['border-w-all', 'border-w-x'],
|
|
261
|
+
'border-color' => ['border-color-t', 'border-color-r', 'border-color-b', 'border-color-l'],
|
|
262
|
+
'border-color-x' => ['border-color-r', 'border-color-l'],
|
|
263
|
+
'border-color-y' => ['border-color-t', 'border-color-b'],
|
|
264
|
+
'scroll-m' => ['scroll-mx', 'scroll-my', 'scroll-mt', 'scroll-mr', 'scroll-mb', 'scroll-ml'],
|
|
265
|
+
'scroll-mx' => ['scroll-mr', 'scroll-ml'],
|
|
266
|
+
'scroll-my' => ['scroll-mt', 'scroll-mb'],
|
|
267
|
+
'scroll-p' => ['scroll-px', 'scroll-py', 'scroll-pt', 'scroll-pr', 'scroll-pb', 'scroll-pl'],
|
|
268
|
+
'scroll-px' => ['scroll-pr', 'scroll-pl'],
|
|
269
|
+
'scroll-py' => ['scroll-pt', 'scroll-pb'],
|
|
108
270
|
];
|
|
109
271
|
|
|
110
|
-
|
|
111
|
-
* Merges multiple CSS class strings or arrays of CSS class strings into a single, optimized CSS class string.
|
|
112
|
-
*
|
|
113
|
-
* @param string|array ...$classes The CSS classes to be merged.
|
|
114
|
-
* @return string A single CSS class string with duplicates and conflicts resolved.
|
|
115
|
-
*/
|
|
116
|
-
public static function mergeClasses(string|array ...$classes): string
|
|
272
|
+
public static function merge(string|array ...$inputs): string
|
|
117
273
|
{
|
|
118
|
-
$
|
|
119
|
-
|
|
120
|
-
foreach ($classes as $class) {
|
|
121
|
-
// Handle arrays by flattening them into strings.
|
|
122
|
-
$classList = is_array($class) ? $class : [$class];
|
|
123
|
-
foreach ($classList as $item) {
|
|
124
|
-
if (!empty(trim($item))) {
|
|
125
|
-
// Split the classes by any whitespace characters.
|
|
126
|
-
$splitClasses = preg_split("/\s+/", $item);
|
|
127
|
-
foreach ($splitClasses as $individualClass) {
|
|
128
|
-
$classKey = self::getClassGroup($individualClass);
|
|
129
|
-
|
|
130
|
-
// If the class is non-responsive (no colon), remove any responsive variants for the same base.
|
|
131
|
-
if (strpos($classKey, ':') === false) {
|
|
132
|
-
$baseGroup = $classKey;
|
|
133
|
-
foreach ($classArray as $existingKey => $existingClass) {
|
|
134
|
-
|
|
135
|
-
if (
|
|
136
|
-
is_string($existingKey) // make sure we have a string
|
|
137
|
-
&& $existingKey !== $baseGroup
|
|
138
|
-
&& substr($existingKey, -strlen($baseGroup)) === $baseGroup
|
|
139
|
-
) {
|
|
140
|
-
unset($classArray[$existingKey]);
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
}
|
|
274
|
+
$allClasses = [];
|
|
144
275
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
276
|
+
foreach ($inputs as $input) {
|
|
277
|
+
if (is_array($input)) {
|
|
278
|
+
$allClasses = array_merge($allClasses, $input);
|
|
279
|
+
} else {
|
|
280
|
+
$classes = preg_split('/\s+/', trim($input));
|
|
281
|
+
$allClasses = array_merge($allClasses, array_filter($classes));
|
|
282
|
+
}
|
|
283
|
+
}
|
|
150
284
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
285
|
+
return self::mergeClassList($allClasses);
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
private static function mergeClassList(array $classes): string
|
|
289
|
+
{
|
|
290
|
+
$result = [];
|
|
291
|
+
|
|
292
|
+
foreach ($classes as $originalClass) {
|
|
293
|
+
if (empty(trim($originalClass))) {
|
|
294
|
+
continue;
|
|
155
295
|
}
|
|
296
|
+
|
|
297
|
+
$classKey = self::getClassGroup($originalClass);
|
|
298
|
+
|
|
299
|
+
$conflictingKeys = self::getConflictingKeys($classKey);
|
|
300
|
+
foreach ($conflictingKeys as $key) {
|
|
301
|
+
unset($result[$key]);
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
$result[$classKey] = $originalClass;
|
|
156
305
|
}
|
|
157
306
|
|
|
158
|
-
|
|
159
|
-
return implode(" ", array_values($classArray));
|
|
307
|
+
return implode(' ', array_values($result));
|
|
160
308
|
}
|
|
161
309
|
|
|
162
|
-
private static function getClassGroup($class)
|
|
310
|
+
private static function getClassGroup(string $class): string
|
|
163
311
|
{
|
|
164
|
-
// Match optional prefixes (responsive and variants).
|
|
165
312
|
$pattern = '/^((?:[^:]+:)*)([^:]+)$/';
|
|
166
313
|
if (preg_match($pattern, $class, $matches)) {
|
|
167
314
|
$prefixes = $matches[1];
|
|
168
315
|
$utilityClass = $matches[2];
|
|
169
316
|
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
317
|
+
foreach (self::$classGroups as $groupKey => $patterns) {
|
|
318
|
+
foreach ($patterns as $pattern) {
|
|
319
|
+
if (is_string($pattern) && str_starts_with($pattern, '/')) {
|
|
320
|
+
if (preg_match($pattern, $utilityClass)) {
|
|
321
|
+
return $prefixes . $groupKey;
|
|
322
|
+
}
|
|
323
|
+
} else {
|
|
324
|
+
if ($pattern === $utilityClass) {
|
|
325
|
+
return $prefixes . $groupKey;
|
|
326
|
+
}
|
|
327
|
+
}
|
|
174
328
|
}
|
|
175
329
|
}
|
|
176
|
-
// If no match, use the full class.
|
|
177
330
|
return $prefixes . $utilityClass;
|
|
178
331
|
}
|
|
179
|
-
// For classes without a recognizable prefix, return the class itself.
|
|
180
332
|
return $class;
|
|
181
333
|
}
|
|
182
334
|
|
|
183
|
-
private static function getConflictingKeys($classKey)
|
|
335
|
+
private static function getConflictingKeys(string $classKey): array
|
|
184
336
|
{
|
|
185
|
-
// Remove any responsive or variant prefixes.
|
|
186
337
|
$baseClassKey = preg_replace("/^(?:[^:]+:)+/", "", $classKey);
|
|
187
|
-
if (isset(self::$
|
|
188
|
-
$prefix = preg_replace("/" . preg_quote($baseClassKey, "/") . '$/', "", $classKey);
|
|
338
|
+
if (isset(self::$conflictingClassGroups[$baseClassKey])) {
|
|
339
|
+
$prefix = preg_replace("/" . preg_quote($baseClassKey, "/") . '$/i', "", $classKey);
|
|
189
340
|
return array_map(function ($conflict) use ($prefix) {
|
|
190
341
|
return $prefix . $conflict;
|
|
191
|
-
}, self::$
|
|
342
|
+
}, self::$conflictingClassGroups[$baseClassKey]);
|
|
192
343
|
}
|
|
193
344
|
return [$classKey];
|
|
194
345
|
}
|
|
@@ -245,10 +245,10 @@ class TypeCoercer
|
|
|
245
245
|
|
|
246
246
|
private static function isArrayLike(string $value): bool
|
|
247
247
|
{
|
|
248
|
-
return Validator::json($value)
|
|
249
|
-
str_contains($value, ',')
|
|
250
|
-
str_contains($value, '[')
|
|
251
|
-
str_contains($value, '{');
|
|
248
|
+
return Validator::json($value) === true
|
|
249
|
+
|| str_contains($value, ',')
|
|
250
|
+
|| str_contains($value, '[')
|
|
251
|
+
|| str_contains($value, '{');
|
|
252
252
|
}
|
|
253
253
|
|
|
254
254
|
private static function getNormalizedType(mixed $value): string
|
|
@@ -328,7 +328,7 @@ class TypeCoercer
|
|
|
328
328
|
return $value;
|
|
329
329
|
}
|
|
330
330
|
if (is_string($value)) {
|
|
331
|
-
if (Validator::json($value)) {
|
|
331
|
+
if (Validator::json($value) === true) {
|
|
332
332
|
$decoded = json_decode($value, true);
|
|
333
333
|
if (is_array($decoded)) {
|
|
334
334
|
return $decoded;
|
package/dist/src/Lib/Request.php
CHANGED
|
@@ -193,8 +193,10 @@ class Request
|
|
|
193
193
|
public static string $requestedWith = '';
|
|
194
194
|
|
|
195
195
|
/**
|
|
196
|
-
*
|
|
196
|
+
* @var string $remoteAddr Holds the remote address of the request.
|
|
197
197
|
*/
|
|
198
|
+
public static string $remoteAddr = '';
|
|
199
|
+
|
|
198
200
|
public static function init(): void
|
|
199
201
|
{
|
|
200
202
|
self::$params = new ArrayObject([], ArrayObject::ARRAY_AS_PROPS);
|
|
@@ -222,6 +224,7 @@ class Request
|
|
|
222
224
|
self::$localStorage = self::getLocalStorage();
|
|
223
225
|
self::$protocol = self::getProtocol();
|
|
224
226
|
self::$documentUrl = self::$protocol . self::$domainName . self::$scriptName;
|
|
227
|
+
self::$remoteAddr = $_SERVER['REMOTE_ADDR'] ?? 'Unknown';
|
|
225
228
|
}
|
|
226
229
|
|
|
227
230
|
/**
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
<?php
|
|
2
|
+
namespace Lib\Security;
|
|
3
|
+
|
|
4
|
+
use Lib\Headers\Boom;
|
|
5
|
+
|
|
6
|
+
final class RateLimiter
|
|
7
|
+
{
|
|
8
|
+
/**
|
|
9
|
+
* Lanza HTTP 429 si se supera el máximo de intentos en la ventana dada.
|
|
10
|
+
*
|
|
11
|
+
* @param string $key Identificador único (p. ej. IP o user‑id).
|
|
12
|
+
* @param int $maxAttempts Nº de peticiones permitidas.
|
|
13
|
+
* @param int $seconds Ventana de tiempo en segundos.
|
|
14
|
+
*/
|
|
15
|
+
public static function check(string $key, int $maxAttempts = 60, int $seconds = 60): void
|
|
16
|
+
{
|
|
17
|
+
if (!function_exists('apcu_fetch')) {
|
|
18
|
+
// APCu no instalado: conviene registrar un “fallback” o lanzar excepción.
|
|
19
|
+
Boom::internal("APCu extension missing for rate‑limit.")->toResponse();
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
$apcuKey = "ratelimit:{$key}";
|
|
23
|
+
$current = apcu_fetch($apcuKey) ?: 0;
|
|
24
|
+
|
|
25
|
+
if ($current >= $maxAttempts) {
|
|
26
|
+
// HTTP 429 Too Many Requests
|
|
27
|
+
Boom::tooManyRequests('Rate limit exceeded, try again later.')->toResponse();
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Incrementa contador y refresca TTL
|
|
31
|
+
apcu_store($apcuKey, $current + 1, $seconds);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -284,15 +284,29 @@ final class Validator
|
|
|
284
284
|
// Other Validation
|
|
285
285
|
|
|
286
286
|
/**
|
|
287
|
-
* Validate a JSON string.
|
|
287
|
+
* Validate a JSON string or convert an array to a JSON string.
|
|
288
288
|
*
|
|
289
|
-
*
|
|
290
|
-
*
|
|
289
|
+
* This function checks if the input is a valid JSON string. If it is, it returns the string.
|
|
290
|
+
* If the input is an array, it converts it to a JSON string with specific options.
|
|
291
|
+
* If the input is invalid, it returns an error message.
|
|
292
|
+
*
|
|
293
|
+
* @param mixed $value The value to validate or convert.
|
|
294
|
+
* @return string The valid JSON string or an error message if invalid.
|
|
291
295
|
*/
|
|
292
|
-
public static function json($value):
|
|
296
|
+
public static function json(mixed $value): string
|
|
293
297
|
{
|
|
294
|
-
|
|
295
|
-
|
|
298
|
+
if (is_string($value)) {
|
|
299
|
+
json_decode($value);
|
|
300
|
+
if (json_last_error() !== JSON_ERROR_NONE) {
|
|
301
|
+
return json_last_error_msg();
|
|
302
|
+
}
|
|
303
|
+
return $value;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
return json_encode(
|
|
307
|
+
$value,
|
|
308
|
+
JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES
|
|
309
|
+
);
|
|
296
310
|
}
|
|
297
311
|
|
|
298
312
|
/**
|