diff --git a/.astro/settings.json b/.astro/settings.json
new file mode 100644
index 0000000..f881d68
--- /dev/null
+++ b/.astro/settings.json
@@ -0,0 +1,5 @@
+{
+ "_variables": {
+ "lastUpdateCheck": 1783587315248
+ }
+}
\ No newline at end of file
diff --git a/.astro/types.d.ts b/.astro/types.d.ts
new file mode 100644
index 0000000..f964fe0
--- /dev/null
+++ b/.astro/types.d.ts
@@ -0,0 +1 @@
+///
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..3c3629e
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+node_modules
diff --git a/astro.config.mjs b/astro.config.mjs
new file mode 100644
index 0000000..dbd74aa
--- /dev/null
+++ b/astro.config.mjs
@@ -0,0 +1,9 @@
+import { defineConfig } from 'astro/config';
+import node from '@astrojs/node';
+
+export default defineConfig({
+ output: 'server',
+ adapter: node({
+ mode: 'standalone',
+ })
+});
diff --git a/data/cms.db b/data/cms.db
new file mode 100644
index 0000000..4a3492c
Binary files /dev/null and b/data/cms.db differ
diff --git a/data/cms.db-shm b/data/cms.db-shm
new file mode 100644
index 0000000..707efc0
Binary files /dev/null and b/data/cms.db-shm differ
diff --git a/data/cms.db-wal b/data/cms.db-wal
new file mode 100644
index 0000000..a1cf3ee
Binary files /dev/null and b/data/cms.db-wal differ
diff --git a/data/cms.json b/data/cms.json
new file mode 100644
index 0000000..2a430dd
--- /dev/null
+++ b/data/cms.json
@@ -0,0 +1,71 @@
+{
+ "settings": {
+ "siteInfo": {
+ "name": "روس امروز",
+ "tagline": "خبرگزاری",
+ "description": "خبرگزاری تخصصی پوشش اخبار روسیه، اوراسیا و روابط منطقهای",
+ "copyright": "۱۴۰۵ روس امروز — تمامی حقوق محفوظ است"
+ },
+ "breakingNews": {
+ "label": "فوری",
+ "href": "/posts/آستانا-میزبان-جام-قهرمانان-چهار-قاره-در-اسکیت-نمایشی-در-سال-۲۰۲۷-1782120219014/",
+ "items": [
+ "آستانا میزبان جام قهرمانان چهار قاره در اسکیت نمایشی در سال ۲۰۲۷"
+ ],
+ "postSlugs": [
+ "آستانا-میزبان-جام-قهرمانان-چهار-قاره-در-اسکیت-نمایشی-در-سال-۲۰۲۷-1782120219014"
+ ]
+ },
+ "hotTopics": [
+ "انرژی",
+ "تحریم",
+ "نفت",
+ "گاز",
+ "اوراسیا",
+ "SCO",
+ "روبل",
+ "تجارت",
+ "قفقاز",
+ "ناتو",
+ "بریکس"
+ ],
+ "homeLayout": {
+ "featuredSlug": "در-کوبان-به-فرد-حمله-کننده-در-مرکز-خرید-اتهام-قتل-و-تلاش-برای-قتل-وارد-شد-1782119386187",
+ "sideFeaturedSlugs": [
+ "south-caucasus-geopolitics",
+ "oil-price-new-record"
+ ],
+ "latestCount": 4,
+ "showAd": true
+ },
+ "ad": {
+ "enabled": true,
+ "label": "آگهی",
+ "href": "/advertise/",
+ "text": "جایگاه تبلیغات"
+ },
+ "agents": {
+ "tass": {
+ "processedLinks": [
+ "https://tass.ru/proisshestviya/27834845",
+ "https://tass.ru/sport/27834851",
+ "https://tass.ru/obschestvo/27834629",
+ "https://tass.ru/mezhdunarodnaya-panorama/27834975",
+ "https://tass.ru/obschestvo/27834957",
+ "https://tass.ru/politika/27834811",
+ "https://tass.ru/obschestvo/27818171",
+ "https://tass.ru/obschestvo/27835073",
+ "https://tass.ru/sport/27835167"
+ ]
+ }
+ }
+ },
+ "categories": [
+ "اقتصاد",
+ "سیاست",
+ "انرژی",
+ "فناوری",
+ "بینالملل"
+ ],
+ "posts": []
+}
\ No newline at end of file
diff --git a/dist/client/_astro/_page_.RC7LoXVl.css b/dist/client/_astro/_page_.RC7LoXVl.css
new file mode 100644
index 0000000..c217629
--- /dev/null
+++ b/dist/client/_astro/_page_.RC7LoXVl.css
@@ -0,0 +1 @@
+*,:before,:after{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }::backdrop{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }*,:before,:after{box-sizing:border-box;border-width:0;border-style:solid;border-color:#e5e7eb}:before,:after{--tw-content: ""}html,:host{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;tab-size:4;font-family:ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji",Segoe UI Symbol,"Noto Color Emoji";font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-feature-settings:normal;font-variation-settings:normal;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;letter-spacing:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dl,dd,h1,h2,h3,h4,h5,h6,hr,figure,p,pre{margin:0}fieldset{margin:0;padding:0}legend{padding:0}ol,ul,menu{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}button,[role=button]{cursor:pointer}:disabled{cursor:default}img,svg,video,canvas,audio,iframe,embed,object{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]:where(:not([hidden=until-found])){display:none}.pointer-events-none{pointer-events:none}.fixed{position:fixed}.absolute{position:absolute}.relative{position:relative}.sticky{position:sticky}.inset-0{inset:0}.bottom-0{bottom:0}.left-0{left:0}.left-1\/2{left:50%}.right-0{right:0}.right-2{right:.5rem}.top-0{top:0}.top-1\/2{top:50%}.z-50{z-index:50}.col-span-2{grid-column:span 2 / span 2}.-mx-1{margin-left:-.25rem;margin-right:-.25rem}.mx-auto{margin-left:auto;margin-right:auto}.my-6{margin-top:1.5rem;margin-bottom:1.5rem}.my-7{margin-top:1.75rem;margin-bottom:1.75rem}.mb-2{margin-bottom:.5rem}.mb-3{margin-bottom:.75rem}.mb-4{margin-bottom:1rem}.mb-5{margin-bottom:1.25rem}.mb-6{margin-bottom:1.5rem}.mb-8{margin-bottom:2rem}.mt-1{margin-top:.25rem}.mt-10{margin-top:2.5rem}.mt-12{margin-top:3rem}.mt-2{margin-top:.5rem}.mt-3{margin-top:.75rem}.mt-4{margin-top:1rem}.mt-5{margin-top:1.25rem}.mt-6{margin-top:1.5rem}.mt-8{margin-top:2rem}.line-clamp-2{overflow:hidden;display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:2}.block{display:block}.flex{display:flex}.grid{display:grid}.hidden{display:none}.h-1{height:.25rem}.h-12{height:3rem}.h-14{height:3.5rem}.h-24{height:6rem}.h-28{height:7rem}.h-36{height:9rem}.h-48{height:12rem}.h-64{height:16rem}.h-8{height:2rem}.h-9{height:2.25rem}.h-full{height:100%}.min-h-screen{min-height:100vh}.w-28{width:7rem}.w-6{width:1.5rem}.w-8{width:2rem}.w-9{width:2.25rem}.w-auto{width:auto}.w-full{width:100%}.min-w-0{min-width:0px}.max-w-3xl{max-width:48rem}.max-w-4xl{max-width:56rem}.max-w-7xl{max-width:80rem}.max-w-sm{max-width:24rem}.flex-1{flex:1 1 0%}.flex-shrink-0{flex-shrink:0}.-translate-x-1\/2{--tw-translate-x: -50%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.-translate-y-1\/2{--tw-translate-y: -50%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.resize{resize:both}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.items-start{align-items:flex-start}.items-center{align-items:center}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.justify-around{justify-content:space-around}.gap-0\.5{gap:.125rem}.gap-1{gap:.25rem}.gap-1\.5{gap:.375rem}.gap-2{gap:.5rem}.gap-2\.5{gap:.625rem}.gap-3{gap:.75rem}.gap-4{gap:1rem}.gap-6{gap:1.5rem}.gap-8{gap:2rem}.space-y-2>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.5rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.5rem * var(--tw-space-y-reverse))}.space-y-3>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.75rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.75rem * var(--tw-space-y-reverse))}.space-y-4>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(1rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1rem * var(--tw-space-y-reverse))}.space-y-5>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(1.25rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1.25rem * var(--tw-space-y-reverse))}.space-y-6>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(1.5rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1.5rem * var(--tw-space-y-reverse))}.overflow-auto{overflow:auto}.overflow-hidden{overflow:hidden}.overflow-x-auto{overflow-x:auto}.truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.whitespace-nowrap{white-space:nowrap}.whitespace-pre-wrap{white-space:pre-wrap}.rounded{border-radius:.25rem}.rounded-2xl{border-radius:1rem}.rounded-full{border-radius:9999px}.rounded-lg{border-radius:.5rem}.rounded-xl{border-radius:.75rem}.rounded-l-xl{border-top-left-radius:.75rem;border-bottom-left-radius:.75rem}.border{border-width:1px}.border-y{border-top-width:1px;border-bottom-width:1px}.border-r-4{border-right-width:4px}.border-t{border-top-width:1px}.border-brand{--tw-border-opacity: 1;border-color:rgb(209 0 0 / var(--tw-border-opacity, 1))}.border-gray-100{--tw-border-opacity: 1;border-color:rgb(243 244 246 / var(--tw-border-opacity, 1))}.border-gray-200{--tw-border-opacity: 1;border-color:rgb(229 231 235 / var(--tw-border-opacity, 1))}.border-gray-800{--tw-border-opacity: 1;border-color:rgb(31 41 55 / var(--tw-border-opacity, 1))}.bg-blue-50{--tw-bg-opacity: 1;background-color:rgb(239 246 255 / var(--tw-bg-opacity, 1))}.bg-blue-600{--tw-bg-opacity: 1;background-color:rgb(37 99 235 / var(--tw-bg-opacity, 1))}.bg-brand{--tw-bg-opacity: 1;background-color:rgb(209 0 0 / var(--tw-bg-opacity, 1))}.bg-gray-100{--tw-bg-opacity: 1;background-color:rgb(243 244 246 / var(--tw-bg-opacity, 1))}.bg-gray-50{--tw-bg-opacity: 1;background-color:rgb(249 250 251 / var(--tw-bg-opacity, 1))}.bg-gray-800{--tw-bg-opacity: 1;background-color:rgb(31 41 55 / var(--tw-bg-opacity, 1))}.bg-gray-900{--tw-bg-opacity: 1;background-color:rgb(17 24 39 / var(--tw-bg-opacity, 1))}.bg-green-50{--tw-bg-opacity: 1;background-color:rgb(240 253 244 / var(--tw-bg-opacity, 1))}.bg-green-600{--tw-bg-opacity: 1;background-color:rgb(22 163 74 / var(--tw-bg-opacity, 1))}.bg-red-50{--tw-bg-opacity: 1;background-color:rgb(254 242 242 / var(--tw-bg-opacity, 1))}.bg-red-50\/60{background-color:#fef2f299}.bg-red-600{--tw-bg-opacity: 1;background-color:rgb(220 38 38 / var(--tw-bg-opacity, 1))}.bg-white{--tw-bg-opacity: 1;background-color:rgb(255 255 255 / var(--tw-bg-opacity, 1))}.bg-yellow-50{--tw-bg-opacity: 1;background-color:rgb(254 252 232 / var(--tw-bg-opacity, 1))}.bg-gradient-to-t{background-image:linear-gradient(to top,var(--tw-gradient-stops))}.from-black\/70{--tw-gradient-from: rgb(0 0 0 / .7) var(--tw-gradient-from-position);--tw-gradient-to: rgb(0 0 0 / 0) var(--tw-gradient-to-position);--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to)}.via-black\/20{--tw-gradient-to: rgb(0 0 0 / 0) var(--tw-gradient-to-position);--tw-gradient-stops: var(--tw-gradient-from), rgb(0 0 0 / .2) var(--tw-gradient-via-position), var(--tw-gradient-to)}.to-transparent{--tw-gradient-to: transparent var(--tw-gradient-to-position)}.object-contain{object-fit:contain}.object-cover{object-fit:cover}.p-2{padding:.5rem}.p-3{padding:.75rem}.p-4{padding:1rem}.p-5{padding:1.25rem}.p-6{padding:1.5rem}.p-8{padding:2rem}.px-1{padding-left:.25rem;padding-right:.25rem}.px-2{padding-left:.5rem;padding-right:.5rem}.px-2\.5{padding-left:.625rem;padding-right:.625rem}.px-3{padding-left:.75rem;padding-right:.75rem}.px-3\.5{padding-left:.875rem;padding-right:.875rem}.px-4{padding-left:1rem;padding-right:1rem}.px-5{padding-left:1.25rem;padding-right:1.25rem}.py-0\.5{padding-top:.125rem;padding-bottom:.125rem}.py-1{padding-top:.25rem;padding-bottom:.25rem}.py-1\.5{padding-top:.375rem;padding-bottom:.375rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.py-2\.5{padding-top:.625rem;padding-bottom:.625rem}.py-3{padding-top:.75rem;padding-bottom:.75rem}.py-4{padding-top:1rem;padding-bottom:1rem}.py-6{padding-top:1.5rem;padding-bottom:1.5rem}.py-8{padding-top:2rem;padding-bottom:2rem}.pb-28{padding-bottom:7rem}.pb-3{padding-bottom:.75rem}.pl-3{padding-left:.75rem}.pr-9{padding-right:2.25rem}.pt-10{padding-top:2.5rem}.pt-6{padding-top:1.5rem}.text-center{text-align:center}.font-mono{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}.font-vazir{font-family:Vazirmatn,sans-serif}.text-2xl{font-size:1.5rem;line-height:2rem}.text-3xl{font-size:1.875rem;line-height:2.25rem}.text-4xl{font-size:2.25rem;line-height:2.5rem}.text-5xl{font-size:3rem;line-height:1}.text-6xl{font-size:3.75rem;line-height:1}.text-base{font-size:1rem;line-height:1.5rem}.text-lg{font-size:1.125rem;line-height:1.75rem}.text-sm{font-size:.875rem;line-height:1.25rem}.text-xl{font-size:1.25rem;line-height:1.75rem}.text-xs{font-size:.75rem;line-height:1rem}.font-black{font-weight:900}.font-bold{font-weight:700}.font-medium{font-weight:500}.font-semibold{font-weight:600}.leading-6{line-height:1.5rem}.leading-7{line-height:1.75rem}.leading-8{line-height:2rem}.leading-none{line-height:1}.leading-relaxed{line-height:1.625}.leading-snug{line-height:1.375}.text-blue-700{--tw-text-opacity: 1;color:rgb(29 78 216 / var(--tw-text-opacity, 1))}.text-brand{--tw-text-opacity: 1;color:rgb(209 0 0 / var(--tw-text-opacity, 1))}.text-gray-100{--tw-text-opacity: 1;color:rgb(243 244 246 / var(--tw-text-opacity, 1))}.text-gray-300{--tw-text-opacity: 1;color:rgb(209 213 219 / var(--tw-text-opacity, 1))}.text-gray-400{--tw-text-opacity: 1;color:rgb(156 163 175 / var(--tw-text-opacity, 1))}.text-gray-500{--tw-text-opacity: 1;color:rgb(107 114 128 / var(--tw-text-opacity, 1))}.text-gray-600{--tw-text-opacity: 1;color:rgb(75 85 99 / var(--tw-text-opacity, 1))}.text-gray-700{--tw-text-opacity: 1;color:rgb(55 65 81 / var(--tw-text-opacity, 1))}.text-gray-900{--tw-text-opacity: 1;color:rgb(17 24 39 / var(--tw-text-opacity, 1))}.text-green-700{--tw-text-opacity: 1;color:rgb(21 128 61 / var(--tw-text-opacity, 1))}.text-white{--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity, 1))}.text-white\/60{color:#fff9}.text-white\/75{color:#ffffffbf}.text-yellow-300{--tw-text-opacity: 1;color:rgb(253 224 71 / var(--tw-text-opacity, 1))}.text-yellow-700{--tw-text-opacity: 1;color:rgb(161 98 7 / var(--tw-text-opacity, 1))}.opacity-30{opacity:.3}.opacity-40{opacity:.4}.shadow-2xl{--tw-shadow: 0 25px 50px -12px rgb(0 0 0 / .25);--tw-shadow-colored: 0 25px 50px -12px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-sm{--tw-shadow: 0 1px 2px 0 rgb(0 0 0 / .05);--tw-shadow-colored: 0 1px 2px 0 var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.outline-none{outline:2px solid transparent;outline-offset:2px}.brightness-0{--tw-brightness: brightness(0);filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.invert{--tw-invert: invert(100%);filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.filter{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.transition{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-colors{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-shadow{transition-property:box-shadow;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.img-placeholder{background:linear-gradient(135deg,#e5e7eb 25%,#f3f4f6,#e5e7eb 75%);background-size:200% 200%;animation:shimmer 1.5s infinite}@keyframes shimmer{0%{background-position:200% 0}to{background-position:-200% 0}}.scrollbar-hide::-webkit-scrollbar{display:none}.scrollbar-hide{-ms-overflow-style:none;scrollbar-width:none}.line-clamp-2{display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}.line-clamp-3{display:-webkit-box;-webkit-line-clamp:3;-webkit-box-orient:vertical;overflow:hidden}.hover\:border-brand:hover{--tw-border-opacity: 1;border-color:rgb(209 0 0 / var(--tw-border-opacity, 1))}.hover\:bg-brand:hover{--tw-bg-opacity: 1;background-color:rgb(209 0 0 / var(--tw-bg-opacity, 1))}.hover\:bg-gray-200:hover{--tw-bg-opacity: 1;background-color:rgb(229 231 235 / var(--tw-bg-opacity, 1))}.hover\:bg-gray-50:hover{--tw-bg-opacity: 1;background-color:rgb(249 250 251 / var(--tw-bg-opacity, 1))}.hover\:bg-red-50:hover{--tw-bg-opacity: 1;background-color:rgb(254 242 242 / var(--tw-bg-opacity, 1))}.hover\:bg-red-700:hover{--tw-bg-opacity: 1;background-color:rgb(185 28 28 / var(--tw-bg-opacity, 1))}.hover\:text-brand:hover{--tw-text-opacity: 1;color:rgb(209 0 0 / var(--tw-text-opacity, 1))}.hover\:text-white:hover{--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity, 1))}.hover\:underline:hover{text-decoration-line:underline}.hover\:shadow-md:hover{--tw-shadow: 0 4px 6px -1px rgb(0 0 0 / .1), 0 2px 4px -2px rgb(0 0 0 / .1);--tw-shadow-colored: 0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.focus\:bg-white:focus{--tw-bg-opacity: 1;background-color:rgb(255 255 255 / var(--tw-bg-opacity, 1))}.focus\:ring-1:focus{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.focus\:ring-brand:focus{--tw-ring-opacity: 1;--tw-ring-color: rgb(209 0 0 / var(--tw-ring-opacity, 1))}.disabled\:opacity-40:disabled{opacity:.4}.group:hover .group-hover\:text-brand{--tw-text-opacity: 1;color:rgb(209 0 0 / var(--tw-text-opacity, 1))}.group:hover .group-hover\:text-red-200{--tw-text-opacity: 1;color:rgb(254 202 202 / var(--tw-text-opacity, 1))}@media (min-width: 640px){.sm\:block{display:block}.sm\:flex{display:flex}.sm\:h-28{height:7rem}.sm\:h-80{height:20rem}.sm\:w-36{width:9rem}.sm\:w-40{width:10rem}.sm\:flex-shrink-0{flex-shrink:0}.sm\:flex-row{flex-direction:row}.sm\:items-start{align-items:flex-start}.sm\:items-center{align-items:center}.sm\:justify-between{justify-content:space-between}.sm\:p-6{padding:1.5rem}.sm\:p-8{padding:2rem}.sm\:text-2xl{font-size:1.5rem;line-height:2rem}.sm\:text-4xl{font-size:2.25rem;line-height:2.5rem}.sm\:text-base{font-size:1rem;line-height:1.5rem}.sm\:text-sm{font-size:.875rem;line-height:1.25rem}}@media (min-width: 1024px){.lg\:col-span-1{grid-column:span 1 / span 1}.lg\:col-span-2{grid-column:span 2 / span 2}.lg\:mb-6{margin-bottom:1.5rem}.lg\:flex{display:flex}.lg\:hidden{display:none}.lg\:h-16{height:4rem}.lg\:h-36{height:9rem}.lg\:h-96{height:24rem}.lg\:h-\[430px\]{height:430px}.lg\:w-52{width:13rem}.lg\:grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.lg\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.lg\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.lg\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}.lg\:px-8{padding-left:2rem;padding-right:2rem}.lg\:pb-10{padding-bottom:2.5rem}}
diff --git a/dist/client/_astro/hoisted.ClNlXW51.js b/dist/client/_astro/hoisted.ClNlXW51.js
new file mode 100644
index 0000000..13f61ab
--- /dev/null
+++ b/dist/client/_astro/hoisted.ClNlXW51.js
@@ -0,0 +1,7 @@
+const d={file:"#new-image-file",image:"#new-image-field",thumb:"#new-thumb-field",preview:"#new-image-preview",status:"#upload-status",button:"#new-upload-btn"},u=(e,t)=>{const n=document.querySelector(d.status);n&&(n.textContent=t,n.className="text-xs rounded-lg px-2.5 py-1 "+(e==="busy"?"bg-gray-100 text-gray-600":e==="ok"?"bg-green-50 text-green-700":e==="err"?"bg-red-50 text-brand":"text-gray-400"))},m=e=>{const t=document.querySelector(d.preview);t&&(t.innerHTML=e?`
`:'تصویری انتخاب نشده')},l={async uploadImage(e){const t=new FormData;t.append("image",e);const n=await fetch("/api/admin/upload",{method:"POST",body:t}),r=await n.json().catch(()=>({error:"پاسخ نامعتبر سرور"}));if(!n.ok||r.error)throw new Error(r.error??"آپلود ناموفق بود");return r},async runTassAgent(e=3){const t=await fetch("/api/agents/tass",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({limit:e})});if(!t.ok)throw new Error("خطا در اجرای agent");return t.json()},initUpload(){const e=document.querySelector(d.file);if(!e)return;const t=document.querySelector(d.image),n=document.querySelector(d.thumb),r=document.querySelector(d.button),c=()=>{const a=t?.value??"";document.querySelectorAll("[data-insert-image]").forEach(o=>{o.dataset.insertImage=a,o.disabled=!a})},i=(a,o)=>{t&&(t.value=a),n&&(n.value=o),m(o||a),c()},s=async a=>{u("busy","در حال آپلود و پردازش..."),r&&(r.disabled=!0);try{const o=await l.uploadImage(a);i(o.image,o.thumbnail),u("ok","تصویر آپلود شد و آماده استفاده است.")}catch(o){u("err",o instanceof Error?o.message:"آپلود ناموفق بود.")}finally{r&&(r.disabled=!1)}};e.addEventListener("change",()=>{const a=e.files?.[0];a&&s(a)}),r?.addEventListener("click",()=>{const a=e.files?.[0];a?s(a):u("err","ابتدا یک فایل انتخاب کنید.")}),c()},initEditor(){const e=document.querySelector("#post-body-editor");if(!e)return;const t=n=>{const r=e.selectionStart,c=e.selectionEnd,i=e.value.slice(0,r),s=e.value.slice(c),a=i.endsWith(`
+`)||i.length===0?"":`
+
+`,o=s.startsWith(`
+`)||s.length===0?"":`
+
+`;e.value=`${i}${a}${n}${o}${s}`,e.focus();const g=i.length+a.length+n.length;e.setSelectionRange(g,g)};document.querySelectorAll("[data-insert-image]").forEach(n=>{n.addEventListener("click",()=>{const r=n.dataset.insertImage;r&&t(`[image:${r}|توضیح تصویر]`)})}),document.querySelectorAll("[data-editor-command]").forEach(n=>{n.addEventListener("click",()=>{const r=n.dataset.editorCommand;r==="heading"&&t("## تیتر میانی"),r==="quote"&&t("> متن نقلقول"),r==="separator"&&t("---")})})},initConfirm(){document.querySelectorAll("[data-confirm-form]").forEach(e=>{e.addEventListener("submit",t=>{const n=e.dataset.confirmMessage||"برای تایید حذف عبارت confirm را تایپ کنید.",r=window.prompt(n);if(r!=="confirm"){t.preventDefault(),window.alert("حذف لغو شد. عبارت واردشده باید دقیقاً confirm باشد.");return}const c=e.querySelector("[data-confirm-input]");c&&(c.value=r)})})},initTass(){const e=document.querySelector("#run-tass-agent"),t=document.querySelector("#tass-agent-result");!e||!t||e.addEventListener("click",async()=>{e.disabled=!0,e.textContent="در حال اجرا...",t.classList.remove("hidden"),t.textContent="در حال دریافت و ترجمه خبرها...";try{const n=await l.runTassAgent(3);t.textContent=JSON.stringify(n,null,2)}catch(n){t.textContent=n instanceof Error?n.message:"خطا در اجرای agent"}finally{e.disabled=!1,e.textContent="اجرای agent و ساخت پست"}})},initTabs(){const t=document.querySelector("[data-active-tab]")?.dataset.activeTab??"pending",n=Array.from(document.querySelectorAll("[data-tab]")),r=Array.from(document.querySelectorAll("[data-panel]")),c=i=>{n.forEach(s=>{const a=s.dataset.tab===i;s.classList.toggle("bg-brand",a),s.classList.toggle("text-white",a),s.classList.toggle("border-brand",a),s.classList.toggle("bg-white",!a),s.classList.toggle("text-gray-700",!a),s.classList.toggle("border-gray-200",!a),a?s.setAttribute("aria-current","page"):s.removeAttribute("aria-current")}),r.forEach(s=>{s.classList.toggle("hidden",s.dataset.panel!==i)})};c(t),n.forEach(i=>{i.addEventListener("click",s=>{const a=i.dataset.tab;if(!a)return;s.preventDefault(),c(a);const o=new URL(window.location.href);o.searchParams.set("tab",a),o.searchParams.delete("pendingPage"),o.searchParams.delete("approvedPage"),o.searchParams.delete("edit"),window.history.pushState({},"",o)})}),window.addEventListener("popstate",()=>{const i=new URLSearchParams(window.location.search);c(i.get("tab")||t)})}};l.initTabs();l.initUpload();l.initEditor();l.initConfirm();l.initTass();
diff --git a/dist/client/assets/logo.png b/dist/client/assets/logo.png
new file mode 100644
index 0000000..b25b4a2
Binary files /dev/null and b/dist/client/assets/logo.png differ
diff --git a/dist/client/uploads/0dfaf688-a3f5-4914-b0a5-14a720c212bf.webp b/dist/client/uploads/0dfaf688-a3f5-4914-b0a5-14a720c212bf.webp
new file mode 100644
index 0000000..d4edc32
Binary files /dev/null and b/dist/client/uploads/0dfaf688-a3f5-4914-b0a5-14a720c212bf.webp differ
diff --git a/dist/client/uploads/10d5c9bf-849e-4ddd-a7d8-dd5dc6a2e666.webp b/dist/client/uploads/10d5c9bf-849e-4ddd-a7d8-dd5dc6a2e666.webp
new file mode 100644
index 0000000..fc17d70
Binary files /dev/null and b/dist/client/uploads/10d5c9bf-849e-4ddd-a7d8-dd5dc6a2e666.webp differ
diff --git a/dist/client/uploads/15a3a254-12db-4851-a434-cae6e631838d.webp b/dist/client/uploads/15a3a254-12db-4851-a434-cae6e631838d.webp
new file mode 100644
index 0000000..53a8ce5
Binary files /dev/null and b/dist/client/uploads/15a3a254-12db-4851-a434-cae6e631838d.webp differ
diff --git a/dist/client/uploads/23ed9cea-411b-4169-a905-a40bd503d8c6.webp b/dist/client/uploads/23ed9cea-411b-4169-a905-a40bd503d8c6.webp
new file mode 100644
index 0000000..aa7f52f
Binary files /dev/null and b/dist/client/uploads/23ed9cea-411b-4169-a905-a40bd503d8c6.webp differ
diff --git a/dist/client/uploads/2d77aee7-e390-4ca2-8100-d0e6528c145a.webp b/dist/client/uploads/2d77aee7-e390-4ca2-8100-d0e6528c145a.webp
new file mode 100644
index 0000000..53a8ce5
Binary files /dev/null and b/dist/client/uploads/2d77aee7-e390-4ca2-8100-d0e6528c145a.webp differ
diff --git a/dist/client/uploads/3b043c9d-57bf-46f2-9fb2-9c8be53bbcf5.webp b/dist/client/uploads/3b043c9d-57bf-46f2-9fb2-9c8be53bbcf5.webp
new file mode 100644
index 0000000..5eb4423
Binary files /dev/null and b/dist/client/uploads/3b043c9d-57bf-46f2-9fb2-9c8be53bbcf5.webp differ
diff --git a/dist/client/uploads/553d2a2a-b924-401e-988d-1db34a907355.webp b/dist/client/uploads/553d2a2a-b924-401e-988d-1db34a907355.webp
new file mode 100644
index 0000000..8efae9d
Binary files /dev/null and b/dist/client/uploads/553d2a2a-b924-401e-988d-1db34a907355.webp differ
diff --git a/dist/client/uploads/70cc796b-ee3c-49c8-8939-d0fe0dde1554.webp b/dist/client/uploads/70cc796b-ee3c-49c8-8939-d0fe0dde1554.webp
new file mode 100644
index 0000000..7400968
Binary files /dev/null and b/dist/client/uploads/70cc796b-ee3c-49c8-8939-d0fe0dde1554.webp differ
diff --git a/dist/client/uploads/7c59fcfa-93cc-4d1d-b2be-d491eb482302.webp b/dist/client/uploads/7c59fcfa-93cc-4d1d-b2be-d491eb482302.webp
new file mode 100644
index 0000000..31a8a9a
Binary files /dev/null and b/dist/client/uploads/7c59fcfa-93cc-4d1d-b2be-d491eb482302.webp differ
diff --git a/dist/client/uploads/9de7af87-a5f2-4299-bc4e-6c3a17ee9f4d.webp b/dist/client/uploads/9de7af87-a5f2-4299-bc4e-6c3a17ee9f4d.webp
new file mode 100644
index 0000000..44906ca
Binary files /dev/null and b/dist/client/uploads/9de7af87-a5f2-4299-bc4e-6c3a17ee9f4d.webp differ
diff --git a/dist/client/uploads/ad228b50-fe89-40a6-b17d-87440c24d6a5.webp b/dist/client/uploads/ad228b50-fe89-40a6-b17d-87440c24d6a5.webp
new file mode 100644
index 0000000..2362e3a
Binary files /dev/null and b/dist/client/uploads/ad228b50-fe89-40a6-b17d-87440c24d6a5.webp differ
diff --git a/dist/client/uploads/af246b9a-cf64-43c2-934d-edb5d8f0b8ee.webp b/dist/client/uploads/af246b9a-cf64-43c2-934d-edb5d8f0b8ee.webp
new file mode 100644
index 0000000..f1a92e8
Binary files /dev/null and b/dist/client/uploads/af246b9a-cf64-43c2-934d-edb5d8f0b8ee.webp differ
diff --git a/dist/client/uploads/d0858db4-fcc3-43b4-b120-53cc41a4473c.webp b/dist/client/uploads/d0858db4-fcc3-43b4-b120-53cc41a4473c.webp
new file mode 100644
index 0000000..05931ea
Binary files /dev/null and b/dist/client/uploads/d0858db4-fcc3-43b4-b120-53cc41a4473c.webp differ
diff --git a/dist/client/uploads/d3a18a75-4a6f-48c6-b3a5-c10f6fe798d7.webp b/dist/client/uploads/d3a18a75-4a6f-48c6-b3a5-c10f6fe798d7.webp
new file mode 100644
index 0000000..18518e6
Binary files /dev/null and b/dist/client/uploads/d3a18a75-4a6f-48c6-b3a5-c10f6fe798d7.webp differ
diff --git a/dist/client/uploads/eaebf85a-c702-4eb6-ae0d-02a752761ea5.webp b/dist/client/uploads/eaebf85a-c702-4eb6-ae0d-02a752761ea5.webp
new file mode 100644
index 0000000..d4edc32
Binary files /dev/null and b/dist/client/uploads/eaebf85a-c702-4eb6-ae0d-02a752761ea5.webp differ
diff --git a/dist/client/uploads/eb8a9dae-70da-4887-8a36-75f660ccd42a.webp b/dist/client/uploads/eb8a9dae-70da-4887-8a36-75f660ccd42a.webp
new file mode 100644
index 0000000..60a6c4e
Binary files /dev/null and b/dist/client/uploads/eb8a9dae-70da-4887-8a36-75f660ccd42a.webp differ
diff --git a/dist/client/uploads/f1b83771-f192-46f1-8629-6982d42c3b48.webp b/dist/client/uploads/f1b83771-f192-46f1-8629-6982d42c3b48.webp
new file mode 100644
index 0000000..05931ea
Binary files /dev/null and b/dist/client/uploads/f1b83771-f192-46f1-8629-6982d42c3b48.webp differ
diff --git a/dist/client/uploads/fcdcd26a-1029-46ec-adae-8013cbe48022.webp b/dist/client/uploads/fcdcd26a-1029-46ec-adae-8013cbe48022.webp
new file mode 100644
index 0000000..b65eec3
Binary files /dev/null and b/dist/client/uploads/fcdcd26a-1029-46ec-adae-8013cbe48022.webp differ
diff --git a/dist/client/uploads/thumbs/0dfaf688-a3f5-4914-b0a5-14a720c212bf-thumb.webp b/dist/client/uploads/thumbs/0dfaf688-a3f5-4914-b0a5-14a720c212bf-thumb.webp
new file mode 100644
index 0000000..2af2f6c
Binary files /dev/null and b/dist/client/uploads/thumbs/0dfaf688-a3f5-4914-b0a5-14a720c212bf-thumb.webp differ
diff --git a/dist/client/uploads/thumbs/10d5c9bf-849e-4ddd-a7d8-dd5dc6a2e666-thumb.webp b/dist/client/uploads/thumbs/10d5c9bf-849e-4ddd-a7d8-dd5dc6a2e666-thumb.webp
new file mode 100644
index 0000000..5ac4361
Binary files /dev/null and b/dist/client/uploads/thumbs/10d5c9bf-849e-4ddd-a7d8-dd5dc6a2e666-thumb.webp differ
diff --git a/dist/client/uploads/thumbs/15a3a254-12db-4851-a434-cae6e631838d-thumb.webp b/dist/client/uploads/thumbs/15a3a254-12db-4851-a434-cae6e631838d-thumb.webp
new file mode 100644
index 0000000..b1691e7
Binary files /dev/null and b/dist/client/uploads/thumbs/15a3a254-12db-4851-a434-cae6e631838d-thumb.webp differ
diff --git a/dist/client/uploads/thumbs/23ed9cea-411b-4169-a905-a40bd503d8c6-thumb.webp b/dist/client/uploads/thumbs/23ed9cea-411b-4169-a905-a40bd503d8c6-thumb.webp
new file mode 100644
index 0000000..f436842
Binary files /dev/null and b/dist/client/uploads/thumbs/23ed9cea-411b-4169-a905-a40bd503d8c6-thumb.webp differ
diff --git a/dist/client/uploads/thumbs/2d77aee7-e390-4ca2-8100-d0e6528c145a-thumb.webp b/dist/client/uploads/thumbs/2d77aee7-e390-4ca2-8100-d0e6528c145a-thumb.webp
new file mode 100644
index 0000000..b1691e7
Binary files /dev/null and b/dist/client/uploads/thumbs/2d77aee7-e390-4ca2-8100-d0e6528c145a-thumb.webp differ
diff --git a/dist/client/uploads/thumbs/3b043c9d-57bf-46f2-9fb2-9c8be53bbcf5-thumb.webp b/dist/client/uploads/thumbs/3b043c9d-57bf-46f2-9fb2-9c8be53bbcf5-thumb.webp
new file mode 100644
index 0000000..f5b9466
Binary files /dev/null and b/dist/client/uploads/thumbs/3b043c9d-57bf-46f2-9fb2-9c8be53bbcf5-thumb.webp differ
diff --git a/dist/client/uploads/thumbs/553d2a2a-b924-401e-988d-1db34a907355-thumb.webp b/dist/client/uploads/thumbs/553d2a2a-b924-401e-988d-1db34a907355-thumb.webp
new file mode 100644
index 0000000..7d74bf1
Binary files /dev/null and b/dist/client/uploads/thumbs/553d2a2a-b924-401e-988d-1db34a907355-thumb.webp differ
diff --git a/dist/client/uploads/thumbs/70cc796b-ee3c-49c8-8939-d0fe0dde1554-thumb.webp b/dist/client/uploads/thumbs/70cc796b-ee3c-49c8-8939-d0fe0dde1554-thumb.webp
new file mode 100644
index 0000000..e0fedca
Binary files /dev/null and b/dist/client/uploads/thumbs/70cc796b-ee3c-49c8-8939-d0fe0dde1554-thumb.webp differ
diff --git a/dist/client/uploads/thumbs/7c59fcfa-93cc-4d1d-b2be-d491eb482302-thumb.webp b/dist/client/uploads/thumbs/7c59fcfa-93cc-4d1d-b2be-d491eb482302-thumb.webp
new file mode 100644
index 0000000..eeb0eae
Binary files /dev/null and b/dist/client/uploads/thumbs/7c59fcfa-93cc-4d1d-b2be-d491eb482302-thumb.webp differ
diff --git a/dist/client/uploads/thumbs/9de7af87-a5f2-4299-bc4e-6c3a17ee9f4d-thumb.webp b/dist/client/uploads/thumbs/9de7af87-a5f2-4299-bc4e-6c3a17ee9f4d-thumb.webp
new file mode 100644
index 0000000..5f91c53
Binary files /dev/null and b/dist/client/uploads/thumbs/9de7af87-a5f2-4299-bc4e-6c3a17ee9f4d-thumb.webp differ
diff --git a/dist/client/uploads/thumbs/ad228b50-fe89-40a6-b17d-87440c24d6a5-thumb.webp b/dist/client/uploads/thumbs/ad228b50-fe89-40a6-b17d-87440c24d6a5-thumb.webp
new file mode 100644
index 0000000..cfcfdc2
Binary files /dev/null and b/dist/client/uploads/thumbs/ad228b50-fe89-40a6-b17d-87440c24d6a5-thumb.webp differ
diff --git a/dist/client/uploads/thumbs/af246b9a-cf64-43c2-934d-edb5d8f0b8ee-thumb.webp b/dist/client/uploads/thumbs/af246b9a-cf64-43c2-934d-edb5d8f0b8ee-thumb.webp
new file mode 100644
index 0000000..50b4de2
Binary files /dev/null and b/dist/client/uploads/thumbs/af246b9a-cf64-43c2-934d-edb5d8f0b8ee-thumb.webp differ
diff --git a/dist/client/uploads/thumbs/d0858db4-fcc3-43b4-b120-53cc41a4473c-thumb.webp b/dist/client/uploads/thumbs/d0858db4-fcc3-43b4-b120-53cc41a4473c-thumb.webp
new file mode 100644
index 0000000..9c292e2
Binary files /dev/null and b/dist/client/uploads/thumbs/d0858db4-fcc3-43b4-b120-53cc41a4473c-thumb.webp differ
diff --git a/dist/client/uploads/thumbs/d3a18a75-4a6f-48c6-b3a5-c10f6fe798d7-thumb.webp b/dist/client/uploads/thumbs/d3a18a75-4a6f-48c6-b3a5-c10f6fe798d7-thumb.webp
new file mode 100644
index 0000000..a1f5823
Binary files /dev/null and b/dist/client/uploads/thumbs/d3a18a75-4a6f-48c6-b3a5-c10f6fe798d7-thumb.webp differ
diff --git a/dist/client/uploads/thumbs/eaebf85a-c702-4eb6-ae0d-02a752761ea5-thumb.webp b/dist/client/uploads/thumbs/eaebf85a-c702-4eb6-ae0d-02a752761ea5-thumb.webp
new file mode 100644
index 0000000..2af2f6c
Binary files /dev/null and b/dist/client/uploads/thumbs/eaebf85a-c702-4eb6-ae0d-02a752761ea5-thumb.webp differ
diff --git a/dist/client/uploads/thumbs/eb8a9dae-70da-4887-8a36-75f660ccd42a-thumb.webp b/dist/client/uploads/thumbs/eb8a9dae-70da-4887-8a36-75f660ccd42a-thumb.webp
new file mode 100644
index 0000000..07b6002
Binary files /dev/null and b/dist/client/uploads/thumbs/eb8a9dae-70da-4887-8a36-75f660ccd42a-thumb.webp differ
diff --git a/dist/client/uploads/thumbs/f1b83771-f192-46f1-8629-6982d42c3b48-thumb.webp b/dist/client/uploads/thumbs/f1b83771-f192-46f1-8629-6982d42c3b48-thumb.webp
new file mode 100644
index 0000000..9c292e2
Binary files /dev/null and b/dist/client/uploads/thumbs/f1b83771-f192-46f1-8629-6982d42c3b48-thumb.webp differ
diff --git a/dist/client/uploads/thumbs/fcdcd26a-1029-46ec-adae-8013cbe48022-thumb.webp b/dist/client/uploads/thumbs/fcdcd26a-1029-46ec-adae-8013cbe48022-thumb.webp
new file mode 100644
index 0000000..61d70fd
Binary files /dev/null and b/dist/client/uploads/thumbs/fcdcd26a-1029-46ec-adae-8013cbe48022-thumb.webp differ
diff --git a/dist/server/_@astrojs-ssr-adapter.mjs b/dist/server/_@astrojs-ssr-adapter.mjs
new file mode 100644
index 0000000..dc9881b
--- /dev/null
+++ b/dist/server/_@astrojs-ssr-adapter.mjs
@@ -0,0 +1 @@
+export { c as createExports, a as start } from './chunks/_@astrojs-ssr-adapter_BSED7qqF.mjs';
diff --git a/dist/server/_noop-middleware.mjs b/dist/server/_noop-middleware.mjs
new file mode 100644
index 0000000..84424b0
--- /dev/null
+++ b/dist/server/_noop-middleware.mjs
@@ -0,0 +1,3 @@
+const onRequest = (_, next) => next();
+
+export { onRequest };
diff --git a/dist/server/chunks/BaseLayout_Bc2tHXHa.mjs b/dist/server/chunks/BaseLayout_Bc2tHXHa.mjs
new file mode 100644
index 0000000..bcc12a0
--- /dev/null
+++ b/dist/server/chunks/BaseLayout_Bc2tHXHa.mjs
@@ -0,0 +1,88 @@
+import { c as createComponent, m as maybeRenderHead, a as addAttribute, r as renderTemplate, b as createAstro, e as renderHead, d as renderComponent, f as renderSlot } from './astro/server_DrnzQWw3.mjs';
+import 'kleur/colors';
+import 'clsx';
+import { r as readCms, b as getApprovedPosts, c as getPostBySlug, j as getPrimaryNav, e as getCategoryPath, k as getFooterPageLinks } from './cms_CRTVymfB.mjs';
+/* empty css */
+
+const $$Astro$2 = createAstro();
+const $$Header = createComponent(async ($$result, $$props, $$slots) => {
+ const Astro2 = $$result.createAstro($$Astro$2, $$props, $$slots);
+ Astro2.self = $$Header;
+ const cms = await readCms();
+ const { breakingNews } = cms.settings;
+ const approvedPosts = getApprovedPosts(cms);
+ const breakingPosts = [
+ ...(breakingNews.postSlugs ?? []).map((slug) => getPostBySlug(approvedPosts, slug)).filter(Boolean),
+ ...approvedPosts.filter((post) => post.urgent)
+ ].filter((post, index, list) => list.findIndex((item) => item.slug === post.slug) === index);
+ const primaryNav = getPrimaryNav();
+ const pathname = Astro2.url.pathname;
+ const normalizePath = (value) => decodeURIComponent(value.replace(/\/+$/, "") || "/");
+ const normalizedPathname = normalizePath(pathname);
+ const isActive = (href) => {
+ const target = normalizePath(href);
+ if (target === "/") {
+ return normalizedPathname === "/";
+ }
+ return normalizedPathname === target || normalizedPathname.startsWith(`${target}/`);
+ };
+ const categoryTabs = [
+ { label: "\u0647\u0645\u0647", href: "/posts/" },
+ ...cms.categories.map((category) => ({
+ label: category,
+ href: getCategoryPath(category)
+ }))
+ ];
+ const breakingText = breakingPosts.length > 0 ? breakingPosts.map((post) => post.title).join(" \u2014 ") : breakingNews.items.join(" \u2014 ");
+ const breakingHref = breakingPosts[0] ? `/posts/${breakingPosts[0].slug}/` : breakingNews.href;
+ return renderTemplate`${maybeRenderHead()}
`;
+}, "/Users/drfelfel/ruseemrooz/src/components/Header.astro", void 0);
+
+const $$Astro$1 = createAstro();
+const $$Footer = createComponent(async ($$result, $$props, $$slots) => {
+ const Astro2 = $$result.createAstro($$Astro$1, $$props, $$slots);
+ Astro2.self = $$Footer;
+ const cms = await readCms();
+ const footerPageLinks = getFooterPageLinks();
+ const pathname = Astro2.url.pathname;
+ const normalizePath = (value) => decodeURIComponent(value.replace(/\/+$/, "") || "/");
+ const normalizedPathname = normalizePath(pathname);
+ const isNavActive = (href) => {
+ const target = normalizePath(href);
+ if (target === "/") {
+ return normalizedPathname === "/";
+ }
+ return normalizedPathname === target || normalizedPathname.startsWith(`${target}/`);
+ };
+ const mobileNav = [
+ { icon: "home", label: "\u062E\u0627\u0646\u0647", href: "/" },
+ { icon: "article", label: "\u0627\u062E\u0628\u0627\u0631", href: "/posts/" },
+ { icon: "insights", label: "\u0627\u0642\u062A\u0635\u0627\u062F", href: getCategoryPath("\u0627\u0642\u062A\u0635\u0627\u062F") },
+ { icon: "explore", label: "\u062C\u0647\u0627\u0646", href: getCategoryPath("\u0628\u06CC\u0646\u200C\u0627\u0644\u0645\u0644\u0644") },
+ { icon: "bolt", label: "\u0627\u0646\u0631\u0698\u06CC", href: getCategoryPath("\u0627\u0646\u0631\u0698\u06CC") }
+ ].map((item) => ({ ...item, active: isNavActive(item.href) }));
+ return renderTemplate`${maybeRenderHead()} `;
+}, "/Users/drfelfel/ruseemrooz/src/components/Footer.astro", void 0);
+
+const $$Astro = createAstro();
+const $$BaseLayout = createComponent(($$result, $$props, $$slots) => {
+ const Astro2 = $$result.createAstro($$Astro, $$props, $$slots);
+ Astro2.self = $$BaseLayout;
+ const {
+ title = "\u0631\u0648\u0633 \u0627\u0645\u0631\u0648\u0632",
+ description = "\u062E\u0628\u0631\u06AF\u0632\u0627\u0631\u06CC \u062A\u062E\u0635\u0635\u06CC \u067E\u0648\u0634\u0634 \u0627\u062E\u0628\u0627\u0631 \u0631\u0648\u0633\u06CC\u0647\u060C \u0627\u0648\u0631\u0627\u0633\u06CC\u0627 \u0648 \u0631\u0648\u0627\u0628\u0637 \u0645\u0646\u0637\u0642\u0647\u200C\u0627\u06CC"
+ } = Astro2.props;
+ return renderTemplate` ${title}${renderHead()} ${renderComponent($$result, "Header", $$Header, {})} ${renderSlot($$result, $$slots["default"])} ${renderComponent($$result, "Footer", $$Footer, {})} `;
+}, "/Users/drfelfel/ruseemrooz/src/layouts/BaseLayout.astro", void 0);
+
+export { $$BaseLayout as $ };
diff --git a/dist/server/chunks/Pagination_BeoGb-H9.mjs b/dist/server/chunks/Pagination_BeoGb-H9.mjs
new file mode 100644
index 0000000..c0c9171
--- /dev/null
+++ b/dist/server/chunks/Pagination_BeoGb-H9.mjs
@@ -0,0 +1,19 @@
+import { c as createComponent, d as renderComponent, F as Fragment, r as renderTemplate, b as createAstro, m as maybeRenderHead, a as addAttribute } from './astro/server_DrnzQWw3.mjs';
+import 'kleur/colors';
+
+const $$Astro = createAstro();
+const $$Pagination = createComponent(($$result, $$props, $$slots) => {
+ const Astro2 = $$result.createAstro($$Astro, $$props, $$slots);
+ Astro2.self = $$Pagination;
+ const { currentPage, totalPages, basePath = "/posts/" } = Astro2.props;
+ const pages = Array.from({ length: totalPages }, (_, index) => index + 1);
+ const getPageHref = (page) => page === 1 ? basePath : `${basePath}?page=${page}`;
+ const previousPage = Math.max(1, currentPage - 1);
+ const nextPage = Math.min(totalPages, currentPage + 1);
+ return renderTemplate`${totalPages > 1 && renderTemplate`${renderComponent($$result, "Fragment", Fragment, {}, { "default": ($$result2) => renderTemplate`${maybeRenderHead()}صفحه ${currentPage} از ${totalPages}
` })}`}`;
+}, "/Users/drfelfel/ruseemrooz/src/components/Pagination.astro", void 0);
+
+export { $$Pagination as $ };
diff --git a/dist/server/chunks/PostCard_D8td2Gog.mjs b/dist/server/chunks/PostCard_D8td2Gog.mjs
new file mode 100644
index 0000000..c64f539
--- /dev/null
+++ b/dist/server/chunks/PostCard_D8td2Gog.mjs
@@ -0,0 +1,16 @@
+import { c as createComponent, m as maybeRenderHead, a as addAttribute, r as renderTemplate, b as createAstro } from './astro/server_DrnzQWw3.mjs';
+import 'kleur/colors';
+import 'clsx';
+import { f as formatPostRelativeTime } from './cms_CRTVymfB.mjs';
+
+const $$Astro = createAstro();
+const $$PostCard = createComponent(($$result, $$props, $$slots) => {
+ const Astro2 = $$result.createAstro($$Astro, $$props, $$slots);
+ Astro2.self = $$PostCard;
+ const { post } = Astro2.props;
+ const image = post.thumbnail || post.image;
+ const timeLabel = formatPostRelativeTime(post);
+ return renderTemplate`${maybeRenderHead()} ${image ? renderTemplate`
![]()
` : renderTemplate`
image
`}
${post.category} ${post.title}
${post.excerpt}
${timeLabel} · ${post.readTime} · ${post.category}
bookmark_border share
`;
+}, "/Users/drfelfel/ruseemrooz/src/components/PostCard.astro", void 0);
+
+export { $$PostCard as $ };
diff --git a/dist/server/chunks/_@astrojs-ssr-adapter_BSED7qqF.mjs b/dist/server/chunks/_@astrojs-ssr-adapter_BSED7qqF.mjs
new file mode 100644
index 0000000..ff09bb8
--- /dev/null
+++ b/dist/server/chunks/_@astrojs-ssr-adapter_BSED7qqF.mjs
@@ -0,0 +1,3083 @@
+import { Http2ServerResponse } from 'node:http2';
+import { R as ROUTE_TYPE_HEADER, h as REROUTE_DIRECTIVE_HEADER, i as decryptString, j as createSlotValueFromString, d as renderComponent, r as renderTemplate, D as DEFAULT_404_COMPONENT, k as renderSlotToString, l as renderJSX, n as chunkToString, o as isRenderInstruction, p as originPathnameSymbol, A as ASTRO_VERSION, q as clientLocalsSymbol, t as clientAddressSymbol, u as responseSentSymbol$1, v as renderPage, w as REWRITE_DIRECTIVE_HEADER_KEY, x as REWRITE_DIRECTIVE_HEADER_VALUE, y as renderEndpoint, z as REROUTABLE_STATUS_CODES } from './astro/server_DrnzQWw3.mjs';
+import { appendForwardSlash as appendForwardSlash$1, joinPaths, trimSlashes, removeTrailingForwardSlash, fileExtension, slash, prependForwardSlash as prependForwardSlash$1 } from '@astrojs/internal-helpers/path';
+import { A as AstroError, q as i18nNoLocaleFoundInPath, R as ResponseSentError, s as MiddlewareNoDataOrNextCalled, t as MiddlewareNotAResponse, G as GetStaticPathsRequired, u as InvalidGetStaticPathsReturn, v as InvalidGetStaticPathsEntry, w as GetStaticPathsExpectedParams, x as GetStaticPathsInvalidRouteParam, P as PageNumberParamNotFound, y as NoMatchingStaticPathFound, z as PrerenderDynamicEndpointPathCollide, B as ReservedSlotName, C as RewriteWithBodyUsed, L as LocalsNotAnObject, H as PrerenderClientAddressNotAvailable, J as ClientAddressNotAvailable, S as StaticClientAddressNotAvailable, K as AstroResponseHeadersReassigned } from './astro/assets-service_BZIICsh3.mjs';
+import { serialize, parse } from 'cookie';
+import { bold, red, yellow, dim, blue } from 'kleur/colors';
+import { g as getActionQueryString, d as deserializeActionResult, e as ensure404Route, D as DEFAULT_404_ROUTE, a as default404Instance, N as NOOP_MIDDLEWARE_FN } from './astro-designed-error-pages_DsfhG0WT.mjs';
+import 'es-module-lexer';
+import 'clsx';
+import { AsyncLocalStorage } from 'node:async_hooks';
+import fs from 'node:fs';
+import http from 'node:http';
+import https from 'node:https';
+import enableDestroy from 'server-destroy';
+import os from 'node:os';
+import path from 'node:path';
+import url from 'node:url';
+import send from 'send';
+import buffer from 'node:buffer';
+import crypto from 'node:crypto';
+
+function shouldAppendForwardSlash(trailingSlash, buildFormat) {
+ switch (trailingSlash) {
+ case "always":
+ return true;
+ case "never":
+ return false;
+ case "ignore": {
+ switch (buildFormat) {
+ case "directory":
+ return true;
+ case "preserve":
+ case "file":
+ return false;
+ }
+ }
+ }
+}
+
+function createI18nMiddleware(i18n, base, trailingSlash, format) {
+ if (!i18n) return (_, next) => next();
+ const payload = {
+ ...i18n,
+ trailingSlash,
+ base,
+ format};
+ const _redirectToDefaultLocale = redirectToDefaultLocale(payload);
+ const _noFoundForNonLocaleRoute = notFound(payload);
+ const _requestHasLocale = requestHasLocale(payload.locales);
+ const _redirectToFallback = redirectToFallback(payload);
+ const prefixAlways = (context) => {
+ const url = context.url;
+ if (url.pathname === base + "/" || url.pathname === base) {
+ return _redirectToDefaultLocale(context);
+ } else if (!_requestHasLocale(context)) {
+ return _noFoundForNonLocaleRoute(context);
+ }
+ return void 0;
+ };
+ const prefixOtherLocales = (context, response) => {
+ let pathnameContainsDefaultLocale = false;
+ const url = context.url;
+ for (const segment of url.pathname.split("/")) {
+ if (normalizeTheLocale(segment) === normalizeTheLocale(i18n.defaultLocale)) {
+ pathnameContainsDefaultLocale = true;
+ break;
+ }
+ }
+ if (pathnameContainsDefaultLocale) {
+ const newLocation = url.pathname.replace(`/${i18n.defaultLocale}`, "");
+ response.headers.set("Location", newLocation);
+ return _noFoundForNonLocaleRoute(context);
+ }
+ return void 0;
+ };
+ return async (context, next) => {
+ const response = await next();
+ const type = response.headers.get(ROUTE_TYPE_HEADER);
+ const isReroute = response.headers.get(REROUTE_DIRECTIVE_HEADER);
+ if (isReroute === "no" && typeof i18n.fallback === "undefined") {
+ return response;
+ }
+ if (type !== "page" && type !== "fallback") {
+ return response;
+ }
+ if (requestIs404Or500(context.request, base)) {
+ return response;
+ }
+ const { currentLocale } = context;
+ switch (i18n.strategy) {
+ case "manual": {
+ return response;
+ }
+ case "domains-prefix-other-locales": {
+ if (localeHasntDomain(i18n, currentLocale)) {
+ const result = prefixOtherLocales(context, response);
+ if (result) {
+ return result;
+ }
+ }
+ break;
+ }
+ case "pathname-prefix-other-locales": {
+ const result = prefixOtherLocales(context, response);
+ if (result) {
+ return result;
+ }
+ break;
+ }
+ case "domains-prefix-always-no-redirect": {
+ if (localeHasntDomain(i18n, currentLocale)) {
+ const result = _noFoundForNonLocaleRoute(context, response);
+ if (result) {
+ return result;
+ }
+ }
+ break;
+ }
+ case "pathname-prefix-always-no-redirect": {
+ const result = _noFoundForNonLocaleRoute(context, response);
+ if (result) {
+ return result;
+ }
+ break;
+ }
+ case "pathname-prefix-always": {
+ const result = prefixAlways(context);
+ if (result) {
+ return result;
+ }
+ break;
+ }
+ case "domains-prefix-always": {
+ if (localeHasntDomain(i18n, currentLocale)) {
+ const result = prefixAlways(context);
+ if (result) {
+ return result;
+ }
+ }
+ break;
+ }
+ }
+ return _redirectToFallback(context, response);
+ };
+}
+function localeHasntDomain(i18n, currentLocale) {
+ for (const domainLocale of Object.values(i18n.domainLookupTable)) {
+ if (domainLocale === currentLocale) {
+ return false;
+ }
+ }
+ return true;
+}
+
+function requestHasLocale(locales) {
+ return function(context) {
+ return pathHasLocale(context.url.pathname, locales);
+ };
+}
+function requestIs404Or500(request, base = "") {
+ const url = new URL(request.url);
+ return url.pathname.startsWith(`${base}/404`) || url.pathname.startsWith(`${base}/500`);
+}
+function pathHasLocale(path, locales) {
+ const segments = path.split("/");
+ for (const segment of segments) {
+ for (const locale of locales) {
+ if (typeof locale === "string") {
+ if (normalizeTheLocale(segment) === normalizeTheLocale(locale)) {
+ return true;
+ }
+ } else if (segment === locale.path) {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+function getPathByLocale(locale, locales) {
+ for (const loopLocale of locales) {
+ if (typeof loopLocale === "string") {
+ if (loopLocale === locale) {
+ return loopLocale;
+ }
+ } else {
+ for (const code of loopLocale.codes) {
+ if (code === locale) {
+ return loopLocale.path;
+ }
+ }
+ }
+ }
+ throw new AstroError(i18nNoLocaleFoundInPath);
+}
+function normalizeTheLocale(locale) {
+ return locale.replaceAll("_", "-").toLowerCase();
+}
+function toCodes(locales) {
+ return locales.map((loopLocale) => {
+ if (typeof loopLocale === "string") {
+ return loopLocale;
+ } else {
+ return loopLocale.codes[0];
+ }
+ });
+}
+function redirectToDefaultLocale({
+ trailingSlash,
+ format,
+ base,
+ defaultLocale
+}) {
+ return function(context, statusCode) {
+ if (shouldAppendForwardSlash(trailingSlash, format)) {
+ return context.redirect(`${appendForwardSlash$1(joinPaths(base, defaultLocale))}`, statusCode);
+ } else {
+ return context.redirect(`${joinPaths(base, defaultLocale)}`, statusCode);
+ }
+ };
+}
+function notFound({ base, locales }) {
+ return function(context, response) {
+ if (response?.headers.get(REROUTE_DIRECTIVE_HEADER) === "no") return response;
+ const url = context.url;
+ const isRoot = url.pathname === base + "/" || url.pathname === base;
+ if (!(isRoot || pathHasLocale(url.pathname, locales))) {
+ if (response) {
+ response.headers.set(REROUTE_DIRECTIVE_HEADER, "no");
+ return new Response(response.body, {
+ status: 404,
+ headers: response.headers
+ });
+ } else {
+ return new Response(null, {
+ status: 404,
+ headers: {
+ [REROUTE_DIRECTIVE_HEADER]: "no"
+ }
+ });
+ }
+ }
+ return void 0;
+ };
+}
+function redirectToFallback({
+ fallback,
+ locales,
+ defaultLocale,
+ strategy,
+ base,
+ fallbackType
+}) {
+ return async function(context, response) {
+ if (response.status >= 300 && fallback) {
+ const fallbackKeys = fallback ? Object.keys(fallback) : [];
+ const segments = context.url.pathname.split("/");
+ const urlLocale = segments.find((segment) => {
+ for (const locale of locales) {
+ if (typeof locale === "string") {
+ if (locale === segment) {
+ return true;
+ }
+ } else if (locale.path === segment) {
+ return true;
+ }
+ }
+ return false;
+ });
+ if (urlLocale && fallbackKeys.includes(urlLocale)) {
+ const fallbackLocale = fallback[urlLocale];
+ const pathFallbackLocale = getPathByLocale(fallbackLocale, locales);
+ let newPathname;
+ if (pathFallbackLocale === defaultLocale && strategy === "pathname-prefix-other-locales") {
+ if (context.url.pathname.includes(`${base}`)) {
+ newPathname = context.url.pathname.replace(`/${urlLocale}`, ``);
+ } else {
+ newPathname = context.url.pathname.replace(`/${urlLocale}`, `/`);
+ }
+ } else {
+ newPathname = context.url.pathname.replace(`/${urlLocale}`, `/${pathFallbackLocale}`);
+ }
+ if (fallbackType === "rewrite") {
+ return await context.rewrite(newPathname);
+ } else {
+ return context.redirect(newPathname);
+ }
+ }
+ }
+ return response;
+ };
+}
+
+const DELETED_EXPIRATION = /* @__PURE__ */ new Date(0);
+const DELETED_VALUE = "deleted";
+const responseSentSymbol = Symbol.for("astro.responseSent");
+class AstroCookie {
+ constructor(value) {
+ this.value = value;
+ }
+ json() {
+ if (this.value === void 0) {
+ throw new Error(`Cannot convert undefined to an object.`);
+ }
+ return JSON.parse(this.value);
+ }
+ number() {
+ return Number(this.value);
+ }
+ boolean() {
+ if (this.value === "false") return false;
+ if (this.value === "0") return false;
+ return Boolean(this.value);
+ }
+}
+class AstroCookies {
+ #request;
+ #requestValues;
+ #outgoing;
+ #consumed;
+ constructor(request) {
+ this.#request = request;
+ this.#requestValues = null;
+ this.#outgoing = null;
+ this.#consumed = false;
+ }
+ /**
+ * Astro.cookies.delete(key) is used to delete a cookie. Using this method will result
+ * in a Set-Cookie header added to the response.
+ * @param key The cookie to delete
+ * @param options Options related to this deletion, such as the path of the cookie.
+ */
+ delete(key, options) {
+ const {
+ // @ts-expect-error
+ maxAge: _ignoredMaxAge,
+ // @ts-expect-error
+ expires: _ignoredExpires,
+ ...sanitizedOptions
+ } = options || {};
+ const serializeOptions = {
+ expires: DELETED_EXPIRATION,
+ ...sanitizedOptions
+ };
+ this.#ensureOutgoingMap().set(key, [
+ DELETED_VALUE,
+ serialize(key, DELETED_VALUE, serializeOptions),
+ false
+ ]);
+ }
+ /**
+ * Astro.cookies.get(key) is used to get a cookie value. The cookie value is read from the
+ * request. If you have set a cookie via Astro.cookies.set(key, value), the value will be taken
+ * from that set call, overriding any values already part of the request.
+ * @param key The cookie to get.
+ * @returns An object containing the cookie value as well as convenience methods for converting its value.
+ */
+ get(key, options = void 0) {
+ if (this.#outgoing?.has(key)) {
+ let [serializedValue, , isSetValue] = this.#outgoing.get(key);
+ if (isSetValue) {
+ return new AstroCookie(serializedValue);
+ } else {
+ return void 0;
+ }
+ }
+ const values = this.#ensureParsed(options);
+ if (key in values) {
+ const value = values[key];
+ return new AstroCookie(value);
+ }
+ }
+ /**
+ * Astro.cookies.has(key) returns a boolean indicating whether this cookie is either
+ * part of the initial request or set via Astro.cookies.set(key)
+ * @param key The cookie to check for.
+ * @returns
+ */
+ has(key, options = void 0) {
+ if (this.#outgoing?.has(key)) {
+ let [, , isSetValue] = this.#outgoing.get(key);
+ return isSetValue;
+ }
+ const values = this.#ensureParsed(options);
+ return !!values[key];
+ }
+ /**
+ * Astro.cookies.set(key, value) is used to set a cookie's value. If provided
+ * an object it will be stringified via JSON.stringify(value). Additionally you
+ * can provide options customizing how this cookie will be set, such as setting httpOnly
+ * in order to prevent the cookie from being read in client-side JavaScript.
+ * @param key The name of the cookie to set.
+ * @param value A value, either a string or other primitive or an object.
+ * @param options Options for the cookie, such as the path and security settings.
+ */
+ set(key, value, options) {
+ if (this.#consumed) {
+ const warning = new Error(
+ "Astro.cookies.set() was called after the cookies had already been sent to the browser.\nThis may have happened if this method was called in an imported component.\nPlease make sure that Astro.cookies.set() is only called in the frontmatter of the main page."
+ );
+ warning.name = "Warning";
+ console.warn(warning);
+ }
+ let serializedValue;
+ if (typeof value === "string") {
+ serializedValue = value;
+ } else {
+ let toStringValue = value.toString();
+ if (toStringValue === Object.prototype.toString.call(value)) {
+ serializedValue = JSON.stringify(value);
+ } else {
+ serializedValue = toStringValue;
+ }
+ }
+ const serializeOptions = {};
+ if (options) {
+ Object.assign(serializeOptions, options);
+ }
+ this.#ensureOutgoingMap().set(key, [
+ serializedValue,
+ serialize(key, serializedValue, serializeOptions),
+ true
+ ]);
+ if (this.#request[responseSentSymbol]) {
+ throw new AstroError({
+ ...ResponseSentError
+ });
+ }
+ }
+ /**
+ * Merges a new AstroCookies instance into the current instance. Any new cookies
+ * will be added to the current instance, overwriting any existing cookies with the same name.
+ */
+ merge(cookies) {
+ const outgoing = cookies.#outgoing;
+ if (outgoing) {
+ for (const [key, value] of outgoing) {
+ this.#ensureOutgoingMap().set(key, value);
+ }
+ }
+ }
+ /**
+ * Astro.cookies.header() returns an iterator for the cookies that have previously
+ * been set by either Astro.cookies.set() or Astro.cookies.delete().
+ * This method is primarily used by adapters to set the header on outgoing responses.
+ * @returns
+ */
+ *headers() {
+ if (this.#outgoing == null) return;
+ for (const [, value] of this.#outgoing) {
+ yield value[1];
+ }
+ }
+ /**
+ * Behaves the same as AstroCookies.prototype.headers(),
+ * but allows a warning when cookies are set after the instance is consumed.
+ */
+ static consume(cookies) {
+ cookies.#consumed = true;
+ return cookies.headers();
+ }
+ #ensureParsed(options = void 0) {
+ if (!this.#requestValues) {
+ this.#parse(options);
+ }
+ if (!this.#requestValues) {
+ this.#requestValues = {};
+ }
+ return this.#requestValues;
+ }
+ #ensureOutgoingMap() {
+ if (!this.#outgoing) {
+ this.#outgoing = /* @__PURE__ */ new Map();
+ }
+ return this.#outgoing;
+ }
+ #parse(options = void 0) {
+ const raw = this.#request.headers.get("cookie");
+ if (!raw) {
+ return;
+ }
+ this.#requestValues = parse(raw, options);
+ }
+}
+
+const astroCookiesSymbol = Symbol.for("astro.cookies");
+function attachCookiesToResponse(response, cookies) {
+ Reflect.set(response, astroCookiesSymbol, cookies);
+}
+function getCookiesFromResponse(response) {
+ let cookies = Reflect.get(response, astroCookiesSymbol);
+ if (cookies != null) {
+ return cookies;
+ } else {
+ return void 0;
+ }
+}
+function* getSetCookiesFromResponse(response) {
+ const cookies = getCookiesFromResponse(response);
+ if (!cookies) {
+ return [];
+ }
+ for (const headerValue of AstroCookies.consume(cookies)) {
+ yield headerValue;
+ }
+ return [];
+}
+
+const dateTimeFormat = new Intl.DateTimeFormat([], {
+ hour: "2-digit",
+ minute: "2-digit",
+ second: "2-digit",
+ hour12: false
+});
+const levels = {
+ debug: 20,
+ info: 30,
+ warn: 40,
+ error: 50,
+ silent: 90
+};
+function log(opts, level, label, message, newLine = true) {
+ const logLevel = opts.level;
+ const dest = opts.dest;
+ const event = {
+ label,
+ level,
+ message,
+ newLine
+ };
+ if (!isLogLevelEnabled(logLevel, level)) {
+ return;
+ }
+ dest.write(event);
+}
+function isLogLevelEnabled(configuredLogLevel, level) {
+ return levels[configuredLogLevel] <= levels[level];
+}
+function info(opts, label, message, newLine = true) {
+ return log(opts, "info", label, message, newLine);
+}
+function warn(opts, label, message, newLine = true) {
+ return log(opts, "warn", label, message, newLine);
+}
+function error(opts, label, message, newLine = true) {
+ return log(opts, "error", label, message, newLine);
+}
+function debug(...args) {
+ if ("_astroGlobalDebug" in globalThis) {
+ globalThis._astroGlobalDebug(...args);
+ }
+}
+function getEventPrefix({ level, label }) {
+ const timestamp = `${dateTimeFormat.format(/* @__PURE__ */ new Date())}`;
+ const prefix = [];
+ if (level === "error" || level === "warn") {
+ prefix.push(bold(timestamp));
+ prefix.push(`[${level.toUpperCase()}]`);
+ } else {
+ prefix.push(timestamp);
+ }
+ if (label) {
+ prefix.push(`[${label}]`);
+ }
+ if (level === "error") {
+ return red(prefix.join(" "));
+ }
+ if (level === "warn") {
+ return yellow(prefix.join(" "));
+ }
+ if (prefix.length === 1) {
+ return dim(prefix[0]);
+ }
+ return dim(prefix[0]) + " " + blue(prefix.splice(1).join(" "));
+}
+class Logger {
+ options;
+ constructor(options) {
+ this.options = options;
+ }
+ info(label, message, newLine = true) {
+ info(this.options, label, message, newLine);
+ }
+ warn(label, message, newLine = true) {
+ warn(this.options, label, message, newLine);
+ }
+ error(label, message, newLine = true) {
+ error(this.options, label, message, newLine);
+ }
+ debug(label, ...messages) {
+ debug(label, ...messages);
+ }
+ level() {
+ return this.options.level;
+ }
+ forkIntegrationLogger(label) {
+ return new AstroIntegrationLogger(this.options, label);
+ }
+}
+class AstroIntegrationLogger {
+ options;
+ label;
+ constructor(logging, label) {
+ this.options = logging;
+ this.label = label;
+ }
+ /**
+ * Creates a new logger instance with a new label, but the same log options.
+ */
+ fork(label) {
+ return new AstroIntegrationLogger(this.options, label);
+ }
+ info(message) {
+ info(this.options, this.label, message);
+ }
+ warn(message) {
+ warn(this.options, this.label, message);
+ }
+ error(message) {
+ error(this.options, this.label, message);
+ }
+ debug(message) {
+ debug(this.label, message);
+ }
+}
+
+const consoleLogDestination = {
+ write(event) {
+ let dest = console.error;
+ if (levels[event.level] < levels["error"]) {
+ dest = console.log;
+ }
+ if (event.label === "SKIP_FORMAT") {
+ dest(event.message);
+ } else {
+ dest(getEventPrefix(event) + " " + event.message);
+ }
+ return true;
+ }
+};
+
+const ACTION_API_CONTEXT_SYMBOL = Symbol.for("astro.actionAPIContext");
+
+function hasActionPayload(locals) {
+ return "_actionPayload" in locals;
+}
+function createGetActionResult(locals) {
+ return (actionFn) => {
+ if (!hasActionPayload(locals) || actionFn.toString() !== getActionQueryString(locals._actionPayload.actionName)) {
+ return void 0;
+ }
+ return deserializeActionResult(locals._actionPayload.actionResult);
+ };
+}
+function createCallAction(context) {
+ return (baseAction, input) => {
+ Reflect.set(context, ACTION_API_CONTEXT_SYMBOL, true);
+ const action = baseAction.bind(context);
+ return action(input);
+ };
+}
+
+function parseLocale(header) {
+ if (header === "*") {
+ return [{ locale: header, qualityValue: void 0 }];
+ }
+ const result = [];
+ const localeValues = header.split(",").map((str) => str.trim());
+ for (const localeValue of localeValues) {
+ const split = localeValue.split(";").map((str) => str.trim());
+ const localeName = split[0];
+ const qualityValue = split[1];
+ if (!split) {
+ continue;
+ }
+ if (qualityValue && qualityValue.startsWith("q=")) {
+ const qualityValueAsFloat = Number.parseFloat(qualityValue.slice("q=".length));
+ if (Number.isNaN(qualityValueAsFloat) || qualityValueAsFloat > 1) {
+ result.push({
+ locale: localeName,
+ qualityValue: void 0
+ });
+ } else {
+ result.push({
+ locale: localeName,
+ qualityValue: qualityValueAsFloat
+ });
+ }
+ } else {
+ result.push({
+ locale: localeName,
+ qualityValue: void 0
+ });
+ }
+ }
+ return result;
+}
+function sortAndFilterLocales(browserLocaleList, locales) {
+ const normalizedLocales = toCodes(locales).map(normalizeTheLocale);
+ return browserLocaleList.filter((browserLocale) => {
+ if (browserLocale.locale !== "*") {
+ return normalizedLocales.includes(normalizeTheLocale(browserLocale.locale));
+ }
+ return true;
+ }).sort((a, b) => {
+ if (a.qualityValue && b.qualityValue) {
+ return Math.sign(b.qualityValue - a.qualityValue);
+ }
+ return 0;
+ });
+}
+function computePreferredLocale(request, locales) {
+ const acceptHeader = request.headers.get("Accept-Language");
+ let result = void 0;
+ if (acceptHeader) {
+ const browserLocaleList = sortAndFilterLocales(parseLocale(acceptHeader), locales);
+ const firstResult = browserLocaleList.at(0);
+ if (firstResult && firstResult.locale !== "*") {
+ for (const currentLocale of locales) {
+ if (typeof currentLocale === "string") {
+ if (normalizeTheLocale(currentLocale) === normalizeTheLocale(firstResult.locale)) {
+ result = currentLocale;
+ }
+ } else {
+ for (const currentCode of currentLocale.codes) {
+ if (normalizeTheLocale(currentCode) === normalizeTheLocale(firstResult.locale)) {
+ result = currentLocale.path;
+ }
+ }
+ }
+ }
+ }
+ }
+ return result;
+}
+function computePreferredLocaleList(request, locales) {
+ const acceptHeader = request.headers.get("Accept-Language");
+ let result = [];
+ if (acceptHeader) {
+ const browserLocaleList = sortAndFilterLocales(parseLocale(acceptHeader), locales);
+ if (browserLocaleList.length === 1 && browserLocaleList.at(0).locale === "*") {
+ return locales.map((locale) => {
+ if (typeof locale === "string") {
+ return locale;
+ } else {
+ return locale.codes.at(0);
+ }
+ });
+ } else if (browserLocaleList.length > 0) {
+ for (const browserLocale of browserLocaleList) {
+ for (const loopLocale of locales) {
+ if (typeof loopLocale === "string") {
+ if (normalizeTheLocale(loopLocale) === normalizeTheLocale(browserLocale.locale)) {
+ result.push(loopLocale);
+ }
+ } else {
+ for (const code of loopLocale.codes) {
+ if (code === browserLocale.locale) {
+ result.push(loopLocale.path);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ return result;
+}
+function computeCurrentLocale(pathname, locales, defaultLocale) {
+ for (const segment of pathname.split("/")) {
+ for (const locale of locales) {
+ if (typeof locale === "string") {
+ if (!segment.includes(locale)) continue;
+ if (normalizeTheLocale(locale) === normalizeTheLocale(segment)) {
+ return locale;
+ }
+ } else {
+ if (locale.path === segment) {
+ return locale.codes.at(0);
+ } else {
+ for (const code of locale.codes) {
+ if (normalizeTheLocale(code) === normalizeTheLocale(segment)) {
+ return code;
+ }
+ }
+ }
+ }
+ }
+ }
+ for (const locale of locales) {
+ if (typeof locale === "string") {
+ if (locale === defaultLocale) {
+ return locale;
+ }
+ } else {
+ if (locale.path === defaultLocale) {
+ return locale.codes.at(0);
+ }
+ }
+ }
+}
+
+async function callMiddleware(onRequest, apiContext, responseFunction) {
+ let nextCalled = false;
+ let responseFunctionPromise = void 0;
+ const next = async (payload) => {
+ nextCalled = true;
+ responseFunctionPromise = responseFunction(apiContext, payload);
+ return responseFunctionPromise;
+ };
+ let middlewarePromise = onRequest(apiContext, next);
+ return await Promise.resolve(middlewarePromise).then(async (value) => {
+ if (nextCalled) {
+ if (typeof value !== "undefined") {
+ if (value instanceof Response === false) {
+ throw new AstroError(MiddlewareNotAResponse);
+ }
+ return value;
+ } else {
+ if (responseFunctionPromise) {
+ return responseFunctionPromise;
+ } else {
+ throw new AstroError(MiddlewareNotAResponse);
+ }
+ }
+ } else if (typeof value === "undefined") {
+ throw new AstroError(MiddlewareNoDataOrNextCalled);
+ } else if (value instanceof Response === false) {
+ throw new AstroError(MiddlewareNotAResponse);
+ } else {
+ return value;
+ }
+ });
+}
+
+const FORM_CONTENT_TYPES = [
+ "application/x-www-form-urlencoded",
+ "multipart/form-data",
+ "text/plain"
+];
+function createOriginCheckMiddleware() {
+ return defineMiddleware((context, next) => {
+ const { request, url } = context;
+ if (request.method === "GET") {
+ return next();
+ }
+ const sameOrigin = (request.method === "POST" || request.method === "PUT" || request.method === "PATCH" || request.method === "DELETE") && request.headers.get("origin") === url.origin;
+ const hasContentType = request.headers.has("content-type");
+ if (hasContentType) {
+ const formLikeHeader = hasFormLikeHeader(request.headers.get("content-type"));
+ if (formLikeHeader && !sameOrigin) {
+ return new Response(`Cross-site ${request.method} form submissions are forbidden`, {
+ status: 403
+ });
+ }
+ } else {
+ if (!sameOrigin) {
+ return new Response(`Cross-site ${request.method} form submissions are forbidden`, {
+ status: 403
+ });
+ }
+ }
+ return next();
+ });
+}
+function hasFormLikeHeader(contentType) {
+ if (contentType) {
+ for (const FORM_CONTENT_TYPE of FORM_CONTENT_TYPES) {
+ if (contentType.toLowerCase().includes(FORM_CONTENT_TYPE)) {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+const VALID_PARAM_TYPES = ["string", "number", "undefined"];
+function validateGetStaticPathsParameter([key, value], route) {
+ if (!VALID_PARAM_TYPES.includes(typeof value)) {
+ throw new AstroError({
+ ...GetStaticPathsInvalidRouteParam,
+ message: GetStaticPathsInvalidRouteParam.message(key, value, typeof value),
+ location: {
+ file: route
+ }
+ });
+ }
+}
+function validateDynamicRouteModule(mod, {
+ ssr,
+ route
+}) {
+ if ((!ssr || route.prerender) && !mod.getStaticPaths) {
+ throw new AstroError({
+ ...GetStaticPathsRequired,
+ location: { file: route.component }
+ });
+ }
+}
+function validateGetStaticPathsResult(result, logger, route) {
+ if (!Array.isArray(result)) {
+ throw new AstroError({
+ ...InvalidGetStaticPathsReturn,
+ message: InvalidGetStaticPathsReturn.message(typeof result),
+ location: {
+ file: route.component
+ }
+ });
+ }
+ result.forEach((pathObject) => {
+ if (typeof pathObject === "object" && Array.isArray(pathObject) || pathObject === null) {
+ throw new AstroError({
+ ...InvalidGetStaticPathsEntry,
+ message: InvalidGetStaticPathsEntry.message(
+ Array.isArray(pathObject) ? "array" : typeof pathObject
+ )
+ });
+ }
+ if (pathObject.params === void 0 || pathObject.params === null || pathObject.params && Object.keys(pathObject.params).length === 0) {
+ throw new AstroError({
+ ...GetStaticPathsExpectedParams,
+ location: {
+ file: route.component
+ }
+ });
+ }
+ for (const [key, val] of Object.entries(pathObject.params)) {
+ if (!(typeof val === "undefined" || typeof val === "string" || typeof val === "number")) {
+ logger.warn(
+ "router",
+ `getStaticPaths() returned an invalid path param: "${key}". A string, number or undefined value was expected, but got \`${JSON.stringify(
+ val
+ )}\`.`
+ );
+ }
+ if (typeof val === "string" && val === "") {
+ logger.warn(
+ "router",
+ `getStaticPaths() returned an invalid path param: "${key}". \`undefined\` expected for an optional param, but got empty string.`
+ );
+ }
+ }
+ });
+}
+
+function stringifyParams(params, route) {
+ const validatedParams = Object.entries(params).reduce((acc, next) => {
+ validateGetStaticPathsParameter(next, route.component);
+ const [key, value] = next;
+ if (value !== void 0) {
+ acc[key] = typeof value === "string" ? trimSlashes(value) : value.toString();
+ }
+ return acc;
+ }, {});
+ return route.generate(validatedParams);
+}
+
+function generatePaginateFunction(routeMatch) {
+ return function paginateUtility(data, args = {}) {
+ let { pageSize: _pageSize, params: _params, props: _props } = args;
+ const pageSize = _pageSize || 10;
+ const paramName = "page";
+ const additionalParams = _params || {};
+ const additionalProps = _props || {};
+ let includesFirstPageNumber;
+ if (routeMatch.params.includes(`...${paramName}`)) {
+ includesFirstPageNumber = false;
+ } else if (routeMatch.params.includes(`${paramName}`)) {
+ includesFirstPageNumber = true;
+ } else {
+ throw new AstroError({
+ ...PageNumberParamNotFound,
+ message: PageNumberParamNotFound.message(paramName)
+ });
+ }
+ const lastPage = Math.max(1, Math.ceil(data.length / pageSize));
+ const result = [...Array(lastPage).keys()].map((num) => {
+ const pageNum = num + 1;
+ const start = pageSize === Infinity ? 0 : (pageNum - 1) * pageSize;
+ const end = Math.min(start + pageSize, data.length);
+ const params = {
+ ...additionalParams,
+ [paramName]: includesFirstPageNumber || pageNum > 1 ? String(pageNum) : void 0
+ };
+ const current = correctIndexRoute(routeMatch.generate({ ...params }));
+ const next = pageNum === lastPage ? void 0 : correctIndexRoute(routeMatch.generate({ ...params, page: String(pageNum + 1) }));
+ const prev = pageNum === 1 ? void 0 : correctIndexRoute(
+ routeMatch.generate({
+ ...params,
+ page: !includesFirstPageNumber && pageNum - 1 === 1 ? void 0 : String(pageNum - 1)
+ })
+ );
+ const first = pageNum === 1 ? void 0 : correctIndexRoute(
+ routeMatch.generate({
+ ...params,
+ page: includesFirstPageNumber ? "1" : void 0
+ })
+ );
+ const last = pageNum === lastPage ? void 0 : correctIndexRoute(routeMatch.generate({ ...params, page: String(lastPage) }));
+ return {
+ params,
+ props: {
+ ...additionalProps,
+ page: {
+ data: data.slice(start, end),
+ start,
+ end: end - 1,
+ size: pageSize,
+ total: data.length,
+ currentPage: pageNum,
+ lastPage,
+ url: { current, next, prev, first, last }
+ }
+ }
+ };
+ });
+ return result;
+ };
+}
+function correctIndexRoute(route) {
+ if (route === "") {
+ return "/";
+ }
+ return route;
+}
+
+async function callGetStaticPaths({
+ mod,
+ route,
+ routeCache,
+ logger,
+ ssr
+}) {
+ const cached = routeCache.get(route);
+ if (!mod) {
+ throw new Error("This is an error caused by Astro and not your code. Please file an issue.");
+ }
+ if (cached?.staticPaths) {
+ return cached.staticPaths;
+ }
+ validateDynamicRouteModule(mod, { ssr, route });
+ if (ssr && !route.prerender) {
+ const entry = Object.assign([], { keyed: /* @__PURE__ */ new Map() });
+ routeCache.set(route, { ...cached, staticPaths: entry });
+ return entry;
+ }
+ let staticPaths = [];
+ if (!mod.getStaticPaths) {
+ throw new Error("Unexpected Error.");
+ }
+ staticPaths = await mod.getStaticPaths({
+ // Q: Why the cast?
+ // A: So users downstream can have nicer typings, we have to make some sacrifice in our internal typings, which necessitate a cast here
+ paginate: generatePaginateFunction(route)
+ });
+ validateGetStaticPathsResult(staticPaths, logger, route);
+ const keyedStaticPaths = staticPaths;
+ keyedStaticPaths.keyed = /* @__PURE__ */ new Map();
+ for (const sp of keyedStaticPaths) {
+ const paramsKey = stringifyParams(sp.params, route);
+ keyedStaticPaths.keyed.set(paramsKey, sp);
+ }
+ routeCache.set(route, { ...cached, staticPaths: keyedStaticPaths });
+ return keyedStaticPaths;
+}
+class RouteCache {
+ logger;
+ cache = {};
+ mode;
+ constructor(logger, mode = "production") {
+ this.logger = logger;
+ this.mode = mode;
+ }
+ /** Clear the cache. */
+ clearAll() {
+ this.cache = {};
+ }
+ set(route, entry) {
+ const key = this.key(route);
+ if (this.mode === "production" && this.cache[key]?.staticPaths) {
+ this.logger.warn(null, `Internal Warning: route cache overwritten. (${key})`);
+ }
+ this.cache[key] = entry;
+ }
+ get(route) {
+ return this.cache[this.key(route)];
+ }
+ key(route) {
+ return `${route.route}_${route.component}`;
+ }
+}
+function findPathItemByKey(staticPaths, params, route, logger) {
+ const paramsKey = stringifyParams(params, route);
+ const matchedStaticPath = staticPaths.keyed.get(paramsKey);
+ if (matchedStaticPath) {
+ return matchedStaticPath;
+ }
+ logger.debug("router", `findPathItemByKey() - Unexpected cache miss looking for ${paramsKey}`);
+}
+
+function getPattern(segments, base, addTrailingSlash) {
+ const pathname = segments.map((segment) => {
+ if (segment.length === 1 && segment[0].spread) {
+ return "(?:\\/(.*?))?";
+ } else {
+ return "\\/" + segment.map((part) => {
+ if (part.spread) {
+ return "(.*?)";
+ } else if (part.dynamic) {
+ return "([^/]+?)";
+ } else {
+ return part.content.normalize().replace(/\?/g, "%3F").replace(/#/g, "%23").replace(/%5B/g, "[").replace(/%5D/g, "]").replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
+ }
+ }).join("");
+ }
+ }).join("");
+ const trailing = addTrailingSlash && segments.length ? getTrailingSlashPattern(addTrailingSlash) : "$";
+ let initial = "\\/";
+ if (addTrailingSlash === "never" && base !== "/") {
+ initial = "";
+ }
+ return new RegExp(`^${pathname || initial}${trailing}`);
+}
+function getTrailingSlashPattern(addTrailingSlash) {
+ if (addTrailingSlash === "always") {
+ return "\\/$";
+ }
+ if (addTrailingSlash === "never") {
+ return "$";
+ }
+ return "\\/?$";
+}
+
+const SERVER_ISLAND_ROUTE = "/_server-islands/[name]";
+const SERVER_ISLAND_COMPONENT = "_server-islands.astro";
+function getServerIslandRouteData(config) {
+ const segments = [
+ [{ content: "_server-islands", dynamic: false, spread: false }],
+ [{ content: "name", dynamic: true, spread: false }]
+ ];
+ const route = {
+ type: "page",
+ component: SERVER_ISLAND_COMPONENT,
+ generate: () => "",
+ params: ["name"],
+ segments,
+ pattern: getPattern(segments, config.base, config.trailingSlash),
+ prerender: false,
+ isIndex: false,
+ fallbackRoutes: [],
+ route: SERVER_ISLAND_ROUTE
+ };
+ return route;
+}
+function ensureServerIslandRoute(config, routeManifest) {
+ if (routeManifest.routes.some((route) => route.route === "/_server-islands/[name]")) {
+ return;
+ }
+ routeManifest.routes.unshift(getServerIslandRouteData(config));
+}
+function createEndpoint(manifest) {
+ const page = async (result) => {
+ const params = result.params;
+ const request = result.request;
+ const raw = await request.text();
+ const data = JSON.parse(raw);
+ if (!params.name) {
+ return new Response(null, {
+ status: 400,
+ statusText: "Bad request"
+ });
+ }
+ const componentId = params.name;
+ const imp = manifest.serverIslandMap?.get(componentId);
+ if (!imp) {
+ return new Response(null, {
+ status: 404,
+ statusText: "Not found"
+ });
+ }
+ const key = await manifest.key;
+ const encryptedProps = data.encryptedProps;
+ const propString = await decryptString(key, encryptedProps);
+ const props = JSON.parse(propString);
+ const componentModule = await imp();
+ const Component = componentModule[data.componentExport];
+ const slots = {};
+ for (const prop in data.slots) {
+ slots[prop] = createSlotValueFromString(data.slots[prop]);
+ }
+ return renderTemplate`${renderComponent(result, "Component", Component, props, slots)}`;
+ };
+ page.isAstroComponentFactory = true;
+ const instance = {
+ default: page,
+ partial: true
+ };
+ return instance;
+}
+
+function injectDefaultRoutes(ssrManifest, routeManifest) {
+ ensure404Route(routeManifest);
+ ensureServerIslandRoute(ssrManifest, routeManifest);
+ return routeManifest;
+}
+function createDefaultRoutes(manifest) {
+ const root = new URL(manifest.hrefRoot);
+ return [
+ {
+ instance: default404Instance,
+ matchesComponent: (filePath) => filePath.href === new URL(DEFAULT_404_COMPONENT, root).href,
+ route: DEFAULT_404_ROUTE.route,
+ component: DEFAULT_404_COMPONENT
+ },
+ {
+ instance: createEndpoint(manifest),
+ matchesComponent: (filePath) => filePath.href === new URL(SERVER_ISLAND_COMPONENT, root).href,
+ route: SERVER_ISLAND_ROUTE,
+ component: SERVER_ISLAND_COMPONENT
+ }
+ ];
+}
+
+class Pipeline {
+ constructor(logger, manifest, mode, renderers, resolve, serverLike, streaming, adapterName = manifest.adapterName, clientDirectives = manifest.clientDirectives, inlinedScripts = manifest.inlinedScripts, compressHTML = manifest.compressHTML, i18n = manifest.i18n, middleware = manifest.middleware, routeCache = new RouteCache(logger, mode), site = manifest.site ? new URL(manifest.site) : void 0, defaultRoutes = createDefaultRoutes(manifest)) {
+ this.logger = logger;
+ this.manifest = manifest;
+ this.mode = mode;
+ this.renderers = renderers;
+ this.resolve = resolve;
+ this.serverLike = serverLike;
+ this.streaming = streaming;
+ this.adapterName = adapterName;
+ this.clientDirectives = clientDirectives;
+ this.inlinedScripts = inlinedScripts;
+ this.compressHTML = compressHTML;
+ this.i18n = i18n;
+ this.middleware = middleware;
+ this.routeCache = routeCache;
+ this.site = site;
+ this.defaultRoutes = defaultRoutes;
+ this.internalMiddleware = [];
+ if (i18n?.strategy !== "manual") {
+ this.internalMiddleware.push(
+ createI18nMiddleware(i18n, manifest.base, manifest.trailingSlash, manifest.buildFormat)
+ );
+ }
+ }
+ internalMiddleware;
+ resolvedMiddleware = void 0;
+ /**
+ * Resolves the middleware from the manifest, and returns the `onRequest` function. If `onRequest` isn't there,
+ * it returns a no-op function
+ */
+ async getMiddleware() {
+ if (this.resolvedMiddleware) {
+ return this.resolvedMiddleware;
+ } else if (this.middleware) {
+ const middlewareInstance = await this.middleware();
+ const onRequest = middlewareInstance.onRequest ?? NOOP_MIDDLEWARE_FN;
+ if (this.manifest.checkOrigin) {
+ this.resolvedMiddleware = sequence(createOriginCheckMiddleware(), onRequest);
+ } else {
+ this.resolvedMiddleware = onRequest;
+ }
+ return this.resolvedMiddleware;
+ } else {
+ this.resolvedMiddleware = NOOP_MIDDLEWARE_FN;
+ return this.resolvedMiddleware;
+ }
+ }
+}
+
+function routeIsRedirect(route) {
+ return route?.type === "redirect";
+}
+function routeIsFallback(route) {
+ return route?.type === "fallback";
+}
+
+const RedirectComponentInstance = {
+ default() {
+ return new Response(null, {
+ status: 301
+ });
+ }
+};
+const RedirectSinglePageBuiltModule = {
+ page: () => Promise.resolve(RedirectComponentInstance),
+ onRequest: (_, next) => next(),
+ renderers: []
+};
+
+async function renderRedirect(renderContext) {
+ const {
+ request: { method },
+ routeData
+ } = renderContext;
+ const { redirect, redirectRoute } = routeData;
+ const status = redirectRoute && typeof redirect === "object" ? redirect.status : method === "GET" ? 301 : 308;
+ const headers = { location: encodeURI(redirectRouteGenerate(renderContext)) };
+ return new Response(null, { status, headers });
+}
+function redirectRouteGenerate(renderContext) {
+ const {
+ params,
+ routeData: { redirect, redirectRoute }
+ } = renderContext;
+ if (typeof redirectRoute !== "undefined") {
+ return redirectRoute?.generate(params) || redirectRoute?.pathname || "/";
+ } else if (typeof redirect === "string") {
+ let target = redirect;
+ for (const param of Object.keys(params)) {
+ const paramValue = params[param];
+ target = target.replace(`[${param}]`, paramValue).replace(`[...${param}]`, paramValue);
+ }
+ return target;
+ } else if (typeof redirect === "undefined") {
+ return "/";
+ }
+ return redirect.destination;
+}
+
+async function getProps(opts) {
+ const { logger, mod, routeData: route, routeCache, pathname, serverLike } = opts;
+ if (!route || route.pathname) {
+ return {};
+ }
+ if (routeIsRedirect(route) || routeIsFallback(route) || route.component === DEFAULT_404_COMPONENT) {
+ return {};
+ }
+ const staticPaths = await callGetStaticPaths({
+ mod,
+ route,
+ routeCache,
+ logger,
+ ssr: serverLike
+ });
+ const params = getParams(route, pathname);
+ const matchedStaticPath = findPathItemByKey(staticPaths, params, route, logger);
+ if (!matchedStaticPath && (serverLike ? route.prerender : true)) {
+ throw new AstroError({
+ ...NoMatchingStaticPathFound,
+ message: NoMatchingStaticPathFound.message(pathname),
+ hint: NoMatchingStaticPathFound.hint([route.component])
+ });
+ }
+ if (mod) {
+ validatePrerenderEndpointCollision(route, mod, params);
+ }
+ const props = matchedStaticPath?.props ? { ...matchedStaticPath.props } : {};
+ return props;
+}
+function getParams(route, pathname) {
+ if (!route.params.length) return {};
+ const paramsMatch = route.pattern.exec(decodeURIComponent(pathname));
+ if (!paramsMatch) return {};
+ const params = {};
+ route.params.forEach((key, i) => {
+ if (key.startsWith("...")) {
+ params[key.slice(3)] = paramsMatch[i + 1] ? paramsMatch[i + 1] : void 0;
+ } else {
+ params[key] = paramsMatch[i + 1];
+ }
+ });
+ return params;
+}
+function validatePrerenderEndpointCollision(route, mod, params) {
+ if (route.type === "endpoint" && mod.getStaticPaths) {
+ const lastSegment = route.segments[route.segments.length - 1];
+ const paramValues = Object.values(params);
+ const lastParam = paramValues[paramValues.length - 1];
+ if (lastSegment.length === 1 && lastSegment[0].dynamic && lastParam === void 0) {
+ throw new AstroError({
+ ...PrerenderDynamicEndpointPathCollide,
+ message: PrerenderDynamicEndpointPathCollide.message(route.route),
+ hint: PrerenderDynamicEndpointPathCollide.hint(route.component),
+ location: {
+ file: route.component
+ }
+ });
+ }
+ }
+}
+
+function getFunctionExpression(slot) {
+ if (!slot) return;
+ const expressions = slot?.expressions?.filter((e) => isRenderInstruction(e) === false);
+ if (expressions?.length !== 1) return;
+ return expressions[0];
+}
+class Slots {
+ #result;
+ #slots;
+ #logger;
+ constructor(result, slots, logger) {
+ this.#result = result;
+ this.#slots = slots;
+ this.#logger = logger;
+ if (slots) {
+ for (const key of Object.keys(slots)) {
+ if (this[key] !== void 0) {
+ throw new AstroError({
+ ...ReservedSlotName,
+ message: ReservedSlotName.message(key)
+ });
+ }
+ Object.defineProperty(this, key, {
+ get() {
+ return true;
+ },
+ enumerable: true
+ });
+ }
+ }
+ }
+ has(name) {
+ if (!this.#slots) return false;
+ return Boolean(this.#slots[name]);
+ }
+ async render(name, args = []) {
+ if (!this.#slots || !this.has(name)) return;
+ const result = this.#result;
+ if (!Array.isArray(args)) {
+ this.#logger.warn(
+ null,
+ `Expected second parameter to be an array, received a ${typeof args}. If you're trying to pass an array as a single argument and getting unexpected results, make sure you're passing your array as a item of an array. Ex: Astro.slots.render('default', [["Hello", "World"]])`
+ );
+ } else if (args.length > 0) {
+ const slotValue = this.#slots[name];
+ const component = typeof slotValue === "function" ? await slotValue(result) : await slotValue;
+ const expression = getFunctionExpression(component);
+ if (expression) {
+ const slot = async () => typeof expression === "function" ? expression(...args) : expression;
+ return await renderSlotToString(result, slot).then((res) => {
+ return res;
+ });
+ }
+ if (typeof component === "function") {
+ return await renderJSX(result, component(...args)).then(
+ (res) => res != null ? String(res) : res
+ );
+ }
+ }
+ const content = await renderSlotToString(result, this.#slots[name]);
+ const outHTML = chunkToString(result, content);
+ return outHTML;
+ }
+}
+
+function sequence(...handlers) {
+ const filtered = handlers.filter((h) => !!h);
+ const length = filtered.length;
+ if (!length) {
+ return defineMiddleware((_context, next) => {
+ return next();
+ });
+ }
+ return defineMiddleware((context, next) => {
+ let carriedPayload = void 0;
+ return applyHandle(0, context);
+ function applyHandle(i, handleContext) {
+ const handle = filtered[i];
+ const result = handle(handleContext, async (payload) => {
+ if (i < length - 1) {
+ if (payload) {
+ let newRequest;
+ if (payload instanceof Request) {
+ newRequest = payload;
+ } else if (payload instanceof URL) {
+ newRequest = new Request(payload, handleContext.request);
+ } else {
+ newRequest = new Request(
+ new URL(payload, handleContext.url.origin),
+ handleContext.request
+ );
+ }
+ const pipeline = Reflect.get(handleContext, apiContextRoutesSymbol);
+ const { routeData, pathname } = await pipeline.tryRewrite(
+ payload,
+ handleContext.request
+ );
+ carriedPayload = payload;
+ handleContext.request = newRequest;
+ handleContext.url = new URL(newRequest.url);
+ handleContext.cookies = new AstroCookies(newRequest);
+ handleContext.params = getParams(routeData, pathname);
+ }
+ return applyHandle(i + 1, handleContext);
+ } else {
+ return next(payload ?? carriedPayload);
+ }
+ });
+ return result;
+ }
+ });
+}
+
+function defineMiddleware(fn) {
+ return fn;
+}
+
+function matchRoute(pathname, manifest) {
+ const decodedPathname = decodeURI(pathname);
+ return manifest.routes.find((route) => {
+ return route.pattern.test(decodedPathname) || route.fallbackRoutes.some((fallbackRoute) => fallbackRoute.pattern.test(decodedPathname));
+ });
+}
+function isRoute404or500(route) {
+ return route.pattern.test("/404") || route.pattern.test("/500");
+}
+
+function findRouteToRewrite({
+ payload,
+ routes,
+ request,
+ trailingSlash,
+ buildFormat,
+ base
+}) {
+ let newUrl = void 0;
+ if (payload instanceof URL) {
+ newUrl = payload;
+ } else if (payload instanceof Request) {
+ newUrl = new URL(payload.url);
+ } else {
+ newUrl = new URL(payload, new URL(request.url).origin);
+ }
+ let pathname = newUrl.pathname;
+ if (base !== "/" && newUrl.pathname.startsWith(base)) {
+ pathname = shouldAppendForwardSlash(trailingSlash, buildFormat) ? appendForwardSlash$1(newUrl.pathname) : removeTrailingForwardSlash(newUrl.pathname);
+ pathname = pathname.slice(base.length);
+ }
+ let foundRoute;
+ for (const route of routes) {
+ if (route.pattern.test(decodeURI(pathname))) {
+ foundRoute = route;
+ break;
+ }
+ }
+ if (foundRoute) {
+ return {
+ routeData: foundRoute,
+ newUrl,
+ pathname
+ };
+ } else {
+ const custom404 = routes.find((route) => route.route === "/404");
+ if (custom404) {
+ return { routeData: custom404, newUrl, pathname };
+ } else {
+ return { routeData: DEFAULT_404_ROUTE, newUrl, pathname };
+ }
+ }
+}
+function copyRequest(newUrl, oldRequest) {
+ if (oldRequest.bodyUsed) {
+ throw new AstroError(RewriteWithBodyUsed);
+ }
+ return new Request(newUrl, {
+ method: oldRequest.method,
+ headers: oldRequest.headers,
+ body: oldRequest.body,
+ referrer: oldRequest.referrer,
+ referrerPolicy: oldRequest.referrerPolicy,
+ mode: oldRequest.mode,
+ credentials: oldRequest.credentials,
+ cache: oldRequest.cache,
+ redirect: oldRequest.redirect,
+ integrity: oldRequest.integrity,
+ signal: oldRequest.signal,
+ keepalive: oldRequest.keepalive,
+ // https://fetch.spec.whatwg.org/#dom-request-duplex
+ // @ts-expect-error It isn't part of the types, but undici accepts it and it allows to carry over the body to a new request
+ duplex: "half"
+ });
+}
+function setOriginPathname(request, pathname) {
+ Reflect.set(request, originPathnameSymbol, encodeURIComponent(pathname));
+}
+
+const apiContextRoutesSymbol = Symbol.for("context.routes");
+class RenderContext {
+ constructor(pipeline, locals, middleware, pathname, request, routeData, status, cookies = new AstroCookies(request), params = getParams(routeData, pathname), url = new URL(request.url), props = {}, partial = void 0) {
+ this.pipeline = pipeline;
+ this.locals = locals;
+ this.middleware = middleware;
+ this.pathname = pathname;
+ this.request = request;
+ this.routeData = routeData;
+ this.status = status;
+ this.cookies = cookies;
+ this.params = params;
+ this.url = url;
+ this.props = props;
+ this.partial = partial;
+ }
+ /**
+ * A flag that tells the render content if the rewriting was triggered
+ */
+ isRewriting = false;
+ /**
+ * A safety net in case of loops
+ */
+ counter = 0;
+ static async create({
+ locals = {},
+ middleware,
+ pathname,
+ pipeline,
+ request,
+ routeData,
+ status = 200,
+ props,
+ partial = void 0
+ }) {
+ const pipelineMiddleware = await pipeline.getMiddleware();
+ setOriginPathname(request, pathname);
+ return new RenderContext(
+ pipeline,
+ locals,
+ sequence(...pipeline.internalMiddleware, middleware ?? pipelineMiddleware),
+ pathname,
+ request,
+ routeData,
+ status,
+ void 0,
+ void 0,
+ void 0,
+ props,
+ partial
+ );
+ }
+ /**
+ * The main function of the RenderContext.
+ *
+ * Use this function to render any route known to Astro.
+ * It attempts to render a route. A route can be a:
+ *
+ * - page
+ * - redirect
+ * - endpoint
+ * - fallback
+ */
+ async render(componentInstance, slots = {}) {
+ const { cookies, middleware, pipeline } = this;
+ const { logger, serverLike, streaming } = pipeline;
+ const isPrerendered = !serverLike || this.routeData.prerender;
+ const props = Object.keys(this.props).length > 0 ? this.props : await getProps({
+ mod: componentInstance,
+ routeData: this.routeData,
+ routeCache: this.pipeline.routeCache,
+ pathname: this.pathname,
+ logger,
+ serverLike
+ });
+ const apiContext = this.createAPIContext(props, isPrerendered);
+ this.counter++;
+ if (this.counter === 4) {
+ return new Response("Loop Detected", {
+ // https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/508
+ status: 508,
+ statusText: "Astro detected a loop where you tried to call the rewriting logic more than four times."
+ });
+ }
+ const lastNext = async (ctx, payload) => {
+ if (payload) {
+ pipeline.logger.debug("router", "Called rewriting to:", payload);
+ const {
+ routeData,
+ componentInstance: newComponent,
+ pathname,
+ newUrl
+ } = await pipeline.tryRewrite(payload, this.request);
+ this.routeData = routeData;
+ componentInstance = newComponent;
+ if (payload instanceof Request) {
+ this.request = payload;
+ } else {
+ this.request = copyRequest(newUrl, this.request);
+ }
+ this.isRewriting = true;
+ this.url = new URL(this.request.url);
+ this.cookies = new AstroCookies(this.request);
+ this.params = getParams(routeData, pathname);
+ this.pathname = pathname;
+ this.status = 200;
+ }
+ let response2;
+ switch (this.routeData.type) {
+ case "endpoint": {
+ response2 = await renderEndpoint(componentInstance, ctx, serverLike, logger);
+ break;
+ }
+ case "redirect":
+ return renderRedirect(this);
+ case "page": {
+ const result = await this.createResult(componentInstance);
+ try {
+ response2 = await renderPage(
+ result,
+ componentInstance?.default,
+ props,
+ slots,
+ streaming,
+ this.routeData
+ );
+ } catch (e) {
+ result.cancelled = true;
+ throw e;
+ }
+ response2.headers.set(ROUTE_TYPE_HEADER, "page");
+ if (this.routeData.route === "/404" || this.routeData.route === "/500") {
+ response2.headers.set(REROUTE_DIRECTIVE_HEADER, "no");
+ }
+ if (this.isRewriting) {
+ response2.headers.set(REWRITE_DIRECTIVE_HEADER_KEY, REWRITE_DIRECTIVE_HEADER_VALUE);
+ }
+ break;
+ }
+ case "fallback": {
+ return new Response(null, { status: 500, headers: { [ROUTE_TYPE_HEADER]: "fallback" } });
+ }
+ }
+ const responseCookies = getCookiesFromResponse(response2);
+ if (responseCookies) {
+ cookies.merge(responseCookies);
+ }
+ return response2;
+ };
+ const response = await callMiddleware(middleware, apiContext, lastNext);
+ if (response.headers.get(ROUTE_TYPE_HEADER)) {
+ response.headers.delete(ROUTE_TYPE_HEADER);
+ }
+ attachCookiesToResponse(response, cookies);
+ return response;
+ }
+ createAPIContext(props, isPrerendered) {
+ const context = this.createActionAPIContext();
+ const redirect = (path, status = 302) => new Response(null, { status, headers: { Location: path } });
+ Reflect.set(context, apiContextRoutesSymbol, this.pipeline);
+ return Object.assign(context, {
+ props,
+ redirect,
+ getActionResult: createGetActionResult(context.locals),
+ callAction: createCallAction(context),
+ // Used internally by Actions middleware.
+ // TODO: discuss exposing this information from APIContext.
+ // middleware runs on prerendered routes in the dev server,
+ // so this is useful information to have.
+ _isPrerendered: isPrerendered
+ });
+ }
+ async #executeRewrite(reroutePayload) {
+ this.pipeline.logger.debug("router", "Calling rewrite: ", reroutePayload);
+ const { routeData, componentInstance, newUrl, pathname } = await this.pipeline.tryRewrite(
+ reroutePayload,
+ this.request
+ );
+ this.routeData = routeData;
+ if (reroutePayload instanceof Request) {
+ this.request = reroutePayload;
+ } else {
+ this.request = copyRequest(newUrl, this.request);
+ }
+ this.url = new URL(this.request.url);
+ this.cookies = new AstroCookies(this.request);
+ this.params = getParams(routeData, pathname);
+ this.pathname = pathname;
+ this.isRewriting = true;
+ this.status = 200;
+ return await this.render(componentInstance);
+ }
+ createActionAPIContext() {
+ const renderContext = this;
+ const { cookies, params, pipeline, url } = this;
+ const generator = `Astro v${ASTRO_VERSION}`;
+ const rewrite = async (reroutePayload) => {
+ return await this.#executeRewrite(reroutePayload);
+ };
+ return {
+ cookies,
+ get clientAddress() {
+ return renderContext.clientAddress();
+ },
+ get currentLocale() {
+ return renderContext.computeCurrentLocale();
+ },
+ generator,
+ get locals() {
+ return renderContext.locals;
+ },
+ // TODO(breaking): disallow replacing the locals object
+ set locals(val) {
+ if (typeof val !== "object") {
+ throw new AstroError(LocalsNotAnObject);
+ } else {
+ renderContext.locals = val;
+ Reflect.set(this.request, clientLocalsSymbol, val);
+ }
+ },
+ params,
+ get preferredLocale() {
+ return renderContext.computePreferredLocale();
+ },
+ get preferredLocaleList() {
+ return renderContext.computePreferredLocaleList();
+ },
+ rewrite,
+ request: this.request,
+ site: pipeline.site,
+ url
+ };
+ }
+ async createResult(mod) {
+ const { cookies, pathname, pipeline, routeData, status } = this;
+ const { clientDirectives, inlinedScripts, compressHTML, manifest, renderers, resolve } = pipeline;
+ const { links, scripts, styles } = await pipeline.headElements(routeData);
+ const componentMetadata = await pipeline.componentMetadata(routeData) ?? manifest.componentMetadata;
+ const headers = new Headers({ "Content-Type": "text/html" });
+ const partial = typeof this.partial === "boolean" ? this.partial : Boolean(mod.partial);
+ const response = {
+ status,
+ statusText: "OK",
+ get headers() {
+ return headers;
+ },
+ // Disallow `Astro.response.headers = new Headers`
+ set headers(_) {
+ throw new AstroError(AstroResponseHeadersReassigned);
+ }
+ };
+ const actionResult = hasActionPayload(this.locals) ? deserializeActionResult(this.locals._actionPayload.actionResult) : void 0;
+ const result = {
+ base: manifest.base,
+ cancelled: false,
+ clientDirectives,
+ inlinedScripts,
+ componentMetadata,
+ compressHTML,
+ cookies,
+ /** This function returns the `Astro` faux-global */
+ createAstro: (astroGlobal, props, slots) => this.createAstro(result, astroGlobal, props, slots),
+ links,
+ params: this.params,
+ partial,
+ pathname,
+ renderers,
+ resolve,
+ response,
+ request: this.request,
+ scripts,
+ styles,
+ actionResult,
+ serverIslandNameMap: manifest.serverIslandNameMap ?? /* @__PURE__ */ new Map(),
+ key: manifest.key,
+ trailingSlash: manifest.trailingSlash,
+ _metadata: {
+ hasHydrationScript: false,
+ rendererSpecificHydrationScripts: /* @__PURE__ */ new Set(),
+ hasRenderedHead: false,
+ renderedScripts: /* @__PURE__ */ new Set(),
+ hasDirectives: /* @__PURE__ */ new Set(),
+ headInTree: false,
+ extraHead: [],
+ propagators: /* @__PURE__ */ new Set()
+ }
+ };
+ return result;
+ }
+ #astroPagePartial;
+ /**
+ * The Astro global is sourced in 3 different phases:
+ * - **Static**: `.generator` and `.glob` is printed by the compiler, instantiated once per process per astro file
+ * - **Page-level**: `.request`, `.cookies`, `.locals` etc. These remain the same for the duration of the request.
+ * - **Component-level**: `.props`, `.slots`, and `.self` are unique to each _use_ of each component.
+ *
+ * The page level partial is used as the prototype of the user-visible `Astro` global object, which is instantiated once per use of a component.
+ */
+ createAstro(result, astroStaticPartial, props, slotValues) {
+ let astroPagePartial;
+ if (this.isRewriting) {
+ astroPagePartial = this.#astroPagePartial = this.createAstroPagePartial(
+ result,
+ astroStaticPartial
+ );
+ } else {
+ astroPagePartial = this.#astroPagePartial ??= this.createAstroPagePartial(
+ result,
+ astroStaticPartial
+ );
+ }
+ const astroComponentPartial = { props, self: null };
+ const Astro = Object.assign(
+ Object.create(astroPagePartial),
+ astroComponentPartial
+ );
+ let _slots;
+ Object.defineProperty(Astro, "slots", {
+ get: () => {
+ if (!_slots) {
+ _slots = new Slots(
+ result,
+ slotValues,
+ this.pipeline.logger
+ );
+ }
+ return _slots;
+ }
+ });
+ return Astro;
+ }
+ createAstroPagePartial(result, astroStaticPartial) {
+ const renderContext = this;
+ const { cookies, locals, params, pipeline, url } = this;
+ const { response } = result;
+ const redirect = (path, status = 302) => {
+ if (this.request[responseSentSymbol$1]) {
+ throw new AstroError({
+ ...ResponseSentError
+ });
+ }
+ return new Response(null, { status, headers: { Location: path } });
+ };
+ const rewrite = async (reroutePayload) => {
+ return await this.#executeRewrite(reroutePayload);
+ };
+ return {
+ generator: astroStaticPartial.generator,
+ glob: astroStaticPartial.glob,
+ cookies,
+ get clientAddress() {
+ return renderContext.clientAddress();
+ },
+ get currentLocale() {
+ return renderContext.computeCurrentLocale();
+ },
+ params,
+ get preferredLocale() {
+ return renderContext.computePreferredLocale();
+ },
+ get preferredLocaleList() {
+ return renderContext.computePreferredLocaleList();
+ },
+ locals,
+ redirect,
+ rewrite,
+ request: this.request,
+ response,
+ site: pipeline.site,
+ getActionResult: createGetActionResult(locals),
+ get callAction() {
+ return createCallAction(this);
+ },
+ url
+ };
+ }
+ clientAddress() {
+ const { pipeline, request } = this;
+ if (clientAddressSymbol in request) {
+ return Reflect.get(request, clientAddressSymbol);
+ }
+ if (pipeline.serverLike) {
+ if (request.body === null) {
+ throw new AstroError(PrerenderClientAddressNotAvailable);
+ }
+ if (pipeline.adapterName) {
+ throw new AstroError({
+ ...ClientAddressNotAvailable,
+ message: ClientAddressNotAvailable.message(pipeline.adapterName)
+ });
+ }
+ }
+ throw new AstroError(StaticClientAddressNotAvailable);
+ }
+ /**
+ * API Context may be created multiple times per request, i18n data needs to be computed only once.
+ * So, it is computed and saved here on creation of the first APIContext and reused for later ones.
+ */
+ #currentLocale;
+ computeCurrentLocale() {
+ const {
+ url,
+ pipeline: { i18n },
+ routeData
+ } = this;
+ if (!i18n) return;
+ const { defaultLocale, locales, strategy } = i18n;
+ const fallbackTo = strategy === "pathname-prefix-other-locales" || strategy === "domains-prefix-other-locales" ? defaultLocale : void 0;
+ if (this.#currentLocale) {
+ return this.#currentLocale;
+ }
+ let computedLocale;
+ const pathname = routeData.pathname && !isRoute404or500(routeData) ? routeData.pathname : url.pathname;
+ computedLocale = computeCurrentLocale(pathname, locales, defaultLocale);
+ this.#currentLocale = computedLocale ?? fallbackTo;
+ return this.#currentLocale;
+ }
+ #preferredLocale;
+ computePreferredLocale() {
+ const {
+ pipeline: { i18n },
+ request
+ } = this;
+ if (!i18n) return;
+ return this.#preferredLocale ??= computePreferredLocale(request, i18n.locales);
+ }
+ #preferredLocaleList;
+ computePreferredLocaleList() {
+ const {
+ pipeline: { i18n },
+ request
+ } = this;
+ if (!i18n) return;
+ return this.#preferredLocaleList ??= computePreferredLocaleList(request, i18n.locales);
+ }
+}
+
+function getAssetsPrefix(fileExtension, assetsPrefix) {
+ if (!assetsPrefix) return "";
+ if (typeof assetsPrefix === "string") return assetsPrefix;
+ const dotLessFileExtension = fileExtension.slice(1);
+ if (assetsPrefix[dotLessFileExtension]) {
+ return assetsPrefix[dotLessFileExtension];
+ }
+ return assetsPrefix.fallback;
+}
+
+function createAssetLink(href, base, assetsPrefix) {
+ if (assetsPrefix) {
+ const pf = getAssetsPrefix(fileExtension(href), assetsPrefix);
+ return joinPaths(pf, slash(href));
+ } else if (base) {
+ return prependForwardSlash$1(joinPaths(base, slash(href)));
+ } else {
+ return href;
+ }
+}
+function createStylesheetElement(stylesheet, base, assetsPrefix) {
+ if (stylesheet.type === "inline") {
+ return {
+ props: {},
+ children: stylesheet.content
+ };
+ } else {
+ return {
+ props: {
+ rel: "stylesheet",
+ href: createAssetLink(stylesheet.src, base, assetsPrefix)
+ },
+ children: ""
+ };
+ }
+}
+function createStylesheetElementSet(stylesheets, base, assetsPrefix) {
+ return new Set(stylesheets.map((s) => createStylesheetElement(s, base, assetsPrefix)));
+}
+function createModuleScriptElement(script, base, assetsPrefix) {
+ if (script.type === "external") {
+ return createModuleScriptElementWithSrc(script.value, base, assetsPrefix);
+ } else {
+ return {
+ props: {
+ type: "module"
+ },
+ children: script.value
+ };
+ }
+}
+function createModuleScriptElementWithSrc(src, base, assetsPrefix) {
+ return {
+ props: {
+ type: "module",
+ src: createAssetLink(src, base, assetsPrefix)
+ },
+ children: ""
+ };
+}
+
+class AppPipeline extends Pipeline {
+ #manifestData;
+ static create(manifestData, {
+ logger,
+ manifest,
+ mode,
+ renderers,
+ resolve,
+ serverLike,
+ streaming,
+ defaultRoutes
+ }) {
+ const pipeline = new AppPipeline(
+ logger,
+ manifest,
+ mode,
+ renderers,
+ resolve,
+ serverLike,
+ streaming,
+ void 0,
+ void 0,
+ void 0,
+ void 0,
+ void 0,
+ void 0,
+ void 0,
+ void 0,
+ defaultRoutes
+ );
+ pipeline.#manifestData = manifestData;
+ return pipeline;
+ }
+ headElements(routeData) {
+ const routeInfo = this.manifest.routes.find((route) => route.routeData === routeData);
+ const links = /* @__PURE__ */ new Set();
+ const scripts = /* @__PURE__ */ new Set();
+ const styles = createStylesheetElementSet(routeInfo?.styles ?? []);
+ for (const script of routeInfo?.scripts ?? []) {
+ if ("stage" in script) {
+ if (script.stage === "head-inline") {
+ scripts.add({
+ props: {},
+ children: script.children
+ });
+ }
+ } else {
+ scripts.add(createModuleScriptElement(script));
+ }
+ }
+ return { links, styles, scripts };
+ }
+ componentMetadata() {
+ }
+ async getComponentByRoute(routeData) {
+ const module = await this.getModuleForRoute(routeData);
+ return module.page();
+ }
+ async tryRewrite(payload, request) {
+ const { newUrl, pathname, routeData } = findRouteToRewrite({
+ payload,
+ request,
+ routes: this.manifest?.routes.map((r) => r.routeData),
+ trailingSlash: this.manifest.trailingSlash,
+ buildFormat: this.manifest.buildFormat,
+ base: this.manifest.base
+ });
+ const componentInstance = await this.getComponentByRoute(routeData);
+ return { newUrl, pathname, componentInstance, routeData };
+ }
+ async getModuleForRoute(route) {
+ for (const defaultRoute of this.defaultRoutes) {
+ if (route.component === defaultRoute.component) {
+ return {
+ page: () => Promise.resolve(defaultRoute.instance),
+ renderers: []
+ };
+ }
+ }
+ if (route.type === "redirect") {
+ return RedirectSinglePageBuiltModule;
+ } else {
+ if (this.manifest.pageMap) {
+ const importComponentInstance = this.manifest.pageMap.get(route.component);
+ if (!importComponentInstance) {
+ throw new Error(
+ `Unexpectedly unable to find a component instance for route ${route.route}`
+ );
+ }
+ return await importComponentInstance();
+ } else if (this.manifest.pageModule) {
+ return this.manifest.pageModule;
+ }
+ throw new Error(
+ "Astro couldn't find the correct page to render, probably because it wasn't correctly mapped for SSR usage. This is an internal error, please file an issue."
+ );
+ }
+ }
+}
+
+class App {
+ #manifest;
+ #manifestData;
+ #logger = new Logger({
+ dest: consoleLogDestination,
+ level: "info"
+ });
+ #baseWithoutTrailingSlash;
+ #pipeline;
+ #adapterLogger;
+ #renderOptionsDeprecationWarningShown = false;
+ constructor(manifest, streaming = true) {
+ this.#manifest = manifest;
+ this.#manifestData = injectDefaultRoutes(manifest, {
+ routes: manifest.routes.map((route) => route.routeData)
+ });
+ this.#baseWithoutTrailingSlash = removeTrailingForwardSlash(this.#manifest.base);
+ this.#pipeline = this.#createPipeline(this.#manifestData, streaming);
+ this.#adapterLogger = new AstroIntegrationLogger(
+ this.#logger.options,
+ this.#manifest.adapterName
+ );
+ }
+ getAdapterLogger() {
+ return this.#adapterLogger;
+ }
+ /**
+ * Creates a pipeline by reading the stored manifest
+ *
+ * @param manifestData
+ * @param streaming
+ * @private
+ */
+ #createPipeline(manifestData, streaming = false) {
+ return AppPipeline.create(manifestData, {
+ logger: this.#logger,
+ manifest: this.#manifest,
+ mode: "production",
+ renderers: this.#manifest.renderers,
+ defaultRoutes: createDefaultRoutes(this.#manifest),
+ resolve: async (specifier) => {
+ if (!(specifier in this.#manifest.entryModules)) {
+ throw new Error(`Unable to resolve [${specifier}]`);
+ }
+ const bundlePath = this.#manifest.entryModules[specifier];
+ if (bundlePath.startsWith("data:") || bundlePath.length === 0) {
+ return bundlePath;
+ } else {
+ return createAssetLink(bundlePath, this.#manifest.base, this.#manifest.assetsPrefix);
+ }
+ },
+ serverLike: true,
+ streaming
+ });
+ }
+ set setManifestData(newManifestData) {
+ this.#manifestData = newManifestData;
+ }
+ removeBase(pathname) {
+ if (pathname.startsWith(this.#manifest.base)) {
+ return pathname.slice(this.#baseWithoutTrailingSlash.length + 1);
+ }
+ return pathname;
+ }
+ #getPathnameFromRequest(request) {
+ const url = new URL(request.url);
+ const pathname = prependForwardSlash$1(this.removeBase(url.pathname));
+ return pathname;
+ }
+ match(request) {
+ const url = new URL(request.url);
+ if (this.#manifest.assets.has(url.pathname)) return void 0;
+ let pathname = this.#computePathnameFromDomain(request);
+ if (!pathname) {
+ pathname = prependForwardSlash$1(this.removeBase(url.pathname));
+ }
+ let routeData = matchRoute(pathname, this.#manifestData);
+ if (!routeData || routeData.prerender) return void 0;
+ return routeData;
+ }
+ #computePathnameFromDomain(request) {
+ let pathname = void 0;
+ const url = new URL(request.url);
+ if (this.#manifest.i18n && (this.#manifest.i18n.strategy === "domains-prefix-always" || this.#manifest.i18n.strategy === "domains-prefix-other-locales" || this.#manifest.i18n.strategy === "domains-prefix-always-no-redirect")) {
+ let host = request.headers.get("X-Forwarded-Host");
+ let protocol = request.headers.get("X-Forwarded-Proto");
+ if (protocol) {
+ protocol = protocol + ":";
+ } else {
+ protocol = url.protocol;
+ }
+ if (!host) {
+ host = request.headers.get("Host");
+ }
+ if (host && protocol) {
+ host = host.split(":")[0];
+ try {
+ let locale;
+ const hostAsUrl = new URL(`${protocol}//${host}`);
+ for (const [domainKey, localeValue] of Object.entries(
+ this.#manifest.i18n.domainLookupTable
+ )) {
+ const domainKeyAsUrl = new URL(domainKey);
+ if (hostAsUrl.host === domainKeyAsUrl.host && hostAsUrl.protocol === domainKeyAsUrl.protocol) {
+ locale = localeValue;
+ break;
+ }
+ }
+ if (locale) {
+ pathname = prependForwardSlash$1(
+ joinPaths(normalizeTheLocale(locale), this.removeBase(url.pathname))
+ );
+ if (url.pathname.endsWith("/")) {
+ pathname = appendForwardSlash$1(pathname);
+ }
+ }
+ } catch (e) {
+ this.#logger.error(
+ "router",
+ `Astro tried to parse ${protocol}//${host} as an URL, but it threw a parsing error. Check the X-Forwarded-Host and X-Forwarded-Proto headers.`
+ );
+ this.#logger.error("router", `Error: ${e}`);
+ }
+ }
+ }
+ return pathname;
+ }
+ async render(request, routeDataOrOptions, maybeLocals) {
+ let routeData;
+ let locals;
+ let clientAddress;
+ let addCookieHeader;
+ if (routeDataOrOptions && ("addCookieHeader" in routeDataOrOptions || "clientAddress" in routeDataOrOptions || "locals" in routeDataOrOptions || "routeData" in routeDataOrOptions)) {
+ if ("addCookieHeader" in routeDataOrOptions) {
+ addCookieHeader = routeDataOrOptions.addCookieHeader;
+ }
+ if ("clientAddress" in routeDataOrOptions) {
+ clientAddress = routeDataOrOptions.clientAddress;
+ }
+ if ("routeData" in routeDataOrOptions) {
+ routeData = routeDataOrOptions.routeData;
+ }
+ if ("locals" in routeDataOrOptions) {
+ locals = routeDataOrOptions.locals;
+ }
+ } else {
+ routeData = routeDataOrOptions;
+ locals = maybeLocals;
+ if (routeDataOrOptions || locals) {
+ this.#logRenderOptionsDeprecationWarning();
+ }
+ }
+ if (routeData) {
+ this.#logger.debug(
+ "router",
+ "The adapter " + this.#manifest.adapterName + " provided a custom RouteData for ",
+ request.url
+ );
+ this.#logger.debug("router", "RouteData:\n" + routeData);
+ }
+ if (locals) {
+ if (typeof locals !== "object") {
+ const error = new AstroError(LocalsNotAnObject);
+ this.#logger.error(null, error.stack);
+ return this.#renderError(request, { status: 500, error });
+ }
+ Reflect.set(request, clientLocalsSymbol, locals);
+ }
+ if (clientAddress) {
+ Reflect.set(request, clientAddressSymbol, clientAddress);
+ }
+ if (!routeData) {
+ routeData = this.match(request);
+ this.#logger.debug("router", "Astro matched the following route for " + request.url);
+ this.#logger.debug("router", "RouteData:\n" + routeData);
+ }
+ if (!routeData) {
+ this.#logger.debug("router", "Astro hasn't found routes that match " + request.url);
+ this.#logger.debug("router", "Here's the available routes:\n", this.#manifestData);
+ return this.#renderError(request, { locals, status: 404 });
+ }
+ const pathname = this.#getPathnameFromRequest(request);
+ const defaultStatus = this.#getDefaultStatusCode(routeData, pathname);
+ let response;
+ try {
+ const mod = await this.#pipeline.getModuleForRoute(routeData);
+ const renderContext = await RenderContext.create({
+ pipeline: this.#pipeline,
+ locals,
+ pathname,
+ request,
+ routeData,
+ status: defaultStatus
+ });
+ response = await renderContext.render(await mod.page());
+ } catch (err) {
+ this.#logger.error(null, err.stack || err.message || String(err));
+ return this.#renderError(request, { locals, status: 500, error: err });
+ }
+ if (REROUTABLE_STATUS_CODES.includes(response.status) && response.headers.get(REROUTE_DIRECTIVE_HEADER) !== "no") {
+ return this.#renderError(request, {
+ locals,
+ response,
+ status: response.status,
+ // We don't have an error to report here. Passing null means we pass nothing intentionally
+ // while undefined means there's no error
+ error: response.status === 500 ? null : void 0
+ });
+ }
+ if (response.headers.has(REROUTE_DIRECTIVE_HEADER)) {
+ response.headers.delete(REROUTE_DIRECTIVE_HEADER);
+ }
+ if (addCookieHeader) {
+ for (const setCookieHeaderValue of App.getSetCookieFromResponse(response)) {
+ response.headers.append("set-cookie", setCookieHeaderValue);
+ }
+ }
+ Reflect.set(response, responseSentSymbol$1, true);
+ return response;
+ }
+ #logRenderOptionsDeprecationWarning() {
+ if (this.#renderOptionsDeprecationWarningShown) return;
+ this.#logger.warn(
+ "deprecated",
+ `The adapter ${this.#manifest.adapterName} is using a deprecated signature of the 'app.render()' method. From Astro 4.0, locals and routeData are provided as properties on an optional object to this method. Using the old signature will cause an error in Astro 5.0. See https://github.com/withastro/astro/pull/9199 for more information.`
+ );
+ this.#renderOptionsDeprecationWarningShown = true;
+ }
+ setCookieHeaders(response) {
+ return getSetCookiesFromResponse(response);
+ }
+ /**
+ * Reads all the cookies written by `Astro.cookie.set()` onto the passed response.
+ * For example,
+ * ```ts
+ * for (const cookie_ of App.getSetCookieFromResponse(response)) {
+ * const cookie: string = cookie_
+ * }
+ * ```
+ * @param response The response to read cookies from.
+ * @returns An iterator that yields key-value pairs as equal-sign-separated strings.
+ */
+ static getSetCookieFromResponse = getSetCookiesFromResponse;
+ /**
+ * If it is a known error code, try sending the according page (e.g. 404.astro / 500.astro).
+ * This also handles pre-rendered /404 or /500 routes
+ */
+ async #renderError(request, {
+ locals,
+ status,
+ response: originalResponse,
+ skipMiddleware = false,
+ error
+ }) {
+ const errorRoutePath = `/${status}${this.#manifest.trailingSlash === "always" ? "/" : ""}`;
+ const errorRouteData = matchRoute(errorRoutePath, this.#manifestData);
+ const url = new URL(request.url);
+ if (errorRouteData) {
+ if (errorRouteData.prerender) {
+ const maybeDotHtml = errorRouteData.route.endsWith(`/${status}`) ? ".html" : "";
+ const statusURL = new URL(
+ `${this.#baseWithoutTrailingSlash}/${status}${maybeDotHtml}`,
+ url
+ );
+ if (statusURL.toString() !== request.url) {
+ const response2 = await fetch(statusURL.toString());
+ const override = { status };
+ return this.#mergeResponses(response2, originalResponse, override);
+ }
+ }
+ const mod = await this.#pipeline.getModuleForRoute(errorRouteData);
+ try {
+ const renderContext = await RenderContext.create({
+ locals,
+ pipeline: this.#pipeline,
+ middleware: skipMiddleware ? NOOP_MIDDLEWARE_FN : void 0,
+ pathname: this.#getPathnameFromRequest(request),
+ request,
+ routeData: errorRouteData,
+ status,
+ props: { error }
+ });
+ const response2 = await renderContext.render(await mod.page());
+ return this.#mergeResponses(response2, originalResponse);
+ } catch {
+ if (skipMiddleware === false) {
+ return this.#renderError(request, {
+ locals,
+ status,
+ response: originalResponse,
+ skipMiddleware: true
+ });
+ }
+ }
+ }
+ const response = this.#mergeResponses(new Response(null, { status }), originalResponse);
+ Reflect.set(response, responseSentSymbol$1, true);
+ return response;
+ }
+ #mergeResponses(newResponse, originalResponse, override) {
+ if (!originalResponse) {
+ if (override !== void 0) {
+ return new Response(newResponse.body, {
+ status: override.status,
+ statusText: newResponse.statusText,
+ headers: newResponse.headers
+ });
+ }
+ return newResponse;
+ }
+ const status = override?.status ? override.status : originalResponse.status === 200 ? newResponse.status : originalResponse.status;
+ try {
+ originalResponse.headers.delete("Content-type");
+ } catch {
+ }
+ return new Response(newResponse.body, {
+ status,
+ statusText: status === 200 ? newResponse.statusText : originalResponse.statusText,
+ // If you're looking at here for possible bugs, it means that it's not a bug.
+ // With the middleware, users can meddle with headers, and we should pass to the 404/500.
+ // If users see something weird, it's because they are setting some headers they should not.
+ //
+ // Although, we don't want it to replace the content-type, because the error page must return `text/html`
+ headers: new Headers([
+ ...Array.from(newResponse.headers),
+ ...Array.from(originalResponse.headers)
+ ])
+ });
+ }
+ #getDefaultStatusCode(routeData, pathname) {
+ if (!routeData.pattern.test(pathname)) {
+ for (const fallbackRoute of routeData.fallbackRoutes) {
+ if (fallbackRoute.pattern.test(pathname)) {
+ return 302;
+ }
+ }
+ }
+ const route = removeTrailingForwardSlash(routeData.route);
+ if (route.endsWith("/404")) return 404;
+ if (route.endsWith("/500")) return 500;
+ return 200;
+ }
+}
+
+const createOutgoingHttpHeaders = (headers) => {
+ if (!headers) {
+ return void 0;
+ }
+ const nodeHeaders = Object.fromEntries(headers.entries());
+ if (Object.keys(nodeHeaders).length === 0) {
+ return void 0;
+ }
+ if (headers.has("set-cookie")) {
+ const cookieHeaders = headers.getSetCookie();
+ if (cookieHeaders.length > 1) {
+ nodeHeaders["set-cookie"] = cookieHeaders;
+ }
+ }
+ return nodeHeaders;
+};
+
+function apply() {
+ if (!globalThis.crypto) {
+ Object.defineProperty(globalThis, "crypto", {
+ value: crypto.webcrypto
+ });
+ }
+ if (!globalThis.File) {
+ Object.defineProperty(globalThis, "File", {
+ value: buffer.File
+ });
+ }
+}
+
+class NodeApp extends App {
+ match(req) {
+ if (!(req instanceof Request)) {
+ req = NodeApp.createRequest(req, {
+ skipBody: true
+ });
+ }
+ return super.match(req);
+ }
+ render(req, routeDataOrOptions, maybeLocals) {
+ if (!(req instanceof Request)) {
+ req = NodeApp.createRequest(req);
+ }
+ return super.render(req, routeDataOrOptions, maybeLocals);
+ }
+ /**
+ * Converts a NodeJS IncomingMessage into a web standard Request.
+ * ```js
+ * import { NodeApp } from 'astro/app/node';
+ * import { createServer } from 'node:http';
+ *
+ * const server = createServer(async (req, res) => {
+ * const request = NodeApp.createRequest(req);
+ * const response = await app.render(request);
+ * await NodeApp.writeResponse(response, res);
+ * })
+ * ```
+ */
+ static createRequest(req, { skipBody = false } = {}) {
+ const isEncrypted = "encrypted" in req.socket && req.socket.encrypted;
+ const getFirstForwardedValue = (multiValueHeader) => {
+ return multiValueHeader?.toString()?.split(",").map((e) => e.trim())?.[0];
+ };
+ const forwardedProtocol = getFirstForwardedValue(req.headers["x-forwarded-proto"]);
+ const protocol = forwardedProtocol ?? (isEncrypted ? "https" : "http");
+ const forwardedHostname = getFirstForwardedValue(req.headers["x-forwarded-host"]);
+ const hostname = forwardedHostname ?? req.headers.host ?? req.headers[":authority"];
+ const port = getFirstForwardedValue(req.headers["x-forwarded-port"]);
+ const portInHostname = typeof hostname === "string" && /:\d+$/.test(hostname);
+ const hostnamePort = portInHostname ? hostname : `${hostname}${port ? `:${port}` : ""}`;
+ const url = `${protocol}://${hostnamePort}${req.url}`;
+ const options = {
+ method: req.method || "GET",
+ headers: makeRequestHeaders(req)
+ };
+ const bodyAllowed = options.method !== "HEAD" && options.method !== "GET" && skipBody === false;
+ if (bodyAllowed) {
+ Object.assign(options, makeRequestBody(req));
+ }
+ const request = new Request(url, options);
+ const forwardedClientIp = getFirstForwardedValue(req.headers["x-forwarded-for"]);
+ const clientIp = forwardedClientIp || req.socket?.remoteAddress;
+ if (clientIp) {
+ Reflect.set(request, clientAddressSymbol, clientIp);
+ }
+ return request;
+ }
+ /**
+ * Streams a web-standard Response into a NodeJS Server Response.
+ * ```js
+ * import { NodeApp } from 'astro/app/node';
+ * import { createServer } from 'node:http';
+ *
+ * const server = createServer(async (req, res) => {
+ * const request = NodeApp.createRequest(req);
+ * const response = await app.render(request);
+ * await NodeApp.writeResponse(response, res);
+ * })
+ * ```
+ * @param source WhatWG Response
+ * @param destination NodeJS ServerResponse
+ */
+ static async writeResponse(source, destination) {
+ const { status, headers, body, statusText } = source;
+ if (!(destination instanceof Http2ServerResponse)) {
+ destination.statusMessage = statusText;
+ }
+ destination.writeHead(status, createOutgoingHttpHeaders(headers));
+ if (!body) return destination.end();
+ try {
+ const reader = body.getReader();
+ destination.on("close", () => {
+ reader.cancel().catch((err) => {
+ console.error(
+ `There was an uncaught error in the middle of the stream while rendering ${destination.req.url}.`,
+ err
+ );
+ });
+ });
+ let result = await reader.read();
+ while (!result.done) {
+ destination.write(result.value);
+ result = await reader.read();
+ }
+ destination.end();
+ } catch (err) {
+ destination.write("Internal server error", () => {
+ err instanceof Error ? destination.destroy(err) : destination.destroy();
+ });
+ }
+ }
+}
+function makeRequestHeaders(req) {
+ const headers = new Headers();
+ for (const [name, value] of Object.entries(req.headers)) {
+ if (value === void 0) {
+ continue;
+ }
+ if (Array.isArray(value)) {
+ for (const item of value) {
+ headers.append(name, item);
+ }
+ } else {
+ headers.append(name, value);
+ }
+ }
+ return headers;
+}
+function makeRequestBody(req) {
+ if (req.body !== void 0) {
+ if (typeof req.body === "string" && req.body.length > 0) {
+ return { body: Buffer.from(req.body) };
+ }
+ if (typeof req.body === "object" && req.body !== null && Object.keys(req.body).length > 0) {
+ return { body: Buffer.from(JSON.stringify(req.body)) };
+ }
+ if (typeof req.body === "object" && req.body !== null && typeof req.body[Symbol.asyncIterator] !== "undefined") {
+ return asyncIterableToBodyProps(req.body);
+ }
+ }
+ return asyncIterableToBodyProps(req);
+}
+function asyncIterableToBodyProps(iterable) {
+ return {
+ // Node uses undici for the Request implementation. Undici accepts
+ // a non-standard async iterable for the body.
+ // @ts-expect-error
+ body: iterable,
+ // The duplex property is required when using a ReadableStream or async
+ // iterable for the body. The type definitions do not include the duplex
+ // property because they are not up-to-date.
+ duplex: "half"
+ };
+}
+
+/**
+ * Creates a Node.js http listener for on-demand rendered pages, compatible with http.createServer and Connect middleware.
+ * If the next callback is provided, it will be called if the request does not have a matching route.
+ * Intended to be used in both standalone and middleware mode.
+ */
+function createAppHandler(app) {
+ /**
+ * Keep track of the current request path using AsyncLocalStorage.
+ * Used to log unhandled rejections with a helpful message.
+ */
+ const als = new AsyncLocalStorage();
+ const logger = app.getAdapterLogger();
+ process.on('unhandledRejection', (reason) => {
+ const requestUrl = als.getStore();
+ logger.error(`Unhandled rejection while rendering ${requestUrl}`);
+ console.error(reason);
+ });
+ return async (req, res, next, locals) => {
+ let request;
+ try {
+ request = NodeApp.createRequest(req);
+ }
+ catch (err) {
+ logger.error(`Could not render ${req.url}`);
+ console.error(err);
+ res.statusCode = 500;
+ res.end('Internal Server Error');
+ return;
+ }
+ const routeData = app.match(request);
+ if (routeData) {
+ const response = await als.run(request.url, () => app.render(request, {
+ addCookieHeader: true,
+ locals,
+ routeData,
+ }));
+ await NodeApp.writeResponse(response, res);
+ }
+ else if (next) {
+ return next();
+ }
+ else {
+ const response = await app.render(req);
+ await NodeApp.writeResponse(response, res);
+ }
+ };
+}
+
+/**
+ * Creates a middleware that can be used with Express, Connect, etc.
+ *
+ * Similar to `createAppHandler` but can additionally be placed in the express
+ * chain as an error middleware.
+ *
+ * https://expressjs.com/en/guide/using-middleware.html#middleware.error-handling
+ */
+function createMiddleware(app) {
+ const handler = createAppHandler(app);
+ const logger = app.getAdapterLogger();
+ // using spread args because express trips up if the function's
+ // stringified body includes req, res, next, locals directly
+ return async (...args) => {
+ // assume normal invocation at first
+ const [req, res, next, locals] = args;
+ // short circuit if it is an error invocation
+ if (req instanceof Error) {
+ const error = req;
+ if (next) {
+ return next(error);
+ // biome-ignore lint/style/noUselessElse:
+ }
+ else {
+ throw error;
+ }
+ }
+ try {
+ await handler(req, res, next, locals);
+ }
+ catch (err) {
+ logger.error(`Could not render ${req.url}`);
+ console.error(err);
+ if (!res.headersSent) {
+ // biome-ignore lint/style/noUnusedTemplateLiteral:
+ res.writeHead(500, `Server error`);
+ res.end();
+ }
+ }
+ };
+}
+
+async function logListeningOn(logger, server, options) {
+ await new Promise((resolve) => server.once('listening', resolve));
+ const protocol = server instanceof https.Server ? 'https' : 'http';
+ // Allow to provide host value at runtime
+ const host = getResolvedHostForHttpServer(process.env.HOST !== undefined && process.env.HOST !== '' ? process.env.HOST : options.host);
+ const { port } = server.address();
+ const address = getNetworkAddress(protocol, host, port);
+ if (host === undefined) {
+ logger.info(`Server listening on \n local: ${address.local[0]} \t\n network: ${address.network[0]}\n`);
+ }
+ else {
+ logger.info(`Server listening on ${address.local[0]}`);
+ }
+}
+function getResolvedHostForHttpServer(host) {
+ if (host === false) {
+ // Use a secure default
+ return 'localhost';
+ // biome-ignore lint/style/noUselessElse:
+ }
+ else if (host === true) {
+ // If passed --host in the CLI without arguments
+ return undefined; // undefined typically means 0.0.0.0 or :: (listen on all IPs)
+ // biome-ignore lint/style/noUselessElse:
+ }
+ else {
+ return host;
+ }
+}
+const wildcardHosts = new Set(['0.0.0.0', '::', '0000:0000:0000:0000:0000:0000:0000:0000']);
+// this code from vite https://github.com/vitejs/vite/blob/d09bbd093a4b893e78f0bbff5b17c7cf7821f403/packages/vite/src/node/utils.ts#L892-L914
+function getNetworkAddress(
+// biome-ignore lint/style/useDefaultParameterLast:
+protocol = 'http', hostname, port, base) {
+ const NetworkAddress = {
+ local: [],
+ network: [],
+ };
+ // biome-ignore lint/complexity/noForEach:
+ Object.values(os.networkInterfaces())
+ .flatMap((nInterface) => nInterface ?? [])
+ .filter((detail) =>
+ // biome-ignore lint/complexity/useOptionalChain:
+ detail &&
+ detail.address &&
+ (detail.family === 'IPv4' ||
+ // @ts-expect-error Node 18.0 - 18.3 returns number
+ detail.family === 4))
+ .forEach((detail) => {
+ let host = detail.address.replace('127.0.0.1', hostname === undefined || wildcardHosts.has(hostname) ? 'localhost' : hostname);
+ // ipv6 host
+ if (host.includes(':')) {
+ host = `[${host}]`;
+ }
+ const url = `${protocol}://${host}:${port}${''}`;
+ if (detail.address.includes('127.0.0.1')) {
+ NetworkAddress.local.push(url);
+ }
+ else {
+ NetworkAddress.network.push(url);
+ }
+ });
+ return NetworkAddress;
+}
+
+// check for a dot followed by a extension made up of lowercase characters
+const isSubresourceRegex = /.+\.[a-z]+$/i;
+/**
+ * Creates a Node.js http listener for static files and prerendered pages.
+ * In standalone mode, the static handler is queried first for the static files.
+ * If one matching the request path is not found, it relegates to the SSR handler.
+ * Intended to be used only in the standalone mode.
+ */
+function createStaticHandler(app, options) {
+ const client = resolveClientDir(options);
+ /**
+ * @param ssr The SSR handler to be called if the static handler does not find a matching file.
+ */
+ return (req, res, ssr) => {
+ if (req.url) {
+ const [urlPath, urlQuery] = req.url.split('?');
+ const filePath = path.join(client, app.removeBase(urlPath));
+ let pathname;
+ let isDirectory = false;
+ try {
+ isDirectory = fs.lstatSync(filePath).isDirectory();
+ }
+ catch { }
+ const { trailingSlash = 'ignore' } = options;
+ const hasSlash = urlPath.endsWith('/');
+ switch (trailingSlash) {
+ case 'never':
+ // biome-ignore lint/suspicious/noDoubleEquals:
+ if (isDirectory && urlPath != '/' && hasSlash) {
+ // biome-ignore lint/style/useTemplate:
+ // biome-ignore lint/suspicious/noFallthroughSwitchClause:
+ pathname = urlPath.slice(0, -1) + (urlQuery ? '?' + urlQuery : '');
+ res.statusCode = 301;
+ res.setHeader('Location', pathname);
+ return res.end();
+ // biome-ignore lint/style/noUselessElse:
+ }
+ else
+ pathname = urlPath;
+ // intentionally fall through
+ case 'ignore':
+ {
+ if (isDirectory && !hasSlash) {
+ // biome-ignore lint/style/useTemplate:
+ pathname = urlPath + '/index.html';
+ }
+ else
+ pathname = urlPath;
+ }
+ break;
+ case 'always':
+ // trailing slash is not added to "subresources"
+ if (!hasSlash && !isSubresourceRegex.test(urlPath)) {
+ // biome-ignore lint/style/useTemplate:
+ pathname = urlPath + '/' + (urlQuery ? '?' + urlQuery : '');
+ res.statusCode = 301;
+ res.setHeader('Location', pathname);
+ return res.end();
+ // biome-ignore lint/style/noUselessElse:
+ }
+ else
+ pathname = urlPath;
+ break;
+ }
+ // app.removeBase sometimes returns a path without a leading slash
+ pathname = prependForwardSlash(app.removeBase(pathname));
+ const stream = send(req, pathname, {
+ root: client,
+ dotfiles: pathname.startsWith('/.well-known/') ? 'allow' : 'deny',
+ });
+ let forwardError = false;
+ stream.on('error', (err) => {
+ if (forwardError) {
+ console.error(err.toString());
+ res.writeHead(500);
+ res.end('Internal server error');
+ return;
+ }
+ // File not found, forward to the SSR handler
+ ssr();
+ });
+ stream.on('headers', (_res) => {
+ // assets in dist/_astro are hashed and should get the immutable header
+ if (pathname.startsWith(`/${options.assets}/`)) {
+ // This is the "far future" cache header, used for static files whose name includes their digest hash.
+ // 1 year (31,536,000 seconds) is convention.
+ // Taken from https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control#immutable
+ _res.setHeader('Cache-Control', 'public, max-age=31536000, immutable');
+ }
+ });
+ stream.on('file', () => {
+ forwardError = true;
+ });
+ stream.pipe(res);
+ }
+ else {
+ ssr();
+ }
+ };
+}
+function resolveClientDir(options) {
+ const clientURLRaw = new URL(options.client);
+ const serverURLRaw = new URL(options.server);
+ const rel = path.relative(url.fileURLToPath(serverURLRaw), url.fileURLToPath(clientURLRaw));
+ // walk up the parent folders until you find the one that is the root of the server entry folder. This is how we find the client folder relatively.
+ const serverFolder = path.basename(options.server);
+ let serverEntryFolderURL = path.dirname(import.meta.url);
+ while (!serverEntryFolderURL.endsWith(serverFolder)) {
+ serverEntryFolderURL = path.dirname(serverEntryFolderURL);
+ }
+ // biome-ignore lint/style/useTemplate:
+ const serverEntryURL = serverEntryFolderURL + '/entry.mjs';
+ const clientURL = new URL(appendForwardSlash(rel), serverEntryURL);
+ const client = url.fileURLToPath(clientURL);
+ return client;
+}
+function prependForwardSlash(pth) {
+ // biome-ignore lint/style/useTemplate:
+ return pth.startsWith('/') ? pth : '/' + pth;
+}
+function appendForwardSlash(pth) {
+ // biome-ignore lint/style/useTemplate:
+ return pth.endsWith('/') ? pth : pth + '/';
+}
+
+// Used to get Host Value at Runtime
+const hostOptions = (host) => {
+ if (typeof host === 'boolean') {
+ return host ? '0.0.0.0' : 'localhost';
+ }
+ return host;
+};
+function standalone(app, options) {
+ const port = process.env.PORT ? Number(process.env.PORT) : (options.port ?? 8080);
+ const host = process.env.HOST ?? hostOptions(options.host);
+ const handler = createStandaloneHandler(app, options);
+ const server = createServer(handler, host, port);
+ server.server.listen(port, host);
+ if (process.env.ASTRO_NODE_LOGGING !== 'disabled') {
+ logListeningOn(app.getAdapterLogger(), server.server, options);
+ }
+ return {
+ server,
+ done: server.closed(),
+ };
+}
+// also used by server entrypoint
+function createStandaloneHandler(app, options) {
+ const appHandler = createAppHandler(app);
+ const staticHandler = createStaticHandler(app, options);
+ return (req, res) => {
+ try {
+ // validate request path
+ // biome-ignore lint/style/noNonNullAssertion:
+ decodeURI(req.url);
+ }
+ catch {
+ res.writeHead(400);
+ res.end('Bad request.');
+ return;
+ }
+ staticHandler(req, res, () => appHandler(req, res));
+ };
+}
+// also used by preview entrypoint
+function createServer(listener, host, port) {
+ let httpServer;
+ if (process.env.SERVER_CERT_PATH && process.env.SERVER_KEY_PATH) {
+ httpServer = https.createServer({
+ key: fs.readFileSync(process.env.SERVER_KEY_PATH),
+ cert: fs.readFileSync(process.env.SERVER_CERT_PATH),
+ }, listener);
+ }
+ else {
+ httpServer = http.createServer(listener);
+ }
+ enableDestroy(httpServer);
+ // Resolves once the server is closed
+ const closed = new Promise((resolve, reject) => {
+ httpServer.addListener('close', resolve);
+ httpServer.addListener('error', reject);
+ });
+ const previewable = {
+ host,
+ port,
+ closed() {
+ return closed;
+ },
+ async stop() {
+ await new Promise((resolve, reject) => {
+ httpServer.destroy((err) => (err ? reject(err) : resolve(undefined)));
+ });
+ },
+ };
+ return {
+ server: httpServer,
+ ...previewable,
+ };
+}
+
+// This needs to run first because some internals depend on `crypto`
+apply();
+// Won't throw if the virtual module is not available because it's not supported in
+// the users's astro version or if astro:env is not enabled in the project
+await import('./astro/env-setup_Cr6XTFvb.mjs')
+ .then((mod) => mod.setGetEnv((key) => process.env[key]))
+ .catch(() => { });
+function createExports(manifest, options) {
+ const app = new NodeApp(manifest);
+ options.trailingSlash = manifest.trailingSlash;
+ return {
+ options: options,
+ handler: options.mode === 'middleware' ? createMiddleware(app) : createStandaloneHandler(app, options),
+ startServer: () => standalone(app, options),
+ };
+}
+function start(manifest, options) {
+ if (options.mode !== 'standalone' || process.env.ASTRO_NODE_AUTOSTART === 'disabled') {
+ return;
+ }
+ const app = new NodeApp(manifest);
+ standalone(app, options);
+}
+
+const serverEntrypointModule = /*#__PURE__*/Object.freeze(/*#__PURE__*/Object.defineProperty({
+ __proto__: null,
+ createExports,
+ start
+}, Symbol.toStringTag, { value: 'Module' }));
+
+export { start as a, createExports as c, serverEntrypointModule as s };
diff --git a/dist/server/chunks/adminAuth_CY4OwU1a.mjs b/dist/server/chunks/adminAuth_CY4OwU1a.mjs
new file mode 100644
index 0000000..2d87cf0
--- /dev/null
+++ b/dist/server/chunks/adminAuth_CY4OwU1a.mjs
@@ -0,0 +1,27 @@
+const sessionCookie = "ruseemrooz_admin";
+const getAdminUser = () => "admin";
+const getAdminPassword = () => "admin123";
+const getSessionSecret = () => "local-session";
+const encodeToken = () => Buffer.from(`${getAdminUser()}:${getSessionSecret()}`).toString("base64url");
+const isValidLogin = (username, password) => username === getAdminUser() && password === getAdminPassword();
+const setAdminSession = (context) => {
+ context.cookies.set(sessionCookie, encodeToken(), {
+ path: "/",
+ httpOnly: true,
+ sameSite: "lax",
+ secure: true,
+ maxAge: 60 * 60 * 8
+ });
+};
+const clearAdminSession = (context) => {
+ context.cookies.delete(sessionCookie, { path: "/" });
+};
+const isAdminAuthenticated = (context) => context.cookies.get(sessionCookie)?.value === encodeToken();
+const requireAdmin = (context) => {
+ if (!isAdminAuthenticated(context)) {
+ return new Response("Unauthorized", { status: 401 });
+ }
+ return null;
+};
+
+export { isValidLogin as a, clearAdminSession as c, isAdminAuthenticated as i, requireAdmin as r, setAdminSession as s };
diff --git a/dist/server/chunks/apiAuth_DcNSM7D5.mjs b/dist/server/chunks/apiAuth_DcNSM7D5.mjs
new file mode 100644
index 0000000..fadd1b9
--- /dev/null
+++ b/dist/server/chunks/apiAuth_DcNSM7D5.mjs
@@ -0,0 +1,16 @@
+const getToken = () => "admin123";
+const requireApiToken = (context) => {
+ const authorization = context.request.headers.get("authorization") ?? "";
+ const headerToken = context.request.headers.get("x-api-key") ?? "";
+ const bearerToken = authorization.startsWith("Bearer ") ? authorization.slice(7) : "";
+ const token = bearerToken || headerToken;
+ if (token !== getToken()) {
+ return new Response(JSON.stringify({ error: "Unauthorized" }), {
+ status: 401,
+ headers: { "content-type": "application/json; charset=utf-8" }
+ });
+ }
+ return null;
+};
+
+export { requireApiToken as r };
diff --git a/dist/server/chunks/astro-designed-error-pages_DsfhG0WT.mjs b/dist/server/chunks/astro-designed-error-pages_DsfhG0WT.mjs
new file mode 100644
index 0000000..16e8a3f
--- /dev/null
+++ b/dist/server/chunks/astro-designed-error-pages_DsfhG0WT.mjs
@@ -0,0 +1,281 @@
+import { N as NOOP_MIDDLEWARE_HEADER, D as DEFAULT_404_COMPONENT } from './astro/server_DrnzQWw3.mjs';
+import { parse } from 'devalue';
+import { escape } from 'html-escaper';
+
+const NOOP_MIDDLEWARE_FN = async (_ctx, next) => {
+ const response = await next();
+ response.headers.set(NOOP_MIDDLEWARE_HEADER, "true");
+ return response;
+};
+
+const ACTION_QUERY_PARAMS = {
+ actionName: "_astroAction"};
+
+const __vite_import_meta_env__ = {"ASSETS_PREFIX": undefined, "BASE_URL": "/", "DEV": false, "MODE": "production", "PROD": true, "SITE": undefined, "SSR": true};
+const codeToStatusMap = {
+ // Implemented from tRPC error code table
+ // https://trpc.io/docs/server/error-handling#error-codes
+ BAD_REQUEST: 400,
+ UNAUTHORIZED: 401,
+ FORBIDDEN: 403,
+ NOT_FOUND: 404,
+ TIMEOUT: 405,
+ CONFLICT: 409,
+ PRECONDITION_FAILED: 412,
+ PAYLOAD_TOO_LARGE: 413,
+ UNSUPPORTED_MEDIA_TYPE: 415,
+ UNPROCESSABLE_CONTENT: 422,
+ TOO_MANY_REQUESTS: 429,
+ CLIENT_CLOSED_REQUEST: 499,
+ INTERNAL_SERVER_ERROR: 500
+};
+const statusToCodeMap = Object.entries(codeToStatusMap).reduce(
+ // reverse the key-value pairs
+ (acc, [key, value]) => ({ ...acc, [value]: key }),
+ {}
+);
+class ActionError extends Error {
+ type = "AstroActionError";
+ code = "INTERNAL_SERVER_ERROR";
+ status = 500;
+ constructor(params) {
+ super(params.message);
+ this.code = params.code;
+ this.status = ActionError.codeToStatus(params.code);
+ if (params.stack) {
+ this.stack = params.stack;
+ }
+ }
+ static codeToStatus(code) {
+ return codeToStatusMap[code];
+ }
+ static statusToCode(status) {
+ return statusToCodeMap[status] ?? "INTERNAL_SERVER_ERROR";
+ }
+ static fromJson(body) {
+ if (isInputError(body)) {
+ return new ActionInputError(body.issues);
+ }
+ if (isActionError(body)) {
+ return new ActionError(body);
+ }
+ return new ActionError({
+ code: "INTERNAL_SERVER_ERROR"
+ });
+ }
+}
+function isActionError(error) {
+ return typeof error === "object" && error != null && "type" in error && error.type === "AstroActionError";
+}
+function isInputError(error) {
+ return typeof error === "object" && error != null && "type" in error && error.type === "AstroActionInputError" && "issues" in error && Array.isArray(error.issues);
+}
+class ActionInputError extends ActionError {
+ type = "AstroActionInputError";
+ // We don't expose all ZodError properties.
+ // Not all properties will serialize from server to client,
+ // and we don't want to import the full ZodError object into the client.
+ issues;
+ fields;
+ constructor(issues) {
+ super({
+ message: `Failed to validate: ${JSON.stringify(issues, null, 2)}`,
+ code: "BAD_REQUEST"
+ });
+ this.issues = issues;
+ this.fields = {};
+ for (const issue of issues) {
+ if (issue.path.length > 0) {
+ const key = issue.path[0].toString();
+ this.fields[key] ??= [];
+ this.fields[key]?.push(issue.message);
+ }
+ }
+ }
+}
+function getActionQueryString(name) {
+ const searchParams = new URLSearchParams({ [ACTION_QUERY_PARAMS.actionName]: name });
+ return `?${searchParams.toString()}`;
+}
+function deserializeActionResult(res) {
+ if (res.type === "error") {
+ let json;
+ try {
+ json = JSON.parse(res.body);
+ } catch {
+ return {
+ data: void 0,
+ error: new ActionError({
+ message: res.body,
+ code: "INTERNAL_SERVER_ERROR"
+ })
+ };
+ }
+ if (Object.assign(__vite_import_meta_env__, { _: process.env._ })?.PROD) {
+ return { error: ActionError.fromJson(json), data: void 0 };
+ } else {
+ const error = ActionError.fromJson(json);
+ error.stack = actionResultErrorStack.get();
+ return {
+ error,
+ data: void 0
+ };
+ }
+ }
+ if (res.type === "empty") {
+ return { data: void 0, error: void 0 };
+ }
+ return {
+ data: parse(res.body, {
+ URL: (href) => new URL(href)
+ }),
+ error: void 0
+ };
+}
+const actionResultErrorStack = /* @__PURE__ */ function actionResultErrorStackFn() {
+ let errorStack;
+ return {
+ set(stack) {
+ errorStack = stack;
+ },
+ get() {
+ return errorStack;
+ }
+ };
+}();
+
+function template({
+ title,
+ pathname,
+ statusCode = 404,
+ tabTitle,
+ body
+}) {
+ return `
+
+
+
+ ${tabTitle}
+
+
+
+
+
+ ${statusCode ? `${statusCode}: ` : ""}${title}
+ ${body || `
+ Path: ${escape(pathname)}
+ `}
+
+
+`;
+}
+
+const DEFAULT_404_ROUTE = {
+ component: DEFAULT_404_COMPONENT,
+ generate: () => "",
+ params: [],
+ pattern: /\/404/,
+ prerender: false,
+ pathname: "/404",
+ segments: [[{ content: "404", dynamic: false, spread: false }]],
+ type: "page",
+ route: "/404",
+ fallbackRoutes: [],
+ isIndex: false
+};
+function ensure404Route(manifest) {
+ if (!manifest.routes.some((route) => route.route === "/404")) {
+ manifest.routes.push(DEFAULT_404_ROUTE);
+ }
+ return manifest;
+}
+async function default404Page({ pathname }) {
+ return new Response(
+ template({
+ statusCode: 404,
+ title: "Not found",
+ tabTitle: "404: Not Found",
+ pathname
+ }),
+ { status: 404, headers: { "Content-Type": "text/html; charset=utf-8" } }
+ );
+}
+default404Page.isAstroComponentFactory = true;
+const default404Instance = {
+ default: default404Page
+};
+
+export { DEFAULT_404_ROUTE as D, NOOP_MIDDLEWARE_FN as N, default404Instance as a, deserializeActionResult as d, ensure404Route as e, getActionQueryString as g };
diff --git a/dist/server/chunks/astro/assets-service_BZIICsh3.mjs b/dist/server/chunks/astro/assets-service_BZIICsh3.mjs
new file mode 100644
index 0000000..331e426
--- /dev/null
+++ b/dist/server/chunks/astro/assets-service_BZIICsh3.mjs
@@ -0,0 +1,666 @@
+import { isRemotePath, joinPaths } from '@astrojs/internal-helpers/path';
+
+const ClientAddressNotAvailable = {
+ name: "ClientAddressNotAvailable",
+ title: "`Astro.clientAddress` is not available in current adapter.",
+ message: (adapterName) => `\`Astro.clientAddress\` is not available in the \`${adapterName}\` adapter. File an issue with the adapter to add support.`
+};
+const PrerenderClientAddressNotAvailable = {
+ name: "PrerenderClientAddressNotAvailable",
+ title: "`Astro.clientAddress` cannot be used inside prerendered routes.",
+ message: `\`Astro.clientAddress\` cannot be used inside prerendered routes`
+};
+const StaticClientAddressNotAvailable = {
+ name: "StaticClientAddressNotAvailable",
+ title: "`Astro.clientAddress` is not available in static mode.",
+ message: "`Astro.clientAddress` is only available when using `output: 'server'` or `output: 'hybrid'`. Update your Astro config if you need SSR features.",
+ hint: "See https://docs.astro.build/en/guides/server-side-rendering/ for more information on how to enable SSR."
+};
+const NoMatchingStaticPathFound = {
+ name: "NoMatchingStaticPathFound",
+ title: "No static path found for requested path.",
+ message: (pathName) => `A \`getStaticPaths()\` route pattern was matched, but no matching static path was found for requested path \`${pathName}\`.`,
+ hint: (possibleRoutes) => `Possible dynamic routes being matched: ${possibleRoutes.join(", ")}.`
+};
+const OnlyResponseCanBeReturned = {
+ name: "OnlyResponseCanBeReturned",
+ title: "Invalid type returned by Astro page.",
+ message: (route, returnedValue) => `Route \`${route ? route : ""}\` returned a \`${returnedValue}\`. Only a [Response](https://developer.mozilla.org/en-US/docs/Web/API/Response) can be returned from Astro files.`,
+ hint: "See https://docs.astro.build/en/guides/server-side-rendering/#response for more information."
+};
+const MissingMediaQueryDirective = {
+ name: "MissingMediaQueryDirective",
+ title: "Missing value for `client:media` directive.",
+ message: 'Media query not provided for `client:media` directive. A media query similar to `client:media="(max-width: 600px)"` must be provided'
+};
+const NoMatchingRenderer = {
+ name: "NoMatchingRenderer",
+ title: "No matching renderer found.",
+ message: (componentName, componentExtension, plural, validRenderersCount) => `Unable to render \`${componentName}\`.
+
+${validRenderersCount > 0 ? `There ${plural ? "are" : "is"} ${validRenderersCount} renderer${plural ? "s" : ""} configured in your \`astro.config.mjs\` file,
+but ${plural ? "none were" : "it was not"} able to server-side render \`${componentName}\`.` : `No valid renderer was found ${componentExtension ? `for the \`.${componentExtension}\` file extension.` : `for this file extension.`}`}`,
+ hint: (probableRenderers) => `Did you mean to enable the ${probableRenderers} integration?
+
+See https://docs.astro.build/en/guides/framework-components/ for more information on how to install and configure integrations.`
+};
+const NoClientEntrypoint = {
+ name: "NoClientEntrypoint",
+ title: "No client entrypoint specified in renderer.",
+ message: (componentName, clientDirective, rendererName) => `\`${componentName}\` component has a \`client:${clientDirective}\` directive, but no client entrypoint was provided by \`${rendererName}\`.`,
+ hint: "See https://docs.astro.build/en/reference/integrations-reference/#addrenderer-option for more information on how to configure your renderer."
+};
+const NoClientOnlyHint = {
+ name: "NoClientOnlyHint",
+ title: "Missing hint on client:only directive.",
+ message: (componentName) => `Unable to render \`${componentName}\`. When using the \`client:only\` hydration strategy, Astro needs a hint to use the correct renderer.`,
+ hint: (probableRenderers) => `Did you mean to pass \`client:only="${probableRenderers}"\`? See https://docs.astro.build/en/reference/directives-reference/#clientonly for more information on client:only`
+};
+const InvalidGetStaticPathsEntry = {
+ name: "InvalidGetStaticPathsEntry",
+ title: "Invalid entry inside getStaticPath's return value",
+ message: (entryType) => `Invalid entry returned by getStaticPaths. Expected an object, got \`${entryType}\``,
+ hint: "If you're using a `.map` call, you might be looking for `.flatMap()` instead. See https://docs.astro.build/en/reference/api-reference/#getstaticpaths for more information on getStaticPaths."
+};
+const InvalidGetStaticPathsReturn = {
+ name: "InvalidGetStaticPathsReturn",
+ title: "Invalid value returned by getStaticPaths.",
+ message: (returnType) => `Invalid type returned by \`getStaticPaths\`. Expected an \`array\`, got \`${returnType}\``,
+ hint: "See https://docs.astro.build/en/reference/api-reference/#getstaticpaths for more information on getStaticPaths."
+};
+const GetStaticPathsExpectedParams = {
+ name: "GetStaticPathsExpectedParams",
+ title: "Missing params property on `getStaticPaths` route.",
+ message: "Missing or empty required `params` property on `getStaticPaths` route.",
+ hint: "See https://docs.astro.build/en/reference/api-reference/#getstaticpaths for more information on getStaticPaths."
+};
+const GetStaticPathsInvalidRouteParam = {
+ name: "GetStaticPathsInvalidRouteParam",
+ title: "Invalid value for `getStaticPaths` route parameter.",
+ message: (key, value, valueType) => `Invalid getStaticPaths route parameter for \`${key}\`. Expected undefined, a string or a number, received \`${valueType}\` (\`${value}\`)`,
+ hint: "See https://docs.astro.build/en/reference/api-reference/#getstaticpaths for more information on getStaticPaths."
+};
+const GetStaticPathsRequired = {
+ name: "GetStaticPathsRequired",
+ title: "`getStaticPaths()` function required for dynamic routes.",
+ message: "`getStaticPaths()` function is required for dynamic routes. Make sure that you `export` a `getStaticPaths` function from your dynamic route.",
+ hint: `See https://docs.astro.build/en/guides/routing/#dynamic-routes for more information on dynamic routes.
+
+Alternatively, set \`output: "server"\` or \`output: "hybrid"\` in your Astro config file to switch to a non-static server build. This error can also occur if using \`export const prerender = true;\`.
+See https://docs.astro.build/en/guides/server-side-rendering/ for more information on non-static rendering.`
+};
+const ReservedSlotName = {
+ name: "ReservedSlotName",
+ title: "Invalid slot name.",
+ message: (slotName) => `Unable to create a slot named \`${slotName}\`. \`${slotName}\` is a reserved slot name. Please update the name of this slot.`
+};
+const NoMatchingImport = {
+ name: "NoMatchingImport",
+ title: "No import found for component.",
+ message: (componentName) => `Could not render \`${componentName}\`. No matching import has been found for \`${componentName}\`.`,
+ hint: "Please make sure the component is properly imported."
+};
+const InvalidComponentArgs = {
+ name: "InvalidComponentArgs",
+ title: "Invalid component arguments.",
+ message: (name) => `Invalid arguments passed to${name ? ` <${name}>` : ""} component.`,
+ hint: "Astro components cannot be rendered directly via function call, such as `Component()` or `{items.map(Component)}`."
+};
+const PageNumberParamNotFound = {
+ name: "PageNumberParamNotFound",
+ title: "Page number param not found.",
+ message: (paramName) => `[paginate()] page number param \`${paramName}\` not found in your filepath.`,
+ hint: "Rename your file to `[page].astro` or `[...page].astro`."
+};
+const ImageMissingAlt = {
+ name: "ImageMissingAlt",
+ title: 'Image missing required "alt" property.',
+ message: 'Image missing "alt" property. "alt" text is required to describe important images on the page.',
+ hint: 'Use an empty string ("") for decorative images.'
+};
+const InvalidImageService = {
+ name: "InvalidImageService",
+ title: "Error while loading image service.",
+ message: "There was an error loading the configured image service. Please see the stack trace for more information."
+};
+const MissingImageDimension = {
+ name: "MissingImageDimension",
+ title: "Missing image dimensions",
+ message: (missingDimension, imageURL) => `Missing ${missingDimension === "both" ? "width and height attributes" : `${missingDimension} attribute`} for ${imageURL}. When using remote images, both dimensions are required in order to avoid CLS.`,
+ hint: "If your image is inside your `src` folder, you probably meant to import it instead. See [the Imports guide for more information](https://docs.astro.build/en/guides/imports/#other-assets). You can also use `inferSize={true}` for remote images to get the original dimensions."
+};
+const FailedToFetchRemoteImageDimensions = {
+ name: "FailedToFetchRemoteImageDimensions",
+ title: "Failed to retrieve remote image dimensions",
+ message: (imageURL) => `Failed to get the dimensions for ${imageURL}.`,
+ hint: "Verify your remote image URL is accurate, and that you are not using `inferSize` with a file located in your `public/` folder."
+};
+const UnsupportedImageFormat = {
+ name: "UnsupportedImageFormat",
+ title: "Unsupported image format",
+ message: (format, imagePath, supportedFormats) => `Received unsupported format \`${format}\` from \`${imagePath}\`. Currently only ${supportedFormats.join(
+ ", "
+ )} are supported by our image services.`,
+ hint: "Using an `img` tag directly instead of the `Image` component might be what you're looking for."
+};
+const UnsupportedImageConversion = {
+ name: "UnsupportedImageConversion",
+ title: "Unsupported image conversion",
+ message: "Converting between vector (such as SVGs) and raster (such as PNGs and JPEGs) images is not currently supported."
+};
+const PrerenderDynamicEndpointPathCollide = {
+ name: "PrerenderDynamicEndpointPathCollide",
+ title: "Prerendered dynamic endpoint has path collision.",
+ message: (pathname) => `Could not render \`${pathname}\` with an \`undefined\` param as the generated path will collide during prerendering. Prevent passing \`undefined\` as \`params\` for the endpoint's \`getStaticPaths()\` function, or add an additional extension to the endpoint's filename.`,
+ hint: (filename) => `Rename \`${filename}\` to \`${filename.replace(/\.(?:js|ts)/, (m) => `.json` + m)}\``
+};
+const ExpectedImage = {
+ name: "ExpectedImage",
+ title: "Expected src to be an image.",
+ message: (src, typeofOptions, fullOptions) => `Expected \`src\` property for \`getImage\` or \`\` to be either an ESM imported image or a string with the path of a remote image. Received \`${src}\` (type: \`${typeofOptions}\`).
+
+Full serialized options received: \`${fullOptions}\`.`,
+ hint: "This error can often happen because of a wrong path. Make sure the path to your image is correct. If you're passing an async function, make sure to call and await it."
+};
+const ExpectedImageOptions = {
+ name: "ExpectedImageOptions",
+ title: "Expected image options.",
+ message: (options) => `Expected getImage() parameter to be an object. Received \`${options}\`.`
+};
+const ExpectedNotESMImage = {
+ name: "ExpectedNotESMImage",
+ title: "Expected image options, not an ESM-imported image.",
+ message: "An ESM-imported image cannot be passed directly to `getImage()`. Instead, pass an object with the image in the `src` property.",
+ hint: "Try changing `getImage(myImage)` to `getImage({ src: myImage })`"
+};
+const IncompatibleDescriptorOptions = {
+ name: "IncompatibleDescriptorOptions",
+ title: "Cannot set both `densities` and `widths`",
+ message: "Only one of `densities` or `widths` can be specified. In most cases, you'll probably want to use only `widths` if you require specific widths.",
+ hint: "Those attributes are used to construct a `srcset` attribute, which cannot have both `x` and `w` descriptors."
+};
+const NoImageMetadata = {
+ name: "NoImageMetadata",
+ title: "Could not process image metadata.",
+ message: (imagePath) => `Could not process image metadata${imagePath ? ` for \`${imagePath}\`` : ""}.`,
+ hint: "This is often caused by a corrupted or malformed image. Re-exporting the image from your image editor may fix this issue."
+};
+const ResponseSentError = {
+ name: "ResponseSentError",
+ title: "Unable to set response.",
+ message: "The response has already been sent to the browser and cannot be altered."
+};
+const MiddlewareNoDataOrNextCalled = {
+ name: "MiddlewareNoDataOrNextCalled",
+ title: "The middleware didn't return a `Response`.",
+ message: "Make sure your middleware returns a `Response` object, either directly or by returning the `Response` from calling the `next` function."
+};
+const MiddlewareNotAResponse = {
+ name: "MiddlewareNotAResponse",
+ title: "The middleware returned something that is not a `Response` object.",
+ message: "Any data returned from middleware must be a valid `Response` object."
+};
+const EndpointDidNotReturnAResponse = {
+ name: "EndpointDidNotReturnAResponse",
+ title: "The endpoint did not return a `Response`.",
+ message: "An endpoint must return either a `Response`, or a `Promise` that resolves with a `Response`."
+};
+const LocalsNotAnObject = {
+ name: "LocalsNotAnObject",
+ title: "Value assigned to `locals` is not accepted.",
+ message: "`locals` can only be assigned to an object. Other values like numbers, strings, etc. are not accepted.",
+ hint: "If you tried to remove some information from the `locals` object, try to use `delete` or set the property to `undefined`."
+};
+const AstroResponseHeadersReassigned = {
+ name: "AstroResponseHeadersReassigned",
+ title: "`Astro.response.headers` must not be reassigned.",
+ message: "Individual headers can be added to and removed from `Astro.response.headers`, but it must not be replaced with another instance of `Headers` altogether.",
+ hint: "Consider using `Astro.response.headers.add()`, and `Astro.response.headers.delete()`."
+};
+const LocalImageUsedWrongly = {
+ name: "LocalImageUsedWrongly",
+ title: "Local images must be imported.",
+ message: (imageFilePath) => `\`Image\`'s and \`getImage\`'s \`src\` parameter must be an imported image or an URL, it cannot be a string filepath. Received \`${imageFilePath}\`.`,
+ hint: "If you want to use an image from your `src` folder, you need to either import it or if the image is coming from a content collection, use the [image() schema helper](https://docs.astro.build/en/guides/images/#images-in-content-collections). See https://docs.astro.build/en/guides/images/#src-required for more information on the `src` property."
+};
+const AstroGlobUsedOutside = {
+ name: "AstroGlobUsedOutside",
+ title: "Astro.glob() used outside of an Astro file.",
+ message: (globStr) => `\`Astro.glob(${globStr})\` can only be used in \`.astro\` files. \`import.meta.glob(${globStr})\` can be used instead to achieve a similar result.`,
+ hint: "See Vite's documentation on `import.meta.glob` for more information: https://vite.dev/guide/features.html#glob-import"
+};
+const AstroGlobNoMatch = {
+ name: "AstroGlobNoMatch",
+ title: "Astro.glob() did not match any files.",
+ message: (globStr) => `\`Astro.glob(${globStr})\` did not return any matching files.`,
+ hint: "Check the pattern for typos."
+};
+const MissingSharp = {
+ name: "MissingSharp",
+ title: "Could not find Sharp.",
+ message: "Could not find Sharp. Please install Sharp (`sharp`) manually into your project or migrate to another image service.",
+ hint: "See Sharp's installation instructions for more information: https://sharp.pixelplumbing.com/install. If you are not relying on `astro:assets` to optimize, transform, or process any images, you can configure a passthrough image service instead of installing Sharp. See https://docs.astro.build/en/reference/errors/missing-sharp for more information.\n\nSee https://docs.astro.build/en/guides/images/#default-image-service for more information on how to migrate to another image service."
+};
+const i18nNoLocaleFoundInPath = {
+ name: "i18nNoLocaleFoundInPath",
+ title: "The path doesn't contain any locale",
+ message: "You tried to use an i18n utility on a path that doesn't contain any locale. You can use `pathHasLocale` first to determine if the path has a locale."
+};
+const RewriteWithBodyUsed = {
+ name: "RewriteWithBodyUsed",
+ title: "Cannot use Astro.rewrite after the request body has been read",
+ message: "Astro.rewrite() cannot be used if the request body has already been read. If you need to read the body, first clone the request."
+};
+
+function normalizeLF(code) {
+ return code.replace(/\r\n|\r(?!\n)|\n/g, "\n");
+}
+
+function codeFrame(src, loc) {
+ if (!loc || loc.line === void 0 || loc.column === void 0) {
+ return "";
+ }
+ const lines = normalizeLF(src).split("\n").map((ln) => ln.replace(/\t/g, " "));
+ const visibleLines = [];
+ for (let n = -2; n <= 2; n++) {
+ if (lines[loc.line + n]) visibleLines.push(loc.line + n);
+ }
+ let gutterWidth = 0;
+ for (const lineNo of visibleLines) {
+ let w = `> ${lineNo}`;
+ if (w.length > gutterWidth) gutterWidth = w.length;
+ }
+ let output = "";
+ for (const lineNo of visibleLines) {
+ const isFocusedLine = lineNo === loc.line - 1;
+ output += isFocusedLine ? "> " : " ";
+ output += `${lineNo + 1} | ${lines[lineNo]}
+`;
+ if (isFocusedLine)
+ output += `${Array.from({ length: gutterWidth }).join(" ")} | ${Array.from({
+ length: loc.column
+ }).join(" ")}^
+`;
+ }
+ return output;
+}
+
+class AstroError extends Error {
+ loc;
+ title;
+ hint;
+ frame;
+ type = "AstroError";
+ constructor(props, options) {
+ const { name, title, message, stack, location, hint, frame } = props;
+ super(message, options);
+ this.title = title;
+ this.name = name;
+ if (message) this.message = message;
+ this.stack = stack ? stack : this.stack;
+ this.loc = location;
+ this.hint = hint;
+ this.frame = frame;
+ }
+ setLocation(location) {
+ this.loc = location;
+ }
+ setName(name) {
+ this.name = name;
+ }
+ setMessage(message) {
+ this.message = message;
+ }
+ setHint(hint) {
+ this.hint = hint;
+ }
+ setFrame(source, location) {
+ this.frame = codeFrame(source, location);
+ }
+ static is(err) {
+ return err.type === "AstroError";
+ }
+}
+
+const URL_PROTOCOL_REGEX = /^(?:(?:http|ftp|https|ws):?\/\/|\/\/)/;
+function isCoreRemotePath(path) {
+ return URL_PROTOCOL_REGEX.test(path) || isRemotePath(path);
+}
+
+const VALID_SUPPORTED_FORMATS = [
+ "jpeg",
+ "jpg",
+ "png",
+ "tiff",
+ "webp",
+ "gif",
+ "svg",
+ "avif"
+];
+const DEFAULT_OUTPUT_FORMAT = "webp";
+const DEFAULT_HASH_PROPS = ["src", "width", "height", "format", "quality"];
+
+function isESMImportedImage(src) {
+ return typeof src === "object";
+}
+function isRemoteImage(src) {
+ return typeof src === "string";
+}
+async function resolveSrc(src) {
+ return typeof src === "object" && "then" in src ? (await src).default ?? await src : src;
+}
+
+function matchPattern(url, remotePattern) {
+ return matchProtocol(url, remotePattern.protocol) && matchHostname(url, remotePattern.hostname, true) && matchPort(url, remotePattern.port) && matchPathname(url, remotePattern.pathname);
+}
+function matchPort(url, port) {
+ return !port || port === url.port;
+}
+function matchProtocol(url, protocol) {
+ return !protocol || protocol === url.protocol.slice(0, -1);
+}
+function matchHostname(url, hostname, allowWildcard) {
+ if (!hostname) {
+ return true;
+ } else if (!allowWildcard || !hostname.startsWith("*")) {
+ return hostname === url.hostname;
+ } else if (hostname.startsWith("**.")) {
+ const slicedHostname = hostname.slice(2);
+ return slicedHostname !== url.hostname && url.hostname.endsWith(slicedHostname);
+ } else if (hostname.startsWith("*.")) {
+ const slicedHostname = hostname.slice(1);
+ const additionalSubdomains = url.hostname.replace(slicedHostname, "").split(".").filter(Boolean);
+ return additionalSubdomains.length === 1;
+ }
+ return false;
+}
+function matchPathname(url, pathname, allowWildcard) {
+ if (!pathname) {
+ return true;
+ } else if (!pathname.endsWith("*")) {
+ return pathname === url.pathname;
+ } else if (pathname.endsWith("/**")) {
+ const slicedPathname = pathname.slice(0, -2);
+ return slicedPathname !== url.pathname && url.pathname.startsWith(slicedPathname);
+ } else if (pathname.endsWith("/*")) {
+ const slicedPathname = pathname.slice(0, -1);
+ const additionalPathChunks = url.pathname.replace(slicedPathname, "").split("/").filter(Boolean);
+ return additionalPathChunks.length === 1;
+ }
+ return false;
+}
+function isRemoteAllowed(src, {
+ domains = [],
+ remotePatterns = []
+}) {
+ if (!isCoreRemotePath(src)) return false;
+ const url = new URL(src);
+ return domains.some((domain) => matchHostname(url, domain)) || remotePatterns.some((remotePattern) => matchPattern(url, remotePattern));
+}
+
+function isLocalService(service) {
+ if (!service) {
+ return false;
+ }
+ return "transform" in service;
+}
+function parseQuality(quality) {
+ let result = parseInt(quality);
+ if (Number.isNaN(result)) {
+ return quality;
+ }
+ return result;
+}
+const baseService = {
+ validateOptions(options) {
+ if (!options.src || typeof options.src !== "string" && typeof options.src !== "object") {
+ throw new AstroError({
+ ...ExpectedImage,
+ message: ExpectedImage.message(
+ JSON.stringify(options.src),
+ typeof options.src,
+ JSON.stringify(options, (_, v) => v === void 0 ? null : v)
+ )
+ });
+ }
+ if (!isESMImportedImage(options.src)) {
+ if (options.src.startsWith("/@fs/") || !isCoreRemotePath(options.src) && !options.src.startsWith("/")) {
+ throw new AstroError({
+ ...LocalImageUsedWrongly,
+ message: LocalImageUsedWrongly.message(options.src)
+ });
+ }
+ let missingDimension;
+ if (!options.width && !options.height) {
+ missingDimension = "both";
+ } else if (!options.width && options.height) {
+ missingDimension = "width";
+ } else if (options.width && !options.height) {
+ missingDimension = "height";
+ }
+ if (missingDimension) {
+ throw new AstroError({
+ ...MissingImageDimension,
+ message: MissingImageDimension.message(missingDimension, options.src)
+ });
+ }
+ } else {
+ if (!VALID_SUPPORTED_FORMATS.includes(options.src.format)) {
+ throw new AstroError({
+ ...UnsupportedImageFormat,
+ message: UnsupportedImageFormat.message(
+ options.src.format,
+ options.src.src,
+ VALID_SUPPORTED_FORMATS
+ )
+ });
+ }
+ if (options.widths && options.densities) {
+ throw new AstroError(IncompatibleDescriptorOptions);
+ }
+ if (options.src.format === "svg") {
+ options.format = "svg";
+ }
+ if (options.src.format === "svg" && options.format !== "svg" || options.src.format !== "svg" && options.format === "svg") {
+ throw new AstroError(UnsupportedImageConversion);
+ }
+ }
+ if (!options.format) {
+ options.format = DEFAULT_OUTPUT_FORMAT;
+ }
+ if (options.width) options.width = Math.round(options.width);
+ if (options.height) options.height = Math.round(options.height);
+ return options;
+ },
+ getHTMLAttributes(options) {
+ const { targetWidth, targetHeight } = getTargetDimensions(options);
+ const { src, width, height, format, quality, densities, widths, formats, ...attributes } = options;
+ return {
+ ...attributes,
+ width: targetWidth,
+ height: targetHeight,
+ loading: attributes.loading ?? "lazy",
+ decoding: attributes.decoding ?? "async"
+ };
+ },
+ getSrcSet(options) {
+ const srcSet = [];
+ const { targetWidth } = getTargetDimensions(options);
+ const { widths, densities } = options;
+ const targetFormat = options.format ?? DEFAULT_OUTPUT_FORMAT;
+ let imageWidth = options.width;
+ let maxWidth = Infinity;
+ if (isESMImportedImage(options.src)) {
+ imageWidth = options.src.width;
+ maxWidth = imageWidth;
+ }
+ const {
+ width: transformWidth,
+ height: transformHeight,
+ ...transformWithoutDimensions
+ } = options;
+ const allWidths = [];
+ if (densities) {
+ const densityValues = densities.map((density) => {
+ if (typeof density === "number") {
+ return density;
+ } else {
+ return parseFloat(density);
+ }
+ });
+ const densityWidths = densityValues.sort().map((density) => Math.round(targetWidth * density));
+ allWidths.push(
+ ...densityWidths.map((width, index) => ({
+ maxTargetWidth: Math.min(width, maxWidth),
+ descriptor: `${densityValues[index]}x`
+ }))
+ );
+ } else if (widths) {
+ allWidths.push(
+ ...widths.map((width) => ({
+ maxTargetWidth: Math.min(width, maxWidth),
+ descriptor: `${width}w`
+ }))
+ );
+ }
+ for (const { maxTargetWidth, descriptor } of allWidths) {
+ const srcSetTransform = { ...transformWithoutDimensions };
+ if (maxTargetWidth !== imageWidth) {
+ srcSetTransform.width = maxTargetWidth;
+ } else {
+ if (options.width && options.height) {
+ srcSetTransform.width = options.width;
+ srcSetTransform.height = options.height;
+ }
+ }
+ srcSet.push({
+ transform: srcSetTransform,
+ descriptor,
+ attributes: {
+ type: `image/${targetFormat}`
+ }
+ });
+ }
+ return srcSet;
+ },
+ getURL(options, imageConfig) {
+ const searchParams = new URLSearchParams();
+ if (isESMImportedImage(options.src)) {
+ searchParams.append("href", options.src.src);
+ } else if (isRemoteAllowed(options.src, imageConfig)) {
+ searchParams.append("href", options.src);
+ } else {
+ return options.src;
+ }
+ const params = {
+ w: "width",
+ h: "height",
+ q: "quality",
+ f: "format"
+ };
+ Object.entries(params).forEach(([param, key]) => {
+ options[key] && searchParams.append(param, options[key].toString());
+ });
+ const imageEndpoint = joinPaths("/", "/_image");
+ return `${imageEndpoint}?${searchParams}`;
+ },
+ parseURL(url) {
+ const params = url.searchParams;
+ if (!params.has("href")) {
+ return void 0;
+ }
+ const transform = {
+ src: params.get("href"),
+ width: params.has("w") ? parseInt(params.get("w")) : void 0,
+ height: params.has("h") ? parseInt(params.get("h")) : void 0,
+ format: params.get("f"),
+ quality: params.get("q")
+ };
+ return transform;
+ }
+};
+function getTargetDimensions(options) {
+ let targetWidth = options.width;
+ let targetHeight = options.height;
+ if (isESMImportedImage(options.src)) {
+ const aspectRatio = options.src.width / options.src.height;
+ if (targetHeight && !targetWidth) {
+ targetWidth = Math.round(targetHeight * aspectRatio);
+ } else if (targetWidth && !targetHeight) {
+ targetHeight = Math.round(targetWidth / aspectRatio);
+ } else if (!targetWidth && !targetHeight) {
+ targetWidth = options.src.width;
+ targetHeight = options.src.height;
+ }
+ }
+ return {
+ targetWidth,
+ targetHeight
+ };
+}
+
+let sharp;
+const qualityTable = {
+ low: 25,
+ mid: 50,
+ high: 80,
+ max: 100
+};
+async function loadSharp() {
+ let sharpImport;
+ try {
+ sharpImport = (await import('sharp')).default;
+ } catch {
+ throw new AstroError(MissingSharp);
+ }
+ sharpImport.cache(false);
+ return sharpImport;
+}
+const sharpService = {
+ validateOptions: baseService.validateOptions,
+ getURL: baseService.getURL,
+ parseURL: baseService.parseURL,
+ getHTMLAttributes: baseService.getHTMLAttributes,
+ getSrcSet: baseService.getSrcSet,
+ async transform(inputBuffer, transformOptions, config) {
+ if (!sharp) sharp = await loadSharp();
+ const transform = transformOptions;
+ if (transform.format === "svg") return { data: inputBuffer, format: "svg" };
+ const result = sharp(inputBuffer, {
+ failOnError: false,
+ pages: -1,
+ limitInputPixels: config.service.config.limitInputPixels
+ });
+ result.rotate();
+ if (transform.height && !transform.width) {
+ result.resize({ height: Math.round(transform.height) });
+ } else if (transform.width) {
+ result.resize({ width: Math.round(transform.width) });
+ }
+ if (transform.format) {
+ let quality = void 0;
+ if (transform.quality) {
+ const parsedQuality = parseQuality(transform.quality);
+ if (typeof parsedQuality === "number") {
+ quality = parsedQuality;
+ } else {
+ quality = transform.quality in qualityTable ? qualityTable[transform.quality] : void 0;
+ }
+ }
+ result.toFormat(transform.format, { quality });
+ }
+ const { data, info } = await result.toBuffer({ resolveWithObject: true });
+ return {
+ data,
+ format: info.format
+ };
+ }
+};
+var sharp_default = sharpService;
+
+const sharp$1 = /*#__PURE__*/Object.freeze(/*#__PURE__*/Object.defineProperty({
+ __proto__: null,
+ default: sharp_default
+}, Symbol.toStringTag, { value: 'Module' }));
+
+export { AstroError as A, ReservedSlotName as B, RewriteWithBodyUsed as C, DEFAULT_HASH_PROPS as D, EndpointDidNotReturnAResponse as E, FailedToFetchRemoteImageDimensions as F, GetStaticPathsRequired as G, PrerenderClientAddressNotAvailable as H, InvalidComponentArgs as I, ClientAddressNotAvailable as J, AstroResponseHeadersReassigned as K, LocalsNotAnObject as L, MissingMediaQueryDirective as M, NoMatchingImport as N, OnlyResponseCanBeReturned as O, PageNumberParamNotFound as P, sharp$1 as Q, ResponseSentError as R, StaticClientAddressNotAvailable as S, AstroGlobUsedOutside as a, AstroGlobNoMatch as b, NoMatchingRenderer as c, NoClientOnlyHint as d, NoClientEntrypoint as e, NoImageMetadata as f, ExpectedImageOptions as g, ExpectedImage as h, ExpectedNotESMImage as i, isRemoteImage as j, isCoreRemotePath as k, isESMImportedImage as l, isLocalService as m, InvalidImageService as n, ImageMissingAlt as o, isRemoteAllowed as p, i18nNoLocaleFoundInPath as q, resolveSrc as r, MiddlewareNoDataOrNextCalled as s, MiddlewareNotAResponse as t, InvalidGetStaticPathsReturn as u, InvalidGetStaticPathsEntry as v, GetStaticPathsExpectedParams as w, GetStaticPathsInvalidRouteParam as x, NoMatchingStaticPathFound as y, PrerenderDynamicEndpointPathCollide as z };
diff --git a/dist/server/chunks/astro/env-setup_Cr6XTFvb.mjs b/dist/server/chunks/astro/env-setup_Cr6XTFvb.mjs
new file mode 100644
index 0000000..a7a04d0
--- /dev/null
+++ b/dist/server/chunks/astro/env-setup_Cr6XTFvb.mjs
@@ -0,0 +1,4 @@
+function setGetEnv(fn, reset = false) {
+}
+
+export { setGetEnv };
diff --git a/dist/server/chunks/astro/server_DrnzQWw3.mjs b/dist/server/chunks/astro/server_DrnzQWw3.mjs
new file mode 100644
index 0000000..ec37702
--- /dev/null
+++ b/dist/server/chunks/astro/server_DrnzQWw3.mjs
@@ -0,0 +1,2055 @@
+import { A as AstroError, I as InvalidComponentArgs, a as AstroGlobUsedOutside, b as AstroGlobNoMatch, E as EndpointDidNotReturnAResponse, M as MissingMediaQueryDirective, N as NoMatchingImport, O as OnlyResponseCanBeReturned, R as ResponseSentError, c as NoMatchingRenderer, d as NoClientOnlyHint, e as NoClientEntrypoint } from './assets-service_BZIICsh3.mjs';
+import { bold } from 'kleur/colors';
+import { clsx } from 'clsx';
+import { escape } from 'html-escaper';
+import { encodeHexUpperCase, encodeBase64, decodeBase64, decodeHex } from '@oslojs/encoding';
+import 'cssesc';
+
+function validateArgs(args) {
+ if (args.length !== 3) return false;
+ if (!args[0] || typeof args[0] !== "object") return false;
+ return true;
+}
+function baseCreateComponent(cb, moduleId, propagation) {
+ const name = moduleId?.split("/").pop()?.replace(".astro", "") ?? "";
+ const fn = (...args) => {
+ if (!validateArgs(args)) {
+ throw new AstroError({
+ ...InvalidComponentArgs,
+ message: InvalidComponentArgs.message(name)
+ });
+ }
+ return cb(...args);
+ };
+ Object.defineProperty(fn, "name", { value: name, writable: false });
+ fn.isAstroComponentFactory = true;
+ fn.moduleId = moduleId;
+ fn.propagation = propagation;
+ return fn;
+}
+function createComponentWithOptions(opts) {
+ const cb = baseCreateComponent(opts.factory, opts.moduleId, opts.propagation);
+ return cb;
+}
+function createComponent(arg1, moduleId, propagation) {
+ if (typeof arg1 === "function") {
+ return baseCreateComponent(arg1, moduleId, propagation);
+ } else {
+ return createComponentWithOptions(arg1);
+ }
+}
+
+const ASTRO_VERSION = "4.16.19";
+const REROUTE_DIRECTIVE_HEADER = "X-Astro-Reroute";
+const REWRITE_DIRECTIVE_HEADER_KEY = "X-Astro-Rewrite";
+const REWRITE_DIRECTIVE_HEADER_VALUE = "yes";
+const NOOP_MIDDLEWARE_HEADER = "X-Astro-Noop";
+const ROUTE_TYPE_HEADER = "X-Astro-Route-Type";
+const DEFAULT_404_COMPONENT = "astro-default-404.astro";
+const REROUTABLE_STATUS_CODES = [404, 500];
+const clientAddressSymbol = Symbol.for("astro.clientAddress");
+const clientLocalsSymbol = Symbol.for("astro.locals");
+const originPathnameSymbol = Symbol.for("astro.originPathname");
+const responseSentSymbol = Symbol.for("astro.responseSent");
+
+function createAstroGlobFn() {
+ const globHandler = (importMetaGlobResult) => {
+ if (typeof importMetaGlobResult === "string") {
+ throw new AstroError({
+ ...AstroGlobUsedOutside,
+ message: AstroGlobUsedOutside.message(JSON.stringify(importMetaGlobResult))
+ });
+ }
+ let allEntries = [...Object.values(importMetaGlobResult)];
+ if (allEntries.length === 0) {
+ throw new AstroError({
+ ...AstroGlobNoMatch,
+ message: AstroGlobNoMatch.message(JSON.stringify(importMetaGlobResult))
+ });
+ }
+ return Promise.all(allEntries.map((fn) => fn()));
+ };
+ return globHandler;
+}
+function createAstro(site) {
+ return {
+ // TODO: this is no longer necessary for `Astro.site`
+ // but it somehow allows working around caching issues in content collections for some tests
+ site: void 0,
+ generator: `Astro v${ASTRO_VERSION}`,
+ glob: createAstroGlobFn()
+ };
+}
+
+async function renderEndpoint(mod, context, ssr, logger) {
+ const { request, url } = context;
+ const method = request.method.toUpperCase();
+ const handler = mod[method] ?? mod["ALL"];
+ if (!ssr && ssr === false && method !== "GET") {
+ logger.warn(
+ "router",
+ `${url.pathname} ${bold(
+ method
+ )} requests are not available for a static site. Update your config to \`output: 'server'\` or \`output: 'hybrid'\` to enable.`
+ );
+ }
+ if (handler === void 0) {
+ logger.warn(
+ "router",
+ `No API Route handler exists for the method "${method}" for the route "${url.pathname}".
+Found handlers: ${Object.keys(mod).map((exp) => JSON.stringify(exp)).join(", ")}
+` + ("all" in mod ? `One of the exported handlers is "all" (lowercase), did you mean to export 'ALL'?
+` : "")
+ );
+ return new Response(null, { status: 404 });
+ }
+ if (typeof handler !== "function") {
+ logger.error(
+ "router",
+ `The route "${url.pathname}" exports a value for the method "${method}", but it is of the type ${typeof handler} instead of a function.`
+ );
+ return new Response(null, { status: 500 });
+ }
+ let response = await handler.call(mod, context);
+ if (!response || response instanceof Response === false) {
+ throw new AstroError(EndpointDidNotReturnAResponse);
+ }
+ if (REROUTABLE_STATUS_CODES.includes(response.status)) {
+ try {
+ response.headers.set(REROUTE_DIRECTIVE_HEADER, "no");
+ } catch (err) {
+ if (err.message?.includes("immutable")) {
+ response = new Response(response.body, response);
+ response.headers.set(REROUTE_DIRECTIVE_HEADER, "no");
+ } else {
+ throw err;
+ }
+ }
+ }
+ return response;
+}
+
+function isPromise(value) {
+ return !!value && typeof value === "object" && "then" in value && typeof value.then === "function";
+}
+async function* streamAsyncIterator(stream) {
+ const reader = stream.getReader();
+ try {
+ while (true) {
+ const { done, value } = await reader.read();
+ if (done) return;
+ yield value;
+ }
+ } finally {
+ reader.releaseLock();
+ }
+}
+
+const escapeHTML = escape;
+class HTMLBytes extends Uint8Array {
+}
+Object.defineProperty(HTMLBytes.prototype, Symbol.toStringTag, {
+ get() {
+ return "HTMLBytes";
+ }
+});
+class HTMLString extends String {
+ get [Symbol.toStringTag]() {
+ return "HTMLString";
+ }
+}
+const markHTMLString = (value) => {
+ if (value instanceof HTMLString) {
+ return value;
+ }
+ if (typeof value === "string") {
+ return new HTMLString(value);
+ }
+ return value;
+};
+function isHTMLString(value) {
+ return Object.prototype.toString.call(value) === "[object HTMLString]";
+}
+function markHTMLBytes(bytes) {
+ return new HTMLBytes(bytes);
+}
+function hasGetReader(obj) {
+ return typeof obj.getReader === "function";
+}
+async function* unescapeChunksAsync(iterable) {
+ if (hasGetReader(iterable)) {
+ for await (const chunk of streamAsyncIterator(iterable)) {
+ yield unescapeHTML(chunk);
+ }
+ } else {
+ for await (const chunk of iterable) {
+ yield unescapeHTML(chunk);
+ }
+ }
+}
+function* unescapeChunks(iterable) {
+ for (const chunk of iterable) {
+ yield unescapeHTML(chunk);
+ }
+}
+function unescapeHTML(str) {
+ if (!!str && typeof str === "object") {
+ if (str instanceof Uint8Array) {
+ return markHTMLBytes(str);
+ } else if (str instanceof Response && str.body) {
+ const body = str.body;
+ return unescapeChunksAsync(body);
+ } else if (typeof str.then === "function") {
+ return Promise.resolve(str).then((value) => {
+ return unescapeHTML(value);
+ });
+ } else if (str[Symbol.for("astro:slot-string")]) {
+ return str;
+ } else if (Symbol.iterator in str) {
+ return unescapeChunks(str);
+ } else if (Symbol.asyncIterator in str || hasGetReader(str)) {
+ return unescapeChunksAsync(str);
+ }
+ }
+ return markHTMLString(str);
+}
+
+const AstroJSX = "astro:jsx";
+function isVNode(vnode) {
+ return vnode && typeof vnode === "object" && vnode[AstroJSX];
+}
+
+const RenderInstructionSymbol = Symbol.for("astro:render");
+function createRenderInstruction(instruction) {
+ return Object.defineProperty(instruction, RenderInstructionSymbol, {
+ value: true
+ });
+}
+function isRenderInstruction(chunk) {
+ return chunk && typeof chunk === "object" && chunk[RenderInstructionSymbol];
+}
+
+const PROP_TYPE = {
+ Value: 0,
+ JSON: 1,
+ // Actually means Array
+ RegExp: 2,
+ Date: 3,
+ Map: 4,
+ Set: 5,
+ BigInt: 6,
+ URL: 7,
+ Uint8Array: 8,
+ Uint16Array: 9,
+ Uint32Array: 10,
+ Infinity: 11
+};
+function serializeArray(value, metadata = {}, parents = /* @__PURE__ */ new WeakSet()) {
+ if (parents.has(value)) {
+ throw new Error(`Cyclic reference detected while serializing props for <${metadata.displayName} client:${metadata.hydrate}>!
+
+Cyclic references cannot be safely serialized for client-side usage. Please remove the cyclic reference.`);
+ }
+ parents.add(value);
+ const serialized = value.map((v) => {
+ return convertToSerializedForm(v, metadata, parents);
+ });
+ parents.delete(value);
+ return serialized;
+}
+function serializeObject(value, metadata = {}, parents = /* @__PURE__ */ new WeakSet()) {
+ if (parents.has(value)) {
+ throw new Error(`Cyclic reference detected while serializing props for <${metadata.displayName} client:${metadata.hydrate}>!
+
+Cyclic references cannot be safely serialized for client-side usage. Please remove the cyclic reference.`);
+ }
+ parents.add(value);
+ const serialized = Object.fromEntries(
+ Object.entries(value).map(([k, v]) => {
+ return [k, convertToSerializedForm(v, metadata, parents)];
+ })
+ );
+ parents.delete(value);
+ return serialized;
+}
+function convertToSerializedForm(value, metadata = {}, parents = /* @__PURE__ */ new WeakSet()) {
+ const tag = Object.prototype.toString.call(value);
+ switch (tag) {
+ case "[object Date]": {
+ return [PROP_TYPE.Date, value.toISOString()];
+ }
+ case "[object RegExp]": {
+ return [PROP_TYPE.RegExp, value.source];
+ }
+ case "[object Map]": {
+ return [PROP_TYPE.Map, serializeArray(Array.from(value), metadata, parents)];
+ }
+ case "[object Set]": {
+ return [PROP_TYPE.Set, serializeArray(Array.from(value), metadata, parents)];
+ }
+ case "[object BigInt]": {
+ return [PROP_TYPE.BigInt, value.toString()];
+ }
+ case "[object URL]": {
+ return [PROP_TYPE.URL, value.toString()];
+ }
+ case "[object Array]": {
+ return [PROP_TYPE.JSON, serializeArray(value, metadata, parents)];
+ }
+ case "[object Uint8Array]": {
+ return [PROP_TYPE.Uint8Array, Array.from(value)];
+ }
+ case "[object Uint16Array]": {
+ return [PROP_TYPE.Uint16Array, Array.from(value)];
+ }
+ case "[object Uint32Array]": {
+ return [PROP_TYPE.Uint32Array, Array.from(value)];
+ }
+ default: {
+ if (value !== null && typeof value === "object") {
+ return [PROP_TYPE.Value, serializeObject(value, metadata, parents)];
+ }
+ if (value === Infinity) {
+ return [PROP_TYPE.Infinity, 1];
+ }
+ if (value === -Infinity) {
+ return [PROP_TYPE.Infinity, -1];
+ }
+ if (value === void 0) {
+ return [PROP_TYPE.Value];
+ }
+ return [PROP_TYPE.Value, value];
+ }
+ }
+}
+function serializeProps(props, metadata) {
+ const serialized = JSON.stringify(serializeObject(props, metadata));
+ return serialized;
+}
+
+const transitionDirectivesToCopyOnIsland = Object.freeze([
+ "data-astro-transition-scope",
+ "data-astro-transition-persist",
+ "data-astro-transition-persist-props"
+]);
+function extractDirectives(inputProps, clientDirectives) {
+ let extracted = {
+ isPage: false,
+ hydration: null,
+ props: {},
+ propsWithoutTransitionAttributes: {}
+ };
+ for (const [key, value] of Object.entries(inputProps)) {
+ if (key.startsWith("server:")) {
+ if (key === "server:root") {
+ extracted.isPage = true;
+ }
+ }
+ if (key.startsWith("client:")) {
+ if (!extracted.hydration) {
+ extracted.hydration = {
+ directive: "",
+ value: "",
+ componentUrl: "",
+ componentExport: { value: "" }
+ };
+ }
+ switch (key) {
+ case "client:component-path": {
+ extracted.hydration.componentUrl = value;
+ break;
+ }
+ case "client:component-export": {
+ extracted.hydration.componentExport.value = value;
+ break;
+ }
+ case "client:component-hydration": {
+ break;
+ }
+ case "client:display-name": {
+ break;
+ }
+ default: {
+ extracted.hydration.directive = key.split(":")[1];
+ extracted.hydration.value = value;
+ if (!clientDirectives.has(extracted.hydration.directive)) {
+ const hydrationMethods = Array.from(clientDirectives.keys()).map((d) => `client:${d}`).join(", ");
+ throw new Error(
+ `Error: invalid hydration directive "${key}". Supported hydration methods: ${hydrationMethods}`
+ );
+ }
+ if (extracted.hydration.directive === "media" && typeof extracted.hydration.value !== "string") {
+ throw new AstroError(MissingMediaQueryDirective);
+ }
+ break;
+ }
+ }
+ } else {
+ extracted.props[key] = value;
+ if (!transitionDirectivesToCopyOnIsland.includes(key)) {
+ extracted.propsWithoutTransitionAttributes[key] = value;
+ }
+ }
+ }
+ for (const sym of Object.getOwnPropertySymbols(inputProps)) {
+ extracted.props[sym] = inputProps[sym];
+ extracted.propsWithoutTransitionAttributes[sym] = inputProps[sym];
+ }
+ return extracted;
+}
+async function generateHydrateScript(scriptOptions, metadata) {
+ const { renderer, result, astroId, props, attrs } = scriptOptions;
+ const { hydrate, componentUrl, componentExport } = metadata;
+ if (!componentExport.value) {
+ throw new AstroError({
+ ...NoMatchingImport,
+ message: NoMatchingImport.message(metadata.displayName)
+ });
+ }
+ const island = {
+ children: "",
+ props: {
+ // This is for HMR, probably can avoid it in prod
+ uid: astroId
+ }
+ };
+ if (attrs) {
+ for (const [key, value] of Object.entries(attrs)) {
+ island.props[key] = escapeHTML(value);
+ }
+ }
+ island.props["component-url"] = await result.resolve(decodeURI(componentUrl));
+ if (renderer.clientEntrypoint) {
+ island.props["component-export"] = componentExport.value;
+ island.props["renderer-url"] = await result.resolve(decodeURI(renderer.clientEntrypoint));
+ island.props["props"] = escapeHTML(serializeProps(props, metadata));
+ }
+ island.props["ssr"] = "";
+ island.props["client"] = hydrate;
+ let beforeHydrationUrl = await result.resolve("astro:scripts/before-hydration.js");
+ if (beforeHydrationUrl.length) {
+ island.props["before-hydration-url"] = beforeHydrationUrl;
+ }
+ island.props["opts"] = escapeHTML(
+ JSON.stringify({
+ name: metadata.displayName,
+ value: metadata.hydrateArgs || ""
+ })
+ );
+ transitionDirectivesToCopyOnIsland.forEach((name) => {
+ if (typeof props[name] !== "undefined") {
+ island.props[name] = props[name];
+ }
+ });
+ return island;
+}
+
+/**
+ * shortdash - https://github.com/bibig/node-shorthash
+ *
+ * @license
+ *
+ * (The MIT License)
+ *
+ * Copyright (c) 2013 Bibig
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+const dictionary = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXY";
+const binary = dictionary.length;
+function bitwise(str) {
+ let hash = 0;
+ if (str.length === 0) return hash;
+ for (let i = 0; i < str.length; i++) {
+ const ch = str.charCodeAt(i);
+ hash = (hash << 5) - hash + ch;
+ hash = hash & hash;
+ }
+ return hash;
+}
+function shorthash(text) {
+ let num;
+ let result = "";
+ let integer = bitwise(text);
+ const sign = integer < 0 ? "Z" : "";
+ integer = Math.abs(integer);
+ while (integer >= binary) {
+ num = integer % binary;
+ integer = Math.floor(integer / binary);
+ result = dictionary[num] + result;
+ }
+ if (integer > 0) {
+ result = dictionary[integer] + result;
+ }
+ return sign + result;
+}
+
+function isAstroComponentFactory(obj) {
+ return obj == null ? false : obj.isAstroComponentFactory === true;
+}
+function isAPropagatingComponent(result, factory) {
+ let hint = factory.propagation || "none";
+ if (factory.moduleId && result.componentMetadata.has(factory.moduleId) && hint === "none") {
+ hint = result.componentMetadata.get(factory.moduleId).propagation;
+ }
+ return hint === "in-tree" || hint === "self";
+}
+
+const headAndContentSym = Symbol.for("astro.headAndContent");
+function isHeadAndContent(obj) {
+ return typeof obj === "object" && obj !== null && !!obj[headAndContentSym];
+}
+
+var astro_island_prebuilt_dev_default = `(()=>{var A=Object.defineProperty;var g=(i,o,a)=>o in i?A(i,o,{enumerable:!0,configurable:!0,writable:!0,value:a}):i[o]=a;var l=(i,o,a)=>g(i,typeof o!="symbol"?o+"":o,a);{let i={0:t=>y(t),1:t=>a(t),2:t=>new RegExp(t),3:t=>new Date(t),4:t=>new Map(a(t)),5:t=>new Set(a(t)),6:t=>BigInt(t),7:t=>new URL(t),8:t=>new Uint8Array(t),9:t=>new Uint16Array(t),10:t=>new Uint32Array(t),11:t=>1/0*t},o=t=>{let[h,e]=t;return h in i?i[h](e):void 0},a=t=>t.map(o),y=t=>typeof t!="object"||t===null?t:Object.fromEntries(Object.entries(t).map(([h,e])=>[h,o(e)]));class f extends HTMLElement{constructor(){super(...arguments);l(this,"Component");l(this,"hydrator");l(this,"hydrate",async()=>{var b;if(!this.hydrator||!this.isConnected)return;let e=(b=this.parentElement)==null?void 0:b.closest("astro-island[ssr]");if(e){e.addEventListener("astro:hydrate",this.hydrate,{once:!0});return}let c=this.querySelectorAll("astro-slot"),n={},p=this.querySelectorAll("template[data-astro-template]");for(let r of p){let s=r.closest(this.tagName);s!=null&&s.isSameNode(this)&&(n[r.getAttribute("data-astro-template")||"default"]=r.innerHTML,r.remove())}for(let r of c){let s=r.closest(this.tagName);s!=null&&s.isSameNode(this)&&(n[r.getAttribute("name")||"default"]=r.innerHTML)}let u;try{u=this.hasAttribute("props")?y(JSON.parse(this.getAttribute("props"))):{}}catch(r){let s=this.getAttribute("component-url")||"",v=this.getAttribute("component-export");throw v&&(s+=\` (export \${v})\`),console.error(\`[hydrate] Error parsing props for component \${s}\`,this.getAttribute("props"),r),r}let d,m=this.hydrator(this);d=performance.now(),await m(this.Component,u,n,{client:this.getAttribute("client")}),d&&this.setAttribute("client-render-time",(performance.now()-d).toString()),this.removeAttribute("ssr"),this.dispatchEvent(new CustomEvent("astro:hydrate"))});l(this,"unmount",()=>{this.isConnected||this.dispatchEvent(new CustomEvent("astro:unmount"))})}disconnectedCallback(){document.removeEventListener("astro:after-swap",this.unmount),document.addEventListener("astro:after-swap",this.unmount,{once:!0})}connectedCallback(){if(!this.hasAttribute("await-children")||document.readyState==="interactive"||document.readyState==="complete")this.childrenConnectedCallback();else{let e=()=>{document.removeEventListener("DOMContentLoaded",e),c.disconnect(),this.childrenConnectedCallback()},c=new MutationObserver(()=>{var n;((n=this.lastChild)==null?void 0:n.nodeType)===Node.COMMENT_NODE&&this.lastChild.nodeValue==="astro:end"&&(this.lastChild.remove(),e())});c.observe(this,{childList:!0}),document.addEventListener("DOMContentLoaded",e)}}async childrenConnectedCallback(){let e=this.getAttribute("before-hydration-url");e&&await import(e),this.start()}async start(){let e=JSON.parse(this.getAttribute("opts")),c=this.getAttribute("client");if(Astro[c]===void 0){window.addEventListener(\`astro:\${c}\`,()=>this.start(),{once:!0});return}try{await Astro[c](async()=>{let n=this.getAttribute("renderer-url"),[p,{default:u}]=await Promise.all([import(this.getAttribute("component-url")),n?import(n):()=>()=>{}]),d=this.getAttribute("component-export")||"default";if(!d.includes("."))this.Component=p[d];else{this.Component=p;for(let m of d.split("."))this.Component=this.Component[m]}return this.hydrator=u,this.hydrate},e,this)}catch(n){console.error(\`[astro-island] Error hydrating \${this.getAttribute("component-url")}\`,n)}}attributeChangedCallback(){this.hydrate()}}l(f,"observedAttributes",["props"]),customElements.get("astro-island")||customElements.define("astro-island",f)}})();`;
+
+var astro_island_prebuilt_default = `(()=>{var A=Object.defineProperty;var g=(i,o,a)=>o in i?A(i,o,{enumerable:!0,configurable:!0,writable:!0,value:a}):i[o]=a;var d=(i,o,a)=>g(i,typeof o!="symbol"?o+"":o,a);{let i={0:t=>m(t),1:t=>a(t),2:t=>new RegExp(t),3:t=>new Date(t),4:t=>new Map(a(t)),5:t=>new Set(a(t)),6:t=>BigInt(t),7:t=>new URL(t),8:t=>new Uint8Array(t),9:t=>new Uint16Array(t),10:t=>new Uint32Array(t),11:t=>1/0*t},o=t=>{let[l,e]=t;return l in i?i[l](e):void 0},a=t=>t.map(o),m=t=>typeof t!="object"||t===null?t:Object.fromEntries(Object.entries(t).map(([l,e])=>[l,o(e)]));class y extends HTMLElement{constructor(){super(...arguments);d(this,"Component");d(this,"hydrator");d(this,"hydrate",async()=>{var b;if(!this.hydrator||!this.isConnected)return;let e=(b=this.parentElement)==null?void 0:b.closest("astro-island[ssr]");if(e){e.addEventListener("astro:hydrate",this.hydrate,{once:!0});return}let c=this.querySelectorAll("astro-slot"),n={},h=this.querySelectorAll("template[data-astro-template]");for(let r of h){let s=r.closest(this.tagName);s!=null&&s.isSameNode(this)&&(n[r.getAttribute("data-astro-template")||"default"]=r.innerHTML,r.remove())}for(let r of c){let s=r.closest(this.tagName);s!=null&&s.isSameNode(this)&&(n[r.getAttribute("name")||"default"]=r.innerHTML)}let p;try{p=this.hasAttribute("props")?m(JSON.parse(this.getAttribute("props"))):{}}catch(r){let s=this.getAttribute("component-url")||"",v=this.getAttribute("component-export");throw v&&(s+=\` (export \${v})\`),console.error(\`[hydrate] Error parsing props for component \${s}\`,this.getAttribute("props"),r),r}let u;await this.hydrator(this)(this.Component,p,n,{client:this.getAttribute("client")}),this.removeAttribute("ssr"),this.dispatchEvent(new CustomEvent("astro:hydrate"))});d(this,"unmount",()=>{this.isConnected||this.dispatchEvent(new CustomEvent("astro:unmount"))})}disconnectedCallback(){document.removeEventListener("astro:after-swap",this.unmount),document.addEventListener("astro:after-swap",this.unmount,{once:!0})}connectedCallback(){if(!this.hasAttribute("await-children")||document.readyState==="interactive"||document.readyState==="complete")this.childrenConnectedCallback();else{let e=()=>{document.removeEventListener("DOMContentLoaded",e),c.disconnect(),this.childrenConnectedCallback()},c=new MutationObserver(()=>{var n;((n=this.lastChild)==null?void 0:n.nodeType)===Node.COMMENT_NODE&&this.lastChild.nodeValue==="astro:end"&&(this.lastChild.remove(),e())});c.observe(this,{childList:!0}),document.addEventListener("DOMContentLoaded",e)}}async childrenConnectedCallback(){let e=this.getAttribute("before-hydration-url");e&&await import(e),this.start()}async start(){let e=JSON.parse(this.getAttribute("opts")),c=this.getAttribute("client");if(Astro[c]===void 0){window.addEventListener(\`astro:\${c}\`,()=>this.start(),{once:!0});return}try{await Astro[c](async()=>{let n=this.getAttribute("renderer-url"),[h,{default:p}]=await Promise.all([import(this.getAttribute("component-url")),n?import(n):()=>()=>{}]),u=this.getAttribute("component-export")||"default";if(!u.includes("."))this.Component=h[u];else{this.Component=h;for(let f of u.split("."))this.Component=this.Component[f]}return this.hydrator=p,this.hydrate},e,this)}catch(n){console.error(\`[astro-island] Error hydrating \${this.getAttribute("component-url")}\`,n)}}attributeChangedCallback(){this.hydrate()}}d(y,"observedAttributes",["props"]),customElements.get("astro-island")||customElements.define("astro-island",y)}})();`;
+
+const ISLAND_STYLES = ``;
+function determineIfNeedsHydrationScript(result) {
+ if (result._metadata.hasHydrationScript) {
+ return false;
+ }
+ return result._metadata.hasHydrationScript = true;
+}
+function determinesIfNeedsDirectiveScript(result, directive) {
+ if (result._metadata.hasDirectives.has(directive)) {
+ return false;
+ }
+ result._metadata.hasDirectives.add(directive);
+ return true;
+}
+function getDirectiveScriptText(result, directive) {
+ const clientDirectives = result.clientDirectives;
+ const clientDirective = clientDirectives.get(directive);
+ if (!clientDirective) {
+ throw new Error(`Unknown directive: ${directive}`);
+ }
+ return clientDirective;
+}
+function getPrescripts(result, type, directive) {
+ switch (type) {
+ case "both":
+ return `${ISLAND_STYLES}`;
+ case "directive":
+ return ``;
+ }
+ return "";
+}
+
+const voidElementNames = /^(area|base|br|col|command|embed|hr|img|input|keygen|link|meta|param|source|track|wbr)$/i;
+const htmlBooleanAttributes = /^(?:allowfullscreen|async|autofocus|autoplay|checked|controls|default|defer|disabled|disablepictureinpicture|disableremoteplayback|formnovalidate|hidden|loop|nomodule|novalidate|open|playsinline|readonly|required|reversed|scoped|seamless|selected|itemscope)$/i;
+const htmlEnumAttributes = /^(?:contenteditable|draggable|spellcheck|value)$/i;
+const svgEnumAttributes = /^(?:autoReverse|externalResourcesRequired|focusable|preserveAlpha)$/i;
+const AMPERSAND_REGEX = /&/g;
+const DOUBLE_QUOTE_REGEX = /"/g;
+const STATIC_DIRECTIVES = /* @__PURE__ */ new Set(["set:html", "set:text"]);
+const toIdent = (k) => k.trim().replace(/(?!^)\b\w|\s+|\W+/g, (match, index) => {
+ if (/\W/.test(match)) return "";
+ return index === 0 ? match : match.toUpperCase();
+});
+const toAttributeString = (value, shouldEscape = true) => shouldEscape ? String(value).replace(AMPERSAND_REGEX, "&").replace(DOUBLE_QUOTE_REGEX, """) : value;
+const kebab = (k) => k.toLowerCase() === k ? k : k.replace(/[A-Z]/g, (match) => `-${match.toLowerCase()}`);
+const toStyleString = (obj) => Object.entries(obj).filter(([_, v]) => typeof v === "string" && v.trim() || typeof v === "number").map(([k, v]) => {
+ if (k[0] !== "-" && k[1] !== "-") return `${kebab(k)}:${v}`;
+ return `${k}:${v}`;
+}).join(";");
+function defineScriptVars(vars) {
+ let output = "";
+ for (const [key, value] of Object.entries(vars)) {
+ output += `const ${toIdent(key)} = ${JSON.stringify(value)?.replace(
+ /<\/script>/g,
+ "\\x3C/script>"
+ )};
+`;
+ }
+ return markHTMLString(output);
+}
+function formatList(values) {
+ if (values.length === 1) {
+ return values[0];
+ }
+ return `${values.slice(0, -1).join(", ")} or ${values[values.length - 1]}`;
+}
+function addAttribute(value, key, shouldEscape = true) {
+ if (value == null) {
+ return "";
+ }
+ if (value === false) {
+ if (htmlEnumAttributes.test(key) || svgEnumAttributes.test(key)) {
+ return markHTMLString(` ${key}="false"`);
+ }
+ return "";
+ }
+ if (STATIC_DIRECTIVES.has(key)) {
+ console.warn(`[astro] The "${key}" directive cannot be applied dynamically at runtime. It will not be rendered as an attribute.
+
+Make sure to use the static attribute syntax (\`${key}={value}\`) instead of the dynamic spread syntax (\`{...{ "${key}": value }}\`).`);
+ return "";
+ }
+ if (key === "class:list") {
+ const listValue = toAttributeString(clsx(value), shouldEscape);
+ if (listValue === "") {
+ return "";
+ }
+ return markHTMLString(` ${key.slice(0, -5)}="${listValue}"`);
+ }
+ if (key === "style" && !(value instanceof HTMLString)) {
+ if (Array.isArray(value) && value.length === 2) {
+ return markHTMLString(
+ ` ${key}="${toAttributeString(`${toStyleString(value[0])};${value[1]}`, shouldEscape)}"`
+ );
+ }
+ if (typeof value === "object") {
+ return markHTMLString(` ${key}="${toAttributeString(toStyleString(value), shouldEscape)}"`);
+ }
+ }
+ if (key === "className") {
+ return markHTMLString(` class="${toAttributeString(value, shouldEscape)}"`);
+ }
+ if (typeof value === "string" && value.includes("&") && isHttpUrl(value)) {
+ return markHTMLString(` ${key}="${toAttributeString(value, false)}"`);
+ }
+ if (value === true && (key.startsWith("data-") || htmlBooleanAttributes.test(key))) {
+ return markHTMLString(` ${key}`);
+ } else {
+ return markHTMLString(` ${key}="${toAttributeString(value, shouldEscape)}"`);
+ }
+}
+function internalSpreadAttributes(values, shouldEscape = true) {
+ let output = "";
+ for (const [key, value] of Object.entries(values)) {
+ output += addAttribute(value, key, shouldEscape);
+ }
+ return markHTMLString(output);
+}
+function renderElement$1(name, { props: _props, children = "" }, shouldEscape = true) {
+ const { lang: _, "data-astro-id": astroId, "define:vars": defineVars, ...props } = _props;
+ if (defineVars) {
+ if (name === "style") {
+ delete props["is:global"];
+ delete props["is:scoped"];
+ }
+ if (name === "script") {
+ delete props.hoist;
+ children = defineScriptVars(defineVars) + "\n" + children;
+ }
+ }
+ if ((children == null || children == "") && voidElementNames.test(name)) {
+ return `<${name}${internalSpreadAttributes(props, shouldEscape)}>`;
+ }
+ return `<${name}${internalSpreadAttributes(props, shouldEscape)}>${children}${name}>`;
+}
+const noop = () => {
+};
+class BufferedRenderer {
+ chunks = [];
+ renderPromise;
+ destination;
+ constructor(bufferRenderFunction) {
+ this.renderPromise = bufferRenderFunction(this);
+ Promise.resolve(this.renderPromise).catch(noop);
+ }
+ write(chunk) {
+ if (this.destination) {
+ this.destination.write(chunk);
+ } else {
+ this.chunks.push(chunk);
+ }
+ }
+ async renderToFinalDestination(destination) {
+ for (const chunk of this.chunks) {
+ destination.write(chunk);
+ }
+ this.destination = destination;
+ await this.renderPromise;
+ }
+}
+function renderToBufferDestination(bufferRenderFunction) {
+ const renderer = new BufferedRenderer(bufferRenderFunction);
+ return renderer;
+}
+const isNode = typeof process !== "undefined" && Object.prototype.toString.call(process) === "[object process]";
+const isDeno = typeof Deno !== "undefined";
+function promiseWithResolvers() {
+ let resolve, reject;
+ const promise = new Promise((_resolve, _reject) => {
+ resolve = _resolve;
+ reject = _reject;
+ });
+ return {
+ promise,
+ resolve,
+ reject
+ };
+}
+const VALID_PROTOCOLS = ["http:", "https:"];
+function isHttpUrl(url) {
+ try {
+ const parsedUrl = new URL(url);
+ return VALID_PROTOCOLS.includes(parsedUrl.protocol);
+ } catch {
+ return false;
+ }
+}
+
+const uniqueElements = (item, index, all) => {
+ const props = JSON.stringify(item.props);
+ const children = item.children;
+ return index === all.findIndex((i) => JSON.stringify(i.props) === props && i.children == children);
+};
+function renderAllHeadContent(result) {
+ result._metadata.hasRenderedHead = true;
+ const styles = Array.from(result.styles).filter(uniqueElements).map(
+ (style) => style.props.rel === "stylesheet" ? renderElement$1("link", style) : renderElement$1("style", style)
+ );
+ result.styles.clear();
+ const scripts = Array.from(result.scripts).filter(uniqueElements).map((script) => {
+ return renderElement$1("script", script, false);
+ });
+ const links = Array.from(result.links).filter(uniqueElements).map((link) => renderElement$1("link", link, false));
+ let content = styles.join("\n") + links.join("\n") + scripts.join("\n");
+ if (result._metadata.extraHead.length > 0) {
+ for (const part of result._metadata.extraHead) {
+ content += part;
+ }
+ }
+ return markHTMLString(content);
+}
+function renderHead() {
+ return createRenderInstruction({ type: "head" });
+}
+function maybeRenderHead() {
+ return createRenderInstruction({ type: "maybe-head" });
+}
+
+const renderTemplateResultSym = Symbol.for("astro.renderTemplateResult");
+class RenderTemplateResult {
+ [renderTemplateResultSym] = true;
+ htmlParts;
+ expressions;
+ error;
+ constructor(htmlParts, expressions) {
+ this.htmlParts = htmlParts;
+ this.error = void 0;
+ this.expressions = expressions.map((expression) => {
+ if (isPromise(expression)) {
+ return Promise.resolve(expression).catch((err) => {
+ if (!this.error) {
+ this.error = err;
+ throw err;
+ }
+ });
+ }
+ return expression;
+ });
+ }
+ async render(destination) {
+ const expRenders = this.expressions.map((exp) => {
+ return renderToBufferDestination((bufferDestination) => {
+ if (exp || exp === 0) {
+ return renderChild(bufferDestination, exp);
+ }
+ });
+ });
+ for (let i = 0; i < this.htmlParts.length; i++) {
+ const html = this.htmlParts[i];
+ const expRender = expRenders[i];
+ destination.write(markHTMLString(html));
+ if (expRender) {
+ await expRender.renderToFinalDestination(destination);
+ }
+ }
+ }
+}
+function isRenderTemplateResult(obj) {
+ return typeof obj === "object" && obj !== null && !!obj[renderTemplateResultSym];
+}
+function renderTemplate(htmlParts, ...expressions) {
+ return new RenderTemplateResult(htmlParts, expressions);
+}
+
+const slotString = Symbol.for("astro:slot-string");
+class SlotString extends HTMLString {
+ instructions;
+ [slotString];
+ constructor(content, instructions) {
+ super(content);
+ this.instructions = instructions;
+ this[slotString] = true;
+ }
+}
+function isSlotString(str) {
+ return !!str[slotString];
+}
+function renderSlot(result, slotted, fallback) {
+ if (!slotted && fallback) {
+ return renderSlot(result, fallback);
+ }
+ return {
+ async render(destination) {
+ await renderChild(destination, typeof slotted === "function" ? slotted(result) : slotted);
+ }
+ };
+}
+async function renderSlotToString(result, slotted, fallback) {
+ let content = "";
+ let instructions = null;
+ const temporaryDestination = {
+ write(chunk) {
+ if (chunk instanceof SlotString) {
+ content += chunk;
+ if (chunk.instructions) {
+ instructions ??= [];
+ instructions.push(...chunk.instructions);
+ }
+ } else if (chunk instanceof Response) return;
+ else if (typeof chunk === "object" && "type" in chunk && typeof chunk.type === "string") {
+ if (instructions === null) {
+ instructions = [];
+ }
+ instructions.push(chunk);
+ } else {
+ content += chunkToString(result, chunk);
+ }
+ }
+ };
+ const renderInstance = renderSlot(result, slotted, fallback);
+ await renderInstance.render(temporaryDestination);
+ return markHTMLString(new SlotString(content, instructions));
+}
+async function renderSlots(result, slots = {}) {
+ let slotInstructions = null;
+ let children = {};
+ if (slots) {
+ await Promise.all(
+ Object.entries(slots).map(
+ ([key, value]) => renderSlotToString(result, value).then((output) => {
+ if (output.instructions) {
+ if (slotInstructions === null) {
+ slotInstructions = [];
+ }
+ slotInstructions.push(...output.instructions);
+ }
+ children[key] = output;
+ })
+ )
+ );
+ }
+ return { slotInstructions, children };
+}
+function createSlotValueFromString(content) {
+ return function() {
+ return renderTemplate`${unescapeHTML(content)}`;
+ };
+}
+
+const Fragment = Symbol.for("astro:fragment");
+const Renderer = Symbol.for("astro:renderer");
+const encoder$1 = new TextEncoder();
+const decoder$1 = new TextDecoder();
+function stringifyChunk(result, chunk) {
+ if (isRenderInstruction(chunk)) {
+ const instruction = chunk;
+ switch (instruction.type) {
+ case "directive": {
+ const { hydration } = instruction;
+ let needsHydrationScript = hydration && determineIfNeedsHydrationScript(result);
+ let needsDirectiveScript = hydration && determinesIfNeedsDirectiveScript(result, hydration.directive);
+ let prescriptType = needsHydrationScript ? "both" : needsDirectiveScript ? "directive" : null;
+ if (prescriptType) {
+ let prescripts = getPrescripts(result, prescriptType, hydration.directive);
+ return markHTMLString(prescripts);
+ } else {
+ return "";
+ }
+ }
+ case "head": {
+ if (result._metadata.hasRenderedHead || result.partial) {
+ return "";
+ }
+ return renderAllHeadContent(result);
+ }
+ case "maybe-head": {
+ if (result._metadata.hasRenderedHead || result._metadata.headInTree || result.partial) {
+ return "";
+ }
+ return renderAllHeadContent(result);
+ }
+ case "renderer-hydration-script": {
+ const { rendererSpecificHydrationScripts } = result._metadata;
+ const { rendererName } = instruction;
+ if (!rendererSpecificHydrationScripts.has(rendererName)) {
+ rendererSpecificHydrationScripts.add(rendererName);
+ return instruction.render();
+ }
+ return "";
+ }
+ default: {
+ throw new Error(`Unknown chunk type: ${chunk.type}`);
+ }
+ }
+ } else if (chunk instanceof Response) {
+ return "";
+ } else if (isSlotString(chunk)) {
+ let out = "";
+ const c = chunk;
+ if (c.instructions) {
+ for (const instr of c.instructions) {
+ out += stringifyChunk(result, instr);
+ }
+ }
+ out += chunk.toString();
+ return out;
+ }
+ return chunk.toString();
+}
+function chunkToString(result, chunk) {
+ if (ArrayBuffer.isView(chunk)) {
+ return decoder$1.decode(chunk);
+ } else {
+ return stringifyChunk(result, chunk);
+ }
+}
+function chunkToByteArray(result, chunk) {
+ if (ArrayBuffer.isView(chunk)) {
+ return chunk;
+ } else {
+ const stringified = stringifyChunk(result, chunk);
+ return encoder$1.encode(stringified.toString());
+ }
+}
+function isRenderInstance(obj) {
+ return !!obj && typeof obj === "object" && "render" in obj && typeof obj.render === "function";
+}
+
+async function renderChild(destination, child) {
+ if (isPromise(child)) {
+ child = await child;
+ }
+ if (child instanceof SlotString) {
+ destination.write(child);
+ } else if (isHTMLString(child)) {
+ destination.write(child);
+ } else if (Array.isArray(child)) {
+ const childRenders = child.map((c) => {
+ return renderToBufferDestination((bufferDestination) => {
+ return renderChild(bufferDestination, c);
+ });
+ });
+ for (const childRender of childRenders) {
+ if (!childRender) continue;
+ await childRender.renderToFinalDestination(destination);
+ }
+ } else if (typeof child === "function") {
+ await renderChild(destination, child());
+ } else if (typeof child === "string") {
+ destination.write(markHTMLString(escapeHTML(child)));
+ } else if (!child && child !== 0) ; else if (isRenderInstance(child)) {
+ await child.render(destination);
+ } else if (isRenderTemplateResult(child)) {
+ await child.render(destination);
+ } else if (isAstroComponentInstance(child)) {
+ await child.render(destination);
+ } else if (ArrayBuffer.isView(child)) {
+ destination.write(child);
+ } else if (typeof child === "object" && (Symbol.asyncIterator in child || Symbol.iterator in child)) {
+ for await (const value of child) {
+ await renderChild(destination, value);
+ }
+ } else {
+ destination.write(child);
+ }
+}
+
+const astroComponentInstanceSym = Symbol.for("astro.componentInstance");
+class AstroComponentInstance {
+ [astroComponentInstanceSym] = true;
+ result;
+ props;
+ slotValues;
+ factory;
+ returnValue;
+ constructor(result, props, slots, factory) {
+ this.result = result;
+ this.props = props;
+ this.factory = factory;
+ this.slotValues = {};
+ for (const name in slots) {
+ let didRender = false;
+ let value = slots[name](result);
+ this.slotValues[name] = () => {
+ if (!didRender) {
+ didRender = true;
+ return value;
+ }
+ return slots[name](result);
+ };
+ }
+ }
+ async init(result) {
+ if (this.returnValue !== void 0) return this.returnValue;
+ this.returnValue = this.factory(result, this.props, this.slotValues);
+ if (isPromise(this.returnValue)) {
+ this.returnValue.then((resolved) => {
+ this.returnValue = resolved;
+ }).catch(() => {
+ });
+ }
+ return this.returnValue;
+ }
+ async render(destination) {
+ const returnValue = await this.init(this.result);
+ if (isHeadAndContent(returnValue)) {
+ await returnValue.content.render(destination);
+ } else {
+ await renderChild(destination, returnValue);
+ }
+ }
+}
+function validateComponentProps(props, displayName) {
+ if (props != null) {
+ for (const prop of Object.keys(props)) {
+ if (prop.startsWith("client:")) {
+ console.warn(
+ `You are attempting to render <${displayName} ${prop} />, but ${displayName} is an Astro component. Astro components do not render in the client and should not have a hydration directive. Please use a framework component for client rendering.`
+ );
+ }
+ }
+ }
+}
+function createAstroComponentInstance(result, displayName, factory, props, slots = {}) {
+ validateComponentProps(props, displayName);
+ const instance = new AstroComponentInstance(result, props, slots, factory);
+ if (isAPropagatingComponent(result, factory)) {
+ result._metadata.propagators.add(instance);
+ }
+ return instance;
+}
+function isAstroComponentInstance(obj) {
+ return typeof obj === "object" && obj !== null && !!obj[astroComponentInstanceSym];
+}
+
+const DOCTYPE_EXP = /" : "\n";
+ str += doctype;
+ }
+ }
+ if (chunk instanceof Response) return;
+ str += chunkToString(result, chunk);
+ }
+ };
+ await templateResult.render(destination);
+ return str;
+}
+async function renderToReadableStream(result, componentFactory, props, children, isPage = false, route) {
+ const templateResult = await callComponentAsTemplateResultOrResponse(
+ result,
+ componentFactory,
+ props,
+ children,
+ route
+ );
+ if (templateResult instanceof Response) return templateResult;
+ let renderedFirstPageChunk = false;
+ if (isPage) {
+ await bufferHeadContent(result);
+ }
+ return new ReadableStream({
+ start(controller) {
+ const destination = {
+ write(chunk) {
+ if (isPage && !renderedFirstPageChunk) {
+ renderedFirstPageChunk = true;
+ if (!result.partial && !DOCTYPE_EXP.test(String(chunk))) {
+ const doctype = result.compressHTML ? "" : "\n";
+ controller.enqueue(encoder$1.encode(doctype));
+ }
+ }
+ if (chunk instanceof Response) {
+ throw new AstroError({
+ ...ResponseSentError
+ });
+ }
+ const bytes = chunkToByteArray(result, chunk);
+ controller.enqueue(bytes);
+ }
+ };
+ (async () => {
+ try {
+ await templateResult.render(destination);
+ controller.close();
+ } catch (e) {
+ if (AstroError.is(e) && !e.loc) {
+ e.setLocation({
+ file: route?.component
+ });
+ }
+ setTimeout(() => controller.error(e), 0);
+ }
+ })();
+ },
+ cancel() {
+ result.cancelled = true;
+ }
+ });
+}
+async function callComponentAsTemplateResultOrResponse(result, componentFactory, props, children, route) {
+ const factoryResult = await componentFactory(result, props, children);
+ if (factoryResult instanceof Response) {
+ return factoryResult;
+ } else if (isHeadAndContent(factoryResult)) {
+ if (!isRenderTemplateResult(factoryResult.content)) {
+ throw new AstroError({
+ ...OnlyResponseCanBeReturned,
+ message: OnlyResponseCanBeReturned.message(
+ route?.route,
+ typeof factoryResult
+ ),
+ location: {
+ file: route?.component
+ }
+ });
+ }
+ return factoryResult.content;
+ } else if (!isRenderTemplateResult(factoryResult)) {
+ throw new AstroError({
+ ...OnlyResponseCanBeReturned,
+ message: OnlyResponseCanBeReturned.message(route?.route, typeof factoryResult),
+ location: {
+ file: route?.component
+ }
+ });
+ }
+ return factoryResult;
+}
+async function bufferHeadContent(result) {
+ const iterator = result._metadata.propagators.values();
+ while (true) {
+ const { value, done } = iterator.next();
+ if (done) {
+ break;
+ }
+ const returnValue = await value.init(result);
+ if (isHeadAndContent(returnValue)) {
+ result._metadata.extraHead.push(returnValue.head);
+ }
+ }
+}
+async function renderToAsyncIterable(result, componentFactory, props, children, isPage = false, route) {
+ const templateResult = await callComponentAsTemplateResultOrResponse(
+ result,
+ componentFactory,
+ props,
+ children,
+ route
+ );
+ if (templateResult instanceof Response) return templateResult;
+ let renderedFirstPageChunk = false;
+ if (isPage) {
+ await bufferHeadContent(result);
+ }
+ let error = null;
+ let next = null;
+ const buffer = [];
+ let renderingComplete = false;
+ const iterator = {
+ async next() {
+ if (result.cancelled) return { done: true, value: void 0 };
+ if (next !== null) {
+ await next.promise;
+ } else if (!renderingComplete && !buffer.length) {
+ next = promiseWithResolvers();
+ await next.promise;
+ }
+ if (!renderingComplete) {
+ next = promiseWithResolvers();
+ }
+ if (error) {
+ throw error;
+ }
+ let length = 0;
+ for (let i = 0, len = buffer.length; i < len; i++) {
+ length += buffer[i].length;
+ }
+ let mergedArray = new Uint8Array(length);
+ let offset = 0;
+ for (let i = 0, len = buffer.length; i < len; i++) {
+ const item = buffer[i];
+ mergedArray.set(item, offset);
+ offset += item.length;
+ }
+ buffer.length = 0;
+ const returnValue = {
+ // The iterator is done when rendering has finished
+ // and there are no more chunks to return.
+ done: length === 0 && renderingComplete,
+ value: mergedArray
+ };
+ return returnValue;
+ },
+ async return() {
+ result.cancelled = true;
+ return { done: true, value: void 0 };
+ }
+ };
+ const destination = {
+ write(chunk) {
+ if (isPage && !renderedFirstPageChunk) {
+ renderedFirstPageChunk = true;
+ if (!result.partial && !DOCTYPE_EXP.test(String(chunk))) {
+ const doctype = result.compressHTML ? "" : "\n";
+ buffer.push(encoder$1.encode(doctype));
+ }
+ }
+ if (chunk instanceof Response) {
+ throw new AstroError(ResponseSentError);
+ }
+ const bytes = chunkToByteArray(result, chunk);
+ if (bytes.length > 0) {
+ buffer.push(bytes);
+ next?.resolve();
+ } else if (buffer.length > 0) {
+ next?.resolve();
+ }
+ }
+ };
+ const renderPromise = templateResult.render(destination);
+ renderPromise.then(() => {
+ renderingComplete = true;
+ next?.resolve();
+ }).catch((err) => {
+ error = err;
+ renderingComplete = true;
+ next?.resolve();
+ });
+ return {
+ [Symbol.asyncIterator]() {
+ return iterator;
+ }
+ };
+}
+
+function componentIsHTMLElement(Component) {
+ return typeof HTMLElement !== "undefined" && HTMLElement.isPrototypeOf(Component);
+}
+async function renderHTMLElement(result, constructor, props, slots) {
+ const name = getHTMLElementName(constructor);
+ let attrHTML = "";
+ for (const attr in props) {
+ attrHTML += ` ${attr}="${toAttributeString(await props[attr])}"`;
+ }
+ return markHTMLString(
+ `<${name}${attrHTML}>${await renderSlotToString(result, slots?.default)}${name}>`
+ );
+}
+function getHTMLElementName(constructor) {
+ const definedName = customElements.getName(constructor);
+ if (definedName) return definedName;
+ const assignedName = constructor.name.replace(/^HTML|Element$/g, "").replace(/[A-Z]/g, "-$&").toLowerCase().replace(/^-/, "html-");
+ return assignedName;
+}
+
+const ALGORITHM = "AES-GCM";
+async function decodeKey(encoded) {
+ const bytes = decodeBase64(encoded);
+ return crypto.subtle.importKey("raw", bytes, ALGORITHM, true, ["encrypt", "decrypt"]);
+}
+const encoder = new TextEncoder();
+const decoder = new TextDecoder();
+const IV_LENGTH = 24;
+async function encryptString(key, raw) {
+ const iv = crypto.getRandomValues(new Uint8Array(IV_LENGTH / 2));
+ const data = encoder.encode(raw);
+ const buffer = await crypto.subtle.encrypt(
+ {
+ name: ALGORITHM,
+ iv
+ },
+ key,
+ data
+ );
+ return encodeHexUpperCase(iv) + encodeBase64(new Uint8Array(buffer));
+}
+async function decryptString(key, encoded) {
+ const iv = decodeHex(encoded.slice(0, IV_LENGTH));
+ const dataArray = decodeBase64(encoded.slice(IV_LENGTH));
+ const decryptedBuffer = await crypto.subtle.decrypt(
+ {
+ name: ALGORITHM,
+ iv
+ },
+ key,
+ dataArray
+ );
+ const decryptedString = decoder.decode(decryptedBuffer);
+ return decryptedString;
+}
+
+const internalProps = /* @__PURE__ */ new Set([
+ "server:component-path",
+ "server:component-export",
+ "server:component-directive",
+ "server:defer"
+]);
+function containsServerDirective(props) {
+ return "server:component-directive" in props;
+}
+function safeJsonStringify(obj) {
+ return JSON.stringify(obj).replace(/\u2028/g, "\\u2028").replace(/\u2029/g, "\\u2029").replace(//g, "\\u003e").replace(/\//g, "\\u002f");
+}
+function renderServerIsland(result, _displayName, props, slots) {
+ return {
+ async render(destination) {
+ const componentPath = props["server:component-path"];
+ const componentExport = props["server:component-export"];
+ const componentId = result.serverIslandNameMap.get(componentPath);
+ if (!componentId) {
+ throw new Error(`Could not find server component name`);
+ }
+ for (const key2 of Object.keys(props)) {
+ if (internalProps.has(key2)) {
+ delete props[key2];
+ }
+ }
+ destination.write("");
+ const renderedSlots = {};
+ for (const name in slots) {
+ if (name !== "fallback") {
+ const content = await renderSlotToString(result, slots[name]);
+ renderedSlots[name] = content.toString();
+ } else {
+ await renderChild(destination, slots.fallback(result));
+ }
+ }
+ const key = await result.key;
+ const propsEncrypted = await encryptString(key, JSON.stringify(props));
+ const hostId = crypto.randomUUID();
+ const slash = result.base.endsWith("/") ? "" : "/";
+ const serverIslandUrl = `${result.base}${slash}_server-islands/${componentId}${result.trailingSlash === "always" ? "/" : ""}`;
+ destination.write(``);
+ }
+ };
+}
+
+const needsHeadRenderingSymbol = Symbol.for("astro.needsHeadRendering");
+const rendererAliases = /* @__PURE__ */ new Map([["solid", "solid-js"]]);
+const clientOnlyValues = /* @__PURE__ */ new Set(["solid-js", "react", "preact", "vue", "svelte", "lit"]);
+function guessRenderers(componentUrl) {
+ const extname = componentUrl?.split(".").pop();
+ switch (extname) {
+ case "svelte":
+ return ["@astrojs/svelte"];
+ case "vue":
+ return ["@astrojs/vue"];
+ case "jsx":
+ case "tsx":
+ return ["@astrojs/react", "@astrojs/preact", "@astrojs/solid-js", "@astrojs/vue (jsx)"];
+ case void 0:
+ default:
+ return [
+ "@astrojs/react",
+ "@astrojs/preact",
+ "@astrojs/solid-js",
+ "@astrojs/vue",
+ "@astrojs/svelte",
+ "@astrojs/lit"
+ ];
+ }
+}
+function isFragmentComponent(Component) {
+ return Component === Fragment;
+}
+function isHTMLComponent(Component) {
+ return Component && Component["astro:html"] === true;
+}
+const ASTRO_SLOT_EXP = /<\/?astro-slot\b[^>]*>/g;
+const ASTRO_STATIC_SLOT_EXP = /<\/?astro-static-slot\b[^>]*>/g;
+function removeStaticAstroSlot(html, supportsAstroStaticSlot = true) {
+ const exp = supportsAstroStaticSlot ? ASTRO_STATIC_SLOT_EXP : ASTRO_SLOT_EXP;
+ return html.replace(exp, "");
+}
+async function renderFrameworkComponent(result, displayName, Component, _props, slots = {}) {
+ if (!Component && "client:only" in _props === false) {
+ throw new Error(
+ `Unable to render ${displayName} because it is ${Component}!
+Did you forget to import the component or is it possible there is a typo?`
+ );
+ }
+ const { renderers, clientDirectives } = result;
+ const metadata = {
+ astroStaticSlot: true,
+ displayName
+ };
+ const { hydration, isPage, props, propsWithoutTransitionAttributes } = extractDirectives(
+ _props,
+ clientDirectives
+ );
+ let html = "";
+ let attrs = void 0;
+ if (hydration) {
+ metadata.hydrate = hydration.directive;
+ metadata.hydrateArgs = hydration.value;
+ metadata.componentExport = hydration.componentExport;
+ metadata.componentUrl = hydration.componentUrl;
+ }
+ const probableRendererNames = guessRenderers(metadata.componentUrl);
+ const validRenderers = renderers.filter((r) => r.name !== "astro:jsx");
+ const { children, slotInstructions } = await renderSlots(result, slots);
+ let renderer;
+ if (metadata.hydrate !== "only") {
+ let isTagged = false;
+ try {
+ isTagged = Component && Component[Renderer];
+ } catch {
+ }
+ if (isTagged) {
+ const rendererName = Component[Renderer];
+ renderer = renderers.find(({ name }) => name === rendererName);
+ }
+ if (!renderer) {
+ let error;
+ for (const r of renderers) {
+ try {
+ if (await r.ssr.check.call({ result }, Component, props, children)) {
+ renderer = r;
+ break;
+ }
+ } catch (e) {
+ error ??= e;
+ }
+ }
+ if (!renderer && error) {
+ throw error;
+ }
+ }
+ if (!renderer && typeof HTMLElement === "function" && componentIsHTMLElement(Component)) {
+ const output = await renderHTMLElement(
+ result,
+ Component,
+ _props,
+ slots
+ );
+ return {
+ render(destination) {
+ destination.write(output);
+ }
+ };
+ }
+ } else {
+ if (metadata.hydrateArgs) {
+ const rendererName = rendererAliases.has(metadata.hydrateArgs) ? rendererAliases.get(metadata.hydrateArgs) : metadata.hydrateArgs;
+ if (clientOnlyValues.has(rendererName)) {
+ renderer = renderers.find(
+ ({ name }) => name === `@astrojs/${rendererName}` || name === rendererName
+ );
+ }
+ }
+ if (!renderer && validRenderers.length === 1) {
+ renderer = validRenderers[0];
+ }
+ if (!renderer) {
+ const extname = metadata.componentUrl?.split(".").pop();
+ renderer = renderers.find(({ name }) => name === `@astrojs/${extname}` || name === extname);
+ }
+ }
+ let componentServerRenderEndTime;
+ if (!renderer) {
+ if (metadata.hydrate === "only") {
+ const rendererName = rendererAliases.has(metadata.hydrateArgs) ? rendererAliases.get(metadata.hydrateArgs) : metadata.hydrateArgs;
+ if (clientOnlyValues.has(rendererName)) {
+ const plural = validRenderers.length > 1;
+ throw new AstroError({
+ ...NoMatchingRenderer,
+ message: NoMatchingRenderer.message(
+ metadata.displayName,
+ metadata?.componentUrl?.split(".").pop(),
+ plural,
+ validRenderers.length
+ ),
+ hint: NoMatchingRenderer.hint(
+ formatList(probableRendererNames.map((r) => "`" + r + "`"))
+ )
+ });
+ } else {
+ throw new AstroError({
+ ...NoClientOnlyHint,
+ message: NoClientOnlyHint.message(metadata.displayName),
+ hint: NoClientOnlyHint.hint(
+ probableRendererNames.map((r) => r.replace("@astrojs/", "")).join("|")
+ )
+ });
+ }
+ } else if (typeof Component !== "string") {
+ const matchingRenderers = validRenderers.filter(
+ (r) => probableRendererNames.includes(r.name)
+ );
+ const plural = validRenderers.length > 1;
+ if (matchingRenderers.length === 0) {
+ throw new AstroError({
+ ...NoMatchingRenderer,
+ message: NoMatchingRenderer.message(
+ metadata.displayName,
+ metadata?.componentUrl?.split(".").pop(),
+ plural,
+ validRenderers.length
+ ),
+ hint: NoMatchingRenderer.hint(
+ formatList(probableRendererNames.map((r) => "`" + r + "`"))
+ )
+ });
+ } else if (matchingRenderers.length === 1) {
+ renderer = matchingRenderers[0];
+ ({ html, attrs } = await renderer.ssr.renderToStaticMarkup.call(
+ { result },
+ Component,
+ propsWithoutTransitionAttributes,
+ children,
+ metadata
+ ));
+ } else {
+ throw new Error(`Unable to render ${metadata.displayName}!
+
+This component likely uses ${formatList(probableRendererNames)},
+but Astro encountered an error during server-side rendering.
+
+Please ensure that ${metadata.displayName}:
+1. Does not unconditionally access browser-specific globals like \`window\` or \`document\`.
+ If this is unavoidable, use the \`client:only\` hydration directive.
+2. Does not conditionally return \`null\` or \`undefined\` when rendered on the server.
+
+If you're still stuck, please open an issue on GitHub or join us at https://astro.build/chat.`);
+ }
+ }
+ } else {
+ if (metadata.hydrate === "only") {
+ const rendererName = rendererAliases.has(metadata.hydrateArgs) ? rendererAliases.get(metadata.hydrateArgs) : metadata.hydrateArgs;
+ if (!clientOnlyValues.has(rendererName)) {
+ console.warn(
+ `The client:only directive for ${metadata.displayName} is not recognized. The renderer ${renderer.name} will be used. If you intended to use a different renderer, please provide a valid client:only directive.`
+ );
+ }
+ html = await renderSlotToString(result, slots?.fallback);
+ } else {
+ const componentRenderStartTime = performance.now();
+ ({ html, attrs } = await renderer.ssr.renderToStaticMarkup.call(
+ { result },
+ Component,
+ propsWithoutTransitionAttributes,
+ children,
+ metadata
+ ));
+ if (process.env.NODE_ENV === "development")
+ componentServerRenderEndTime = performance.now() - componentRenderStartTime;
+ }
+ }
+ if (renderer && !renderer.clientEntrypoint && renderer.name !== "@astrojs/lit" && metadata.hydrate) {
+ throw new AstroError({
+ ...NoClientEntrypoint,
+ message: NoClientEntrypoint.message(
+ displayName,
+ metadata.hydrate,
+ renderer.name
+ )
+ });
+ }
+ if (!html && typeof Component === "string") {
+ const Tag = sanitizeElementName(Component);
+ const childSlots = Object.values(children).join("");
+ const renderTemplateResult = renderTemplate`<${Tag}${internalSpreadAttributes(
+ props
+ )}${markHTMLString(
+ childSlots === "" && voidElementNames.test(Tag) ? `/>` : `>${childSlots}${Tag}>`
+ )}`;
+ html = "";
+ const destination = {
+ write(chunk) {
+ if (chunk instanceof Response) return;
+ html += chunkToString(result, chunk);
+ }
+ };
+ await renderTemplateResult.render(destination);
+ }
+ if (!hydration) {
+ return {
+ render(destination) {
+ if (slotInstructions) {
+ for (const instruction of slotInstructions) {
+ destination.write(instruction);
+ }
+ }
+ if (isPage || renderer?.name === "astro:jsx") {
+ destination.write(html);
+ } else if (html && html.length > 0) {
+ destination.write(
+ markHTMLString(removeStaticAstroSlot(html, renderer?.ssr?.supportsAstroStaticSlot))
+ );
+ }
+ }
+ };
+ }
+ const astroId = shorthash(
+ `
+${html}
+${serializeProps(
+ props,
+ metadata
+ )}`
+ );
+ const island = await generateHydrateScript(
+ { renderer, result, astroId, props, attrs },
+ metadata
+ );
+ if (componentServerRenderEndTime && process.env.NODE_ENV === "development")
+ island.props["server-render-time"] = componentServerRenderEndTime;
+ let unrenderedSlots = [];
+ if (html) {
+ if (Object.keys(children).length > 0) {
+ for (const key of Object.keys(children)) {
+ let tagName = renderer?.ssr?.supportsAstroStaticSlot ? !!metadata.hydrate ? "astro-slot" : "astro-static-slot" : "astro-slot";
+ let expectedHTML = key === "default" ? `<${tagName}>` : `<${tagName} name="${key}">`;
+ if (!html.includes(expectedHTML)) {
+ unrenderedSlots.push(key);
+ }
+ }
+ }
+ } else {
+ unrenderedSlots = Object.keys(children);
+ }
+ const template = unrenderedSlots.length > 0 ? unrenderedSlots.map(
+ (key) => `${children[key]}`
+ ).join("") : "";
+ island.children = `${html ?? ""}${template}`;
+ if (island.children) {
+ island.props["await-children"] = "";
+ island.children += ``;
+ }
+ return {
+ render(destination) {
+ if (slotInstructions) {
+ for (const instruction of slotInstructions) {
+ destination.write(instruction);
+ }
+ }
+ destination.write(createRenderInstruction({ type: "directive", hydration }));
+ if (hydration.directive !== "only" && renderer?.ssr.renderHydrationScript) {
+ destination.write(
+ createRenderInstruction({
+ type: "renderer-hydration-script",
+ rendererName: renderer.name,
+ render: renderer.ssr.renderHydrationScript
+ })
+ );
+ }
+ const renderedElement = renderElement$1("astro-island", island, false);
+ destination.write(markHTMLString(renderedElement));
+ }
+ };
+}
+function sanitizeElementName(tag) {
+ const unsafe = /[&<>'"\s]+/;
+ if (!unsafe.test(tag)) return tag;
+ return tag.trim().split(unsafe)[0].trim();
+}
+async function renderFragmentComponent(result, slots = {}) {
+ const children = await renderSlotToString(result, slots?.default);
+ return {
+ render(destination) {
+ if (children == null) return;
+ destination.write(children);
+ }
+ };
+}
+async function renderHTMLComponent(result, Component, _props, slots = {}) {
+ const { slotInstructions, children } = await renderSlots(result, slots);
+ const html = Component({ slots: children });
+ const hydrationHtml = slotInstructions ? slotInstructions.map((instr) => chunkToString(result, instr)).join("") : "";
+ return {
+ render(destination) {
+ destination.write(markHTMLString(hydrationHtml + html));
+ }
+ };
+}
+function renderAstroComponent(result, displayName, Component, props, slots = {}) {
+ if (containsServerDirective(props)) {
+ return renderServerIsland(result, displayName, props, slots);
+ }
+ const instance = createAstroComponentInstance(result, displayName, Component, props, slots);
+ return {
+ async render(destination) {
+ await instance.render(destination);
+ }
+ };
+}
+async function renderComponent(result, displayName, Component, props, slots = {}) {
+ if (isPromise(Component)) {
+ Component = await Component.catch(handleCancellation);
+ }
+ if (isFragmentComponent(Component)) {
+ return await renderFragmentComponent(result, slots).catch(handleCancellation);
+ }
+ props = normalizeProps(props);
+ if (isHTMLComponent(Component)) {
+ return await renderHTMLComponent(result, Component, props, slots).catch(handleCancellation);
+ }
+ if (isAstroComponentFactory(Component)) {
+ return renderAstroComponent(result, displayName, Component, props, slots);
+ }
+ return await renderFrameworkComponent(result, displayName, Component, props, slots).catch(
+ handleCancellation
+ );
+ function handleCancellation(e) {
+ if (result.cancelled)
+ return {
+ render() {
+ }
+ };
+ throw e;
+ }
+}
+function normalizeProps(props) {
+ if (props["class:list"] !== void 0) {
+ const value = props["class:list"];
+ delete props["class:list"];
+ props["class"] = clsx(props["class"], value);
+ if (props["class"] === "") {
+ delete props["class"];
+ }
+ }
+ return props;
+}
+async function renderComponentToString(result, displayName, Component, props, slots = {}, isPage = false, route) {
+ let str = "";
+ let renderedFirstPageChunk = false;
+ let head = "";
+ if (isPage && !result.partial && nonAstroPageNeedsHeadInjection(Component)) {
+ head += chunkToString(result, maybeRenderHead());
+ }
+ try {
+ const destination = {
+ write(chunk) {
+ if (isPage && !result.partial && !renderedFirstPageChunk) {
+ renderedFirstPageChunk = true;
+ if (!/" : "\n";
+ str += doctype + head;
+ }
+ }
+ if (chunk instanceof Response) return;
+ str += chunkToString(result, chunk);
+ }
+ };
+ const renderInstance = await renderComponent(result, displayName, Component, props, slots);
+ await renderInstance.render(destination);
+ } catch (e) {
+ if (AstroError.is(e) && !e.loc) {
+ e.setLocation({
+ file: route?.component
+ });
+ }
+ throw e;
+ }
+ return str;
+}
+function nonAstroPageNeedsHeadInjection(pageComponent) {
+ return !!pageComponent?.[needsHeadRenderingSymbol];
+}
+
+const ClientOnlyPlaceholder = "astro-client-only";
+const hasTriedRenderComponentSymbol = Symbol("hasTriedRenderComponent");
+async function renderJSX(result, vnode) {
+ switch (true) {
+ case vnode instanceof HTMLString:
+ if (vnode.toString().trim() === "") {
+ return "";
+ }
+ return vnode;
+ case typeof vnode === "string":
+ return markHTMLString(escapeHTML(vnode));
+ case typeof vnode === "function":
+ return vnode;
+ case (!vnode && vnode !== 0):
+ return "";
+ case Array.isArray(vnode):
+ return markHTMLString(
+ (await Promise.all(vnode.map((v) => renderJSX(result, v)))).join("")
+ );
+ }
+ return renderJSXVNode(result, vnode);
+}
+async function renderJSXVNode(result, vnode) {
+ if (isVNode(vnode)) {
+ switch (true) {
+ case !vnode.type: {
+ throw new Error(`Unable to render ${result.pathname} because it contains an undefined Component!
+Did you forget to import the component or is it possible there is a typo?`);
+ }
+ case vnode.type === Symbol.for("astro:fragment"):
+ return renderJSX(result, vnode.props.children);
+ case vnode.type.isAstroComponentFactory: {
+ let props = {};
+ let slots = {};
+ for (const [key, value] of Object.entries(vnode.props ?? {})) {
+ if (key === "children" || value && typeof value === "object" && value["$$slot"]) {
+ slots[key === "children" ? "default" : key] = () => renderJSX(result, value);
+ } else {
+ props[key] = value;
+ }
+ }
+ const str = await renderToString(result, vnode.type, props, slots);
+ if (str instanceof Response) {
+ throw str;
+ }
+ const html = markHTMLString(str);
+ return html;
+ }
+ case (!vnode.type && vnode.type !== 0):
+ return "";
+ case (typeof vnode.type === "string" && vnode.type !== ClientOnlyPlaceholder):
+ return markHTMLString(await renderElement(result, vnode.type, vnode.props ?? {}));
+ }
+ if (vnode.type) {
+ let extractSlots2 = function(child) {
+ if (Array.isArray(child)) {
+ return child.map((c) => extractSlots2(c));
+ }
+ if (!isVNode(child)) {
+ _slots.default.push(child);
+ return;
+ }
+ if ("slot" in child.props) {
+ _slots[child.props.slot] = [..._slots[child.props.slot] ?? [], child];
+ delete child.props.slot;
+ return;
+ }
+ _slots.default.push(child);
+ };
+ if (typeof vnode.type === "function" && vnode.props["server:root"]) {
+ const output2 = await vnode.type(vnode.props ?? {});
+ return await renderJSX(result, output2);
+ }
+ if (typeof vnode.type === "function") {
+ if (vnode.props[hasTriedRenderComponentSymbol]) {
+ delete vnode.props[hasTriedRenderComponentSymbol];
+ const output2 = await vnode.type(vnode.props ?? {});
+ if (output2?.[AstroJSX] || !output2) {
+ return await renderJSXVNode(result, output2);
+ } else {
+ return;
+ }
+ } else {
+ vnode.props[hasTriedRenderComponentSymbol] = true;
+ }
+ }
+ const { children = null, ...props } = vnode.props ?? {};
+ const _slots = {
+ default: []
+ };
+ extractSlots2(children);
+ for (const [key, value] of Object.entries(props)) {
+ if (value?.["$$slot"]) {
+ _slots[key] = value;
+ delete props[key];
+ }
+ }
+ const slotPromises = [];
+ const slots = {};
+ for (const [key, value] of Object.entries(_slots)) {
+ slotPromises.push(
+ renderJSX(result, value).then((output2) => {
+ if (output2.toString().trim().length === 0) return;
+ slots[key] = () => output2;
+ })
+ );
+ }
+ await Promise.all(slotPromises);
+ let output;
+ if (vnode.type === ClientOnlyPlaceholder && vnode.props["client:only"]) {
+ output = await renderComponentToString(
+ result,
+ vnode.props["client:display-name"] ?? "",
+ null,
+ props,
+ slots
+ );
+ } else {
+ output = await renderComponentToString(
+ result,
+ typeof vnode.type === "function" ? vnode.type.name : vnode.type,
+ vnode.type,
+ props,
+ slots
+ );
+ }
+ return markHTMLString(output);
+ }
+ }
+ return markHTMLString(`${vnode}`);
+}
+async function renderElement(result, tag, { children, ...props }) {
+ return markHTMLString(
+ `<${tag}${spreadAttributes(props)}${markHTMLString(
+ (children == null || children == "") && voidElementNames.test(tag) ? `/>` : `>${children == null ? "" : await renderJSX(result, prerenderElementChildren(tag, children))}${tag}>`
+ )}`
+ );
+}
+function prerenderElementChildren(tag, children) {
+ if (typeof children === "string" && (tag === "style" || tag === "script")) {
+ return markHTMLString(children);
+ } else {
+ return children;
+ }
+}
+
+async function renderPage(result, componentFactory, props, children, streaming, route) {
+ if (!isAstroComponentFactory(componentFactory)) {
+ result._metadata.headInTree = result.componentMetadata.get(componentFactory.moduleId)?.containsHead ?? false;
+ const pageProps = { ...props ?? {}, "server:root": true };
+ const str = await renderComponentToString(
+ result,
+ componentFactory.name,
+ componentFactory,
+ pageProps,
+ {},
+ true,
+ route
+ );
+ const bytes = encoder$1.encode(str);
+ return new Response(bytes, {
+ headers: new Headers([
+ ["Content-Type", "text/html; charset=utf-8"],
+ ["Content-Length", bytes.byteLength.toString()]
+ ])
+ });
+ }
+ result._metadata.headInTree = result.componentMetadata.get(componentFactory.moduleId)?.containsHead ?? false;
+ let body;
+ if (streaming) {
+ if (isNode && !isDeno) {
+ const nodeBody = await renderToAsyncIterable(
+ result,
+ componentFactory,
+ props,
+ children,
+ true,
+ route
+ );
+ body = nodeBody;
+ } else {
+ body = await renderToReadableStream(result, componentFactory, props, children, true, route);
+ }
+ } else {
+ body = await renderToString(result, componentFactory, props, children, true, route);
+ }
+ if (body instanceof Response) return body;
+ const init = result.response;
+ const headers = new Headers(init.headers);
+ if (!streaming && typeof body === "string") {
+ body = encoder$1.encode(body);
+ headers.set("Content-Length", body.byteLength.toString());
+ }
+ if (route?.component.endsWith(".md")) {
+ headers.set("Content-Type", "text/html; charset=utf-8");
+ }
+ let status = init.status;
+ if (route?.route === "/404") {
+ status = 404;
+ } else if (route?.route === "/500") {
+ status = 500;
+ }
+ if (status) {
+ return new Response(body, { ...init, headers, status });
+ } else {
+ return new Response(body, { ...init, headers });
+ }
+}
+
+"0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_".split("").reduce((v, c) => (v[c.charCodeAt(0)] = c, v), []);
+"-0123456789_".split("").reduce((v, c) => (v[c.charCodeAt(0)] = c, v), []);
+
+function spreadAttributes(values = {}, _name, { class: scopedClassName } = {}) {
+ let output = "";
+ if (scopedClassName) {
+ if (typeof values.class !== "undefined") {
+ values.class += ` ${scopedClassName}`;
+ } else if (typeof values["class:list"] !== "undefined") {
+ values["class:list"] = [values["class:list"], scopedClassName];
+ } else {
+ values.class = scopedClassName;
+ }
+ }
+ for (const [key, value] of Object.entries(values)) {
+ output += addAttribute(value, key, true);
+ }
+ return markHTMLString(output);
+}
+
+export { ASTRO_VERSION as A, DEFAULT_404_COMPONENT as D, Fragment as F, NOOP_MIDDLEWARE_HEADER as N, ROUTE_TYPE_HEADER as R, addAttribute as a, createAstro as b, createComponent as c, renderComponent as d, renderHead as e, renderSlot as f, decodeKey as g, REROUTE_DIRECTIVE_HEADER as h, decryptString as i, createSlotValueFromString as j, renderSlotToString as k, renderJSX as l, maybeRenderHead as m, chunkToString as n, isRenderInstruction as o, originPathnameSymbol as p, clientLocalsSymbol as q, renderTemplate as r, spreadAttributes as s, clientAddressSymbol as t, responseSentSymbol as u, renderPage as v, REWRITE_DIRECTIVE_HEADER_KEY as w, REWRITE_DIRECTIVE_HEADER_VALUE as x, renderEndpoint as y, REROUTABLE_STATUS_CODES as z };
diff --git a/dist/server/chunks/cms_CRTVymfB.mjs b/dist/server/chunks/cms_CRTVymfB.mjs
new file mode 100644
index 0000000..51c89f8
--- /dev/null
+++ b/dist/server/chunks/cms_CRTVymfB.mjs
@@ -0,0 +1,322 @@
+import Database from 'better-sqlite3';
+import { existsSync, mkdirSync } from 'node:fs';
+import path from 'node:path';
+
+const settings = {
+ siteInfo: {
+ name: "روس امروز",
+ tagline: "خبرگزاری",
+ description: "خبرگزاری تخصصی پوشش اخبار روسیه، اوراسیا و روابط منطقهای",
+ copyright: "۱۴۰۵ روس امروز — تمامی حقوق محفوظ است"
+ },
+ breakingNews: {
+ label: "فوری",
+ href: "/posts/آستانا-میزبان-جام-قهرمانان-چهار-قاره-در-اسکیت-نمایشی-در-سال-۲۰۲۷-1782120219014/",
+ items: [
+ "آستانا میزبان جام قهرمانان چهار قاره در اسکیت نمایشی در سال ۲۰۲۷"
+ ],
+ postSlugs: [
+ "آستانا-میزبان-جام-قهرمانان-چهار-قاره-در-اسکیت-نمایشی-در-سال-۲۰۲۷-1782120219014"
+ ]
+ },
+ hotTopics: [
+ "انرژی",
+ "تحریم",
+ "نفت",
+ "گاز",
+ "اوراسیا",
+ "SCO",
+ "روبل",
+ "تجارت",
+ "قفقاز",
+ "ناتو",
+ "بریکس"
+ ],
+ homeLayout: {
+ featuredSlug: "در-کوبان-به-فرد-حمله-کننده-در-مرکز-خرید-اتهام-قتل-و-تلاش-برای-قتل-وارد-شد-1782119386187",
+ sideFeaturedSlugs: [
+ "south-caucasus-geopolitics",
+ "oil-price-new-record"
+ ],
+ latestCount: 4,
+ showAd: true
+ },
+ ad: {
+ enabled: true,
+ label: "آگهی",
+ href: "/advertise/",
+ text: "جایگاه تبلیغات"
+ },
+ agents: {
+ tass: {
+ processedLinks: [
+ "https://tass.ru/proisshestviya/27834845",
+ "https://tass.ru/sport/27834851",
+ "https://tass.ru/obschestvo/27834629",
+ "https://tass.ru/mezhdunarodnaya-panorama/27834975",
+ "https://tass.ru/obschestvo/27834957",
+ "https://tass.ru/politika/27834811",
+ "https://tass.ru/obschestvo/27818171",
+ "https://tass.ru/obschestvo/27835073",
+ "https://tass.ru/sport/27835167"
+ ]
+ }
+ }
+};
+const categories = [
+ "اقتصاد",
+ "سیاست",
+ "انرژی",
+ "فناوری",
+ "بینالملل"
+];
+const posts = [
+];
+const fallbackCms = {
+ settings: settings,
+ categories: categories,
+ posts: posts
+};
+
+const PERSIAN_DIGITS = ["۰", "۱", "۲", "۳", "۴", "۵", "۶", "۷", "۸", "۹"];
+const toPersianDigits = (value) => String(value).replace(/\d/g, (digit) => PERSIAN_DIGITS[Number(digit)] ?? digit);
+const getPublishedAt = (post) => {
+ if (post.publishedAt) {
+ const date = new Date(post.publishedAt);
+ return Number.isNaN(date.getTime()) ? null : date;
+ }
+ const match = post.slug.match(/(\d{13})$/);
+ if (match) {
+ const date = new Date(Number(match[1]));
+ return Number.isNaN(date.getTime()) ? null : date;
+ }
+ return null;
+};
+const derivePublishedAt = (post) => {
+ const date = getPublishedAt(post);
+ return date ? date.toISOString() : void 0;
+};
+const formatShamsiDate = (date) => {
+ const formatter = new Intl.DateTimeFormat("fa-IR-u-ca-persian", {
+ year: "numeric",
+ month: "long",
+ day: "numeric",
+ hour: "2-digit",
+ minute: "2-digit"
+ });
+ return toPersianDigits(formatter.format(date));
+};
+const formatShamsiDay = (date) => {
+ const formatter = new Intl.DateTimeFormat("fa-IR-u-ca-persian", {
+ year: "numeric",
+ month: "long",
+ day: "numeric"
+ });
+ return toPersianDigits(formatter.format(date));
+};
+const formatRelativeTime = (date) => {
+ const now = Date.now();
+ const diffSeconds = Math.max(0, Math.round((now - date.getTime()) / 1e3));
+ if (diffSeconds < 60) {
+ return "هماکنون";
+ }
+ const diffMinutes = Math.floor(diffSeconds / 60);
+ if (diffMinutes < 60) {
+ return `${toPersianDigits(diffMinutes)} دقیقه پیش`;
+ }
+ const diffHours = Math.floor(diffMinutes / 60);
+ if (diffHours < 24) {
+ return `${toPersianDigits(diffHours)} ساعت پیش`;
+ }
+ const diffDays = Math.floor(diffHours / 24);
+ if (diffDays < 30) {
+ return `${toPersianDigits(diffDays)} روز پیش`;
+ }
+ return formatShamsiDay(date);
+};
+const formatPostRelativeTime = (post) => {
+ const date = getPublishedAt(post);
+ return date ? formatRelativeTime(date) : post.time;
+};
+const formatPostShamsiDate = (post) => {
+ const date = getPublishedAt(post);
+ return date ? formatShamsiDate(date) : post.time;
+};
+
+const dbPath = path.join(process.cwd(), "data", "cms.db");
+const CMS_KEY = "cms";
+let dbInstance = null;
+const getDb = () => {
+ if (dbInstance) {
+ return dbInstance;
+ }
+ const directory = path.dirname(dbPath);
+ if (!existsSync(directory)) {
+ mkdirSync(directory, { recursive: true });
+ }
+ const db = new Database(dbPath);
+ db.pragma("journal_mode = WAL");
+ db.exec(`
+ CREATE TABLE IF NOT EXISTS cms (
+ key TEXT PRIMARY KEY,
+ value TEXT NOT NULL,
+ updated_at INTEGER NOT NULL
+ )
+ `);
+ dbInstance = db;
+ return db;
+};
+const seedIfEmpty = (db) => {
+ const existing = db.prepare("SELECT value FROM cms WHERE key = ?").get(CMS_KEY);
+ if (existing) {
+ return;
+ }
+ db.prepare("INSERT INTO cms (key, value, updated_at) VALUES (?, ?, ?)").run(
+ CMS_KEY,
+ JSON.stringify(fallbackCms),
+ Date.now()
+ );
+};
+const staticPages = [
+ {
+ slug: "about",
+ title: "درباره ما",
+ description: "آشنایی با مأموریت، رویکرد خبری و تیم تحریریه روس امروز",
+ body: [
+ "روس امروز رسانهای تخصصی برای پوشش خبرها، گزارشها و تحلیلهای مرتبط با روسیه، اوراسیا و روابط منطقهای است.",
+ "هدف ما ارائه روایت دقیق، سریع و قابل اتکا از روندهای سیاسی، اقتصادی، انرژی و فناوری در این حوزه جغرافیایی است.",
+ "تحریریه روس امروز تلاش میکند با زبان روشن و تحلیل دادهمحور، تصویر کاملتری از تحولات منطقه ارائه دهد."
+ ]
+ },
+ {
+ slug: "contact",
+ title: "ارتباط با ما",
+ description: "راههای ارتباط با تحریریه و واحد بازرگانی روس امروز",
+ body: [
+ "برای ارسال خبر، پیشنهاد همکاری یا پیگیری موضوعات رسانهای میتوانید از طریق ایمیل info@ruseemrooz.ir با ما در تماس باشید.",
+ "پیامهای مرتبط با آگهی و همکاری تجاری توسط واحد بازرگانی بررسی و در کوتاهترین زمان پاسخ داده میشود."
+ ]
+ },
+ {
+ slug: "editorial-team",
+ title: "تیم تحریریه",
+ description: "معرفی ساختار تحریریه و حوزههای تخصصی روس امروز",
+ footerLabel: "تیم تحریریه",
+ body: [
+ "تیم تحریریه روس امروز از نویسندگان و تحلیلگرانی تشکیل شده که حوزههای روسیه، قفقاز، آسیای مرکزی، انرژی و اقتصاد منطقهای را دنبال میکنند.",
+ "مطالب منتشرشده پس از بررسی محتوایی و ویرایشی در سایت قرار میگیرند تا دقت و خوانایی حفظ شود."
+ ]
+ },
+ {
+ slug: "advertise",
+ title: "آگهی در سایت",
+ description: "فرصتهای تبلیغاتی و همکاری رسانهای با روس امروز",
+ body: [
+ "روس امروز امکان انتشار آگهی، رپورتاژ و کمپینهای هدفمند برای مخاطبان علاقهمند به روسیه و اوراسیا را فراهم میکند.",
+ "برای دریافت تعرفه و پیشنهاد رسانهای اختصاصی، با واحد بازرگانی سایت تماس بگیرید."
+ ]
+ },
+ {
+ slug: "privacy",
+ title: "حریم خصوصی",
+ description: "سیاست حفظ حریم خصوصی کاربران روس امروز",
+ body: [
+ "روس امروز اطلاعات کاربران را تنها برای بهبود تجربه کاربری، ارسال خبرنامه و تحلیل عملکرد سایت استفاده میکند.",
+ "اطلاعات تماس کاربران بدون رضایت آنها در اختیار اشخاص ثالث قرار نمیگیرد."
+ ]
+ }
+];
+const readCms = async () => {
+ try {
+ const db = getDb();
+ seedIfEmpty(db);
+ const row = db.prepare("SELECT value FROM cms WHERE key = ?").get(CMS_KEY);
+ const data = row ? JSON.parse(row.value) : fallbackCms;
+ return normalizeCms(data);
+ } catch {
+ return normalizeCms(fallbackCms);
+ }
+};
+const writeCms = async (data) => {
+ const db = getDb();
+ const json = JSON.stringify(normalizeCms(data));
+ db.prepare(
+ `INSERT INTO cms (key, value, updated_at) VALUES (?, ?, ?)
+ ON CONFLICT(key) DO UPDATE SET value = excluded.value, updated_at = excluded.updated_at`
+ ).run(CMS_KEY, json, Date.now());
+};
+const normalizeCms = (data) => ({
+ ...data,
+ settings: {
+ ...data.settings,
+ breakingNews: {
+ ...data.settings.breakingNews,
+ postSlugs: data.settings.breakingNews.postSlugs ?? []
+ },
+ agents: {
+ ...data.settings.agents,
+ tass: {
+ processedLinks: data.settings.agents?.tass?.processedLinks ?? []
+ }
+ }
+ },
+ posts: data.posts.map((post) => ({
+ ...post,
+ status: post.status ?? "pending",
+ body: Array.isArray(post.body) ? post.body : [],
+ urgent: post.urgent ?? false,
+ sidebar: post.sidebar ?? false,
+ publishedAt: post.publishedAt ?? derivePublishedAt(post)
+ }))
+});
+const getApprovedPosts = (data) => data.posts.filter((post) => post.status === "approved");
+const getCategoryPath = (category) => `/categories/${encodeURIComponent(category)}/`;
+const getStaticPageBySlug = (slug) => staticPages.find((page) => page.slug === slug);
+const getPrimaryNav = () => [
+ { label: "صفحه اصلی", href: "/" },
+ ...staticPages.filter((page) => ["about", "contact"].includes(page.slug)).map((page) => ({ label: page.navLabel ?? page.title, href: `/${page.slug}/` }))
+];
+const getFooterPageLinks = () => staticPages.map((page) => ({
+ label: page.footerLabel ?? page.navLabel ?? page.title,
+ href: `/${page.slug}/`
+}));
+const getPostBySlug = (posts, slug) => posts.find((post) => post.slug === slug);
+const getPostsByCategory = (posts, category) => posts.filter((post) => post.category === category);
+const getPaginatedPosts = (page, perPage, source) => {
+ const totalPages = Math.max(1, Math.ceil(source.length / perPage));
+ const currentPage = Math.min(Math.max(Math.floor(page), 1), totalPages);
+ const start = (currentPage - 1) * perPage;
+ return {
+ currentPage,
+ totalPages,
+ posts: source.slice(start, start + perPage)
+ };
+};
+const searchPosts = (posts, query) => {
+ const normalizedQuery = query.trim().toLocaleLowerCase("fa-IR");
+ if (!normalizedQuery) {
+ return [];
+ }
+ return posts.filter((post) => {
+ const searchableText = [
+ post.title,
+ post.category,
+ post.excerpt,
+ ...post.body
+ ].join(" ").toLocaleLowerCase("fa-IR");
+ return searchableText.includes(normalizedQuery);
+ });
+};
+const slugify = (value) => value.trim().toLowerCase().replace(/[^\p{L}\p{N}]+/gu, "-").replace(/^-+|-+$/g, "");
+const parseImageBlock = (value) => {
+ const match = value.match(/^\[image:(.+?)(?:\|(.*))?\]$/);
+ if (!match) {
+ return null;
+ }
+ return {
+ src: match[1].trim(),
+ caption: match[2]?.trim() ?? ""
+ };
+};
+
+export { getPostsByCategory as a, getApprovedPosts as b, getPostBySlug as c, getPaginatedPosts as d, getCategoryPath as e, formatPostRelativeTime as f, getStaticPageBySlug as g, formatPostShamsiDate as h, searchPosts as i, getPrimaryNav as j, getFooterPageLinks as k, parseImageBlock as p, readCms as r, slugify as s, writeCms as w };
diff --git a/dist/server/chunks/images_DSuHPXn1.mjs b/dist/server/chunks/images_DSuHPXn1.mjs
new file mode 100644
index 0000000..0b8df00
--- /dev/null
+++ b/dist/server/chunks/images_DSuHPXn1.mjs
@@ -0,0 +1,52 @@
+import { existsSync, mkdirSync } from 'node:fs';
+import path from 'node:path';
+import { randomUUID } from 'node:crypto';
+import sharp from 'sharp';
+
+const uploadDirectory = path.join(process.cwd(), "public", "uploads");
+const thumbnailDirectory = path.join(uploadDirectory, "thumbs");
+const ensureUploadDirectories = () => {
+ if (!existsSync(uploadDirectory)) {
+ mkdirSync(uploadDirectory, { recursive: true });
+ }
+ if (!existsSync(thumbnailDirectory)) {
+ mkdirSync(thumbnailDirectory, { recursive: true });
+ }
+};
+const storeImageBuffer = async (sourceBuffer) => {
+ ensureUploadDirectories();
+ const id = randomUUID();
+ const imageName = `${id}.webp`;
+ const thumbnailName = `${id}-thumb.webp`;
+ const imagePath = path.join(uploadDirectory, imageName);
+ const thumbnailPath = path.join(thumbnailDirectory, thumbnailName);
+ await sharp(sourceBuffer).rotate().resize({ width: 1400, withoutEnlargement: true }).webp({ quality: 82 }).toFile(imagePath);
+ await sharp(sourceBuffer).rotate().resize(420, 260, { fit: "cover" }).webp({ quality: 76 }).toFile(thumbnailPath);
+ return {
+ image: `/uploads/${imageName}`,
+ thumbnail: `/uploads/thumbs/${thumbnailName}`
+ };
+};
+const downloadAndStoreImage = async (url) => {
+ try {
+ const response = await fetch(url, {
+ headers: {
+ "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36",
+ accept: "image/avif,image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8",
+ referer: "https://tass.ru/"
+ }
+ });
+ if (!response.ok) {
+ return null;
+ }
+ const contentType = response.headers.get("content-type") ?? "";
+ if (!contentType.startsWith("image/")) {
+ return null;
+ }
+ return storeImageBuffer(Buffer.from(await response.arrayBuffer()));
+ } catch {
+ return null;
+ }
+};
+
+export { downloadAndStoreImage as d, storeImageBuffer as s };
diff --git a/dist/server/chunks/publisher_Dj_UvF8J.mjs b/dist/server/chunks/publisher_Dj_UvF8J.mjs
new file mode 100644
index 0000000..5f32a8a
--- /dev/null
+++ b/dist/server/chunks/publisher_Dj_UvF8J.mjs
@@ -0,0 +1,41 @@
+import { r as readCms, s as slugify, w as writeCms } from './cms_CRTVymfB.mjs';
+
+const publishPost = async (input) => {
+ const cms = await readCms();
+ const title = input.title.trim();
+ const slug = slugify(input.slug || title);
+ if (!title || !slug) {
+ throw new Error("title is required");
+ }
+ const body = Array.isArray(input.body) ? input.body : String(input.body ?? "").split(/\n\s*\n/).map((item) => item.trim()).filter(Boolean);
+ const existingIndex = cms.posts.findIndex((item) => item.slug === slug);
+ const existing = existingIndex >= 0 ? cms.posts[existingIndex] : void 0;
+ const post = {
+ slug,
+ title,
+ category: input.category || cms.categories[0] || "عمومی",
+ time: input.time || "هماکنون",
+ readTime: input.readTime || "۳ دقیقه",
+ excerpt: input.excerpt || body[0] || "",
+ body,
+ status: input.status || "pending",
+ image: input.image || void 0,
+ thumbnail: input.thumbnail || void 0,
+ urgent: input.urgent ?? false,
+ sidebar: input.sidebar ?? false,
+ sourceUrl: input.sourceUrl || void 0,
+ publishedAt: input.publishedAt || existing?.publishedAt || (/* @__PURE__ */ new Date()).toISOString()
+ };
+ if (existingIndex >= 0) {
+ cms.posts[existingIndex] = post;
+ } else {
+ cms.posts.unshift(post);
+ }
+ if (!cms.categories.includes(post.category)) {
+ cms.categories.push(post.category);
+ }
+ await writeCms(cms);
+ return post;
+};
+
+export { publishPost as p };
diff --git a/dist/server/chunks/tassAgent_jxZgSz7r.mjs b/dist/server/chunks/tassAgent_jxZgSz7r.mjs
new file mode 100644
index 0000000..728b216
--- /dev/null
+++ b/dist/server/chunks/tassAgent_jxZgSz7r.mjs
@@ -0,0 +1,454 @@
+import { XMLParser } from 'fast-xml-parser';
+import OpenAI from 'openai';
+import * as cheerio from 'cheerio';
+import { d as downloadAndStoreImage } from './images_DSuHPXn1.mjs';
+import { p as publishPost } from './publisher_Dj_UvF8J.mjs';
+import { r as readCms, s as slugify, w as writeCms } from './cms_CRTVymfB.mjs';
+import puppeteer from 'puppeteer-core';
+import { existsSync } from 'node:fs';
+
+let browserInstance = null;
+const getChromePath = () => {
+ const platform = process.platform;
+ if (platform === "darwin") {
+ const paths = [
+ "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome",
+ "/Applications/Chromium.app/Contents/MacOS/Chromium",
+ "/Applications/Brave Browser.app/Contents/MacOS/Brave Browser"
+ ];
+ for (const p of paths) {
+ if (existsSync(p)) return p;
+ }
+ }
+ if (platform === "linux") {
+ const paths = [
+ "/usr/bin/google-chrome",
+ "/usr/bin/google-chrome-stable",
+ "/usr/bin/chromium",
+ "/usr/bin/chromium-browser",
+ "/snap/bin/chromium"
+ ];
+ for (const p of paths) {
+ if (existsSync(p)) return p;
+ }
+ }
+ if (platform === "win32") {
+ const paths = [
+ "C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe",
+ "C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe"
+ ];
+ for (const p of paths) {
+ if (existsSync(p)) return p;
+ }
+ }
+ return "";
+};
+const getBrowser$1 = async () => {
+ if (browserInstance && browserInstance.connected) {
+ return browserInstance;
+ }
+ const chromePath = process.env.PUPPETEER_EXECUTABLE_PATH || getChromePath();
+ if (!chromePath) {
+ throw new Error("Chrome/Chromium not found. Install Chrome or set PUPPETEER_EXECUTABLE_PATH.");
+ }
+ browserInstance = await puppeteer.launch({
+ executablePath: chromePath,
+ headless: true,
+ args: [
+ "--no-sandbox",
+ "--disable-setuid-sandbox",
+ "--disable-dev-shm-usage",
+ "--disable-gpu",
+ "--window-size=1920,1080"
+ ]
+ });
+ return browserInstance;
+};
+const closeBrowser = async () => {
+ if (browserInstance) {
+ await browserInstance.close();
+ browserInstance = null;
+ }
+};
+const scrapeArticle = async (url) => {
+ const browser = await getBrowser$1();
+ const page = await browser.newPage();
+ try {
+ await page.setViewport({ width: 1920, height: 1080 });
+ await page.setUserAgent(
+ "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36"
+ );
+ await page.setRequestInterception(true);
+ page.on("request", (req) => {
+ const type = req.resourceType();
+ if (["font", "media"].includes(type)) {
+ req.abort();
+ } else {
+ req.continue();
+ }
+ });
+ await page.goto(url, {
+ waitUntil: "domcontentloaded",
+ timeout: 6e4
+ });
+ await new Promise((r) => setTimeout(r, 2e3));
+ const data = await page.evaluate(() => {
+ const title = document.querySelector('meta[property="og:title"]')?.getAttribute("content") || document.querySelector("h1")?.textContent?.trim() || document.title;
+ const description = document.querySelector('meta[property="og:description"]')?.getAttribute("content") || document.querySelector('meta[name="description"]')?.getAttribute("content") || "";
+ const ogImage = document.querySelector('meta[property="og:image"]')?.getAttribute("content") || "";
+ const allImages = [];
+ const imageBlacklist = ["logo", "icon", "avatar", "counter", "yadro", "pixel", "track", "1x1", "badge"];
+ document.querySelectorAll("img").forEach((img) => {
+ const src = img.getAttribute("src") || img.getAttribute("data-src") || "";
+ if (!src) return;
+ const lowerSrc = src.toLowerCase();
+ if (imageBlacklist.some((b) => lowerSrc.includes(b))) return;
+ const width = parseInt(img.getAttribute("width") || "0");
+ const height = parseInt(img.getAttribute("height") || "0");
+ if (width > 0 && width < 50 || height > 0 && height < 50) return;
+ try {
+ allImages.push(new URL(src, window.location.origin).href);
+ } catch {
+ }
+ });
+ const contentContainer = document.querySelector('[class*="ContentPageContainer_content"]') || document.querySelector("article") || document.querySelector("[data-content]");
+ const articleImage = contentContainer?.querySelector("img")?.getAttribute("src") || "";
+ const imageUrl = ogImage || articleImage || "";
+ const paragraphs = [];
+ const footerTexts = /* @__PURE__ */ new Set();
+ document.querySelectorAll('[class*="Legal"], [class*="legal"], [class*="footer"], [class*="Footer"], [class*="Copyright"], [class*="copyright"], [class*="sidebar"], [class*="Sidebar"]').forEach((el) => {
+ el.querySelectorAll("p, li").forEach((p) => {
+ const t = p.textContent?.trim().replace(/\s+/g, " ") || "";
+ if (t) footerTexts.add(t);
+ });
+ });
+ const selectors = [
+ '[class*="ContentPageContainer_content"] p',
+ '[class*="article"] p',
+ '[class*="Article"] p',
+ '[class*="news-text"] p',
+ '[class*="detail"] p',
+ '[class*="topic"] p',
+ "article p",
+ "[data-content] p",
+ ".text-content p",
+ ".article-body p"
+ ];
+ for (const selector of selectors) {
+ document.querySelectorAll(selector).forEach((el) => {
+ const text = el.textContent?.trim().replace(/\s+/g, " ") || "";
+ if (text.length > 20 && !paragraphs.includes(text) && !footerTexts.has(text)) {
+ paragraphs.push(text);
+ }
+ });
+ if (paragraphs.length > 0) break;
+ }
+ if (paragraphs.length < 2) {
+ document.querySelectorAll("main p, .content p, .post-content p").forEach((el) => {
+ const text = el.textContent?.trim().replace(/\s+/g, " ") || "";
+ if (text.length > 30 && !paragraphs.includes(text) && !footerTexts.has(text)) {
+ paragraphs.push(text);
+ }
+ });
+ }
+ if (paragraphs.length < 2) {
+ document.querySelectorAll("p").forEach((el) => {
+ const text = el.textContent?.trim().replace(/\s+/g, " ") || "";
+ if (text.length > 60 && !paragraphs.includes(text) && !footerTexts.has(text)) {
+ paragraphs.push(text);
+ }
+ });
+ }
+ return {
+ title: title?.trim() || "",
+ description: description?.trim() || "",
+ imageUrl,
+ text: paragraphs.slice(0, 14).join("\n\n"),
+ allImages
+ };
+ });
+ const resolvedImages = data.allImages.map((img) => {
+ try {
+ return new URL(img, url).href;
+ } catch {
+ return img;
+ }
+ });
+ return {
+ ...data,
+ allImages: resolvedImages,
+ imageUrl: data.imageUrl ? (() => {
+ try {
+ return new URL(data.imageUrl, url).href;
+ } catch {
+ return data.imageUrl;
+ }
+ })() : ""
+ };
+ } finally {
+ await page.close();
+ }
+};
+
+const rssUrl = "https://tass.ru/rss/v2.xml";
+const getOpenAIClient = () => new OpenAI({
+ apiKey: "sk-C9kUNuxFPA6Jvpi2stzikxzZMJzPuYy5TIoJEJiVR89oeiD6",
+ baseURL: "https://api.gapgpt.app/v1"
+});
+const getModel = () => "gpt-5-nano";
+const fetchText = async (url) => {
+ try {
+ const response = await fetch(url, {
+ headers: {
+ "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36",
+ accept: "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
+ "accept-language": "ru-RU,ru;q=0.9,en-US;q=0.8,en;q=0.7",
+ referer: "https://tass.ru/"
+ },
+ signal: AbortSignal.timeout(8e3)
+ });
+ if (response.ok) {
+ return response.text();
+ }
+ } catch {
+ }
+ const browser = await getBrowser();
+ const page = await browser.newPage();
+ try {
+ await page.goto(url, { waitUntil: "domcontentloaded", timeout: 3e4 });
+ await new Promise((r) => setTimeout(r, 1e3));
+ const content = await page.evaluate(() => {
+ const pre = document.querySelector("pre");
+ if (pre) return pre.textContent || "";
+ return document.body.innerText || document.body.textContent || "";
+ });
+ return content;
+ } finally {
+ await page.close();
+ }
+};
+const getTassFeedLinks = async (limit = 50) => {
+ const xml = await fetchText(rssUrl);
+ const parser = new XMLParser({ ignoreAttributes: false });
+ const parsed = parser.parse(xml);
+ const rawItems = parsed?.rss?.channel?.item ?? [];
+ const items = Array.isArray(rawItems) ? rawItems : [rawItems];
+ return items.slice(0, limit).map((item) => item.link).filter(Boolean);
+};
+const getItems = async (limit) => {
+ const xml = await fetchText(rssUrl);
+ const parser = new XMLParser({ ignoreAttributes: false });
+ const parsed = parser.parse(xml);
+ const rawItems = parsed?.rss?.channel?.item ?? [];
+ const items = Array.isArray(rawItems) ? rawItems : [rawItems];
+ return items.slice(0, Math.max(limit * 10, 30));
+};
+const stripHtml = (value = "") => {
+ if (!value) return "";
+ const $ = cheerio.load(value);
+ return $.text().replace(/\s+/g, " ").trim();
+};
+const getRssImage = (item) => {
+ const candidates = [
+ item.enclosure?.["@_type"]?.startsWith("image/") ? item.enclosure?.["@_url"] : "",
+ item["media:content"]?.["@_type"]?.startsWith("image/") ? item["media:content"]?.["@_url"] : "",
+ item["media:thumbnail"]?.["@_url"]
+ ].filter(Boolean);
+ return candidates.find((candidate) => candidate.startsWith("http")) ?? "";
+};
+const sortImageItemsFirst = (items) => [...items].sort((first, second) => {
+ const firstHasImage = getRssImage(first) ? 1 : 0;
+ const secondHasImage = getRssImage(second) ? 1 : 0;
+ return secondHasImage - firstHasImage;
+});
+const getFallbackArticle = (item) => {
+ const description = stripHtml(item.description);
+ return {
+ title: item.title ?? "",
+ description,
+ imageUrl: getRssImage(item),
+ text: description,
+ usedFallback: true
+ };
+};
+const toAbsoluteUrl = (value, baseUrl) => {
+ try {
+ return new URL(value, baseUrl).toString();
+ } catch {
+ return "";
+ }
+};
+const extractArticleCheerio = async (url) => {
+ const html = await fetchText(url);
+ const $ = cheerio.load(html);
+ const title = $('meta[property="og:title"]').attr("content") || $("h1").first().text().replace(/\s+/g, " ").trim();
+ const description = $('meta[property="og:description"]').attr("content") || "";
+ const imageCandidates = [
+ $('meta[property="og:image"]').attr("content"),
+ $("article img").first().attr("src"),
+ $("img").first().attr("src")
+ ].filter(Boolean);
+ const imageUrl = imageCandidates.map((c) => toAbsoluteUrl(c, url)).find(Boolean) ?? "";
+ const paragraphs = [];
+ $("article p, [data-content] p, .text-content p, p").each((_, el) => {
+ const pText = $(el).text().replace(/\s+/g, " ").trim();
+ if (pText.length > 80) {
+ paragraphs.push(pText);
+ }
+ });
+ return {
+ title,
+ description,
+ imageUrl,
+ text: paragraphs.slice(0, 14).join("\n\n")
+ };
+};
+const extractArticle = async (url) => {
+ try {
+ const scraped = await scrapeArticle(url);
+ if (scraped.text || scraped.title) {
+ return {
+ title: scraped.title,
+ description: scraped.description,
+ imageUrl: scraped.imageUrl,
+ text: scraped.text,
+ usedFallback: false
+ };
+ }
+ } catch {
+ }
+ const fallback = await extractArticleCheerio(url);
+ return {
+ ...fallback,
+ usedFallback: true
+ };
+};
+const translateArticle = async (article) => {
+ const client = getOpenAIClient();
+ if (!client.apiKey) {
+ throw new Error("GAPGPT_API_KEY or OPENAI_API_KEY is not configured");
+ }
+ const sourceText = article.text || article.description || article.title;
+ if (!sourceText) {
+ throw new Error("RSS item has no usable text");
+ }
+ const completion = await client.chat.completions.create({
+ model: getModel(),
+ messages: [
+ {
+ role: "system",
+ content: "تو سردبیر فارسی یک سایت خبری هستی. متن روسی/انگلیسی خبر را به فارسی روان و خبری تبدیل کن. خروجی فقط JSON معتبر باشد."
+ },
+ {
+ role: "user",
+ content: JSON.stringify({
+ instruction: "ترجمه و بازنویسی خبری فارسی بساز. دسته یکی از اقتصاد، سیاست، انرژی، فناوری، بینالملل باشد. body آرایه پاراگرافها باشد.",
+ source: { ...article, text: sourceText },
+ schema: {
+ title: "string",
+ excerpt: "string",
+ category: "string",
+ readTime: "string",
+ body: ["string"]
+ }
+ })
+ }
+ ],
+ response_format: { type: "json_object" },
+ temperature: 0.3
+ });
+ const content = completion.choices[0]?.message?.content ?? "{}";
+ const jsonText = content.match(/\{[\s\S]*\}/)?.[0] ?? content;
+ const parsed = JSON.parse(jsonText);
+ return {
+ title: parsed.title?.trim() || article.title,
+ excerpt: parsed.excerpt?.trim() || article.description,
+ category: parsed.category?.trim() || "بینالملل",
+ readTime: parsed.readTime?.trim() || "۳ دقیقه",
+ body: Array.isArray(parsed.body) ? parsed.body.map(String).filter(Boolean) : [article.text]
+ };
+};
+const runTassAgent = async (limit = 3) => {
+ const cms = await readCms();
+ const processedLinks = new Set(cms.settings.agents?.tass?.processedLinks ?? []);
+ const items = sortImageItemsFirst(await getItems(limit));
+ const created = [];
+ const failed = [];
+ const skipped = [];
+ for (const item of items) {
+ if (created.length >= limit) {
+ break;
+ }
+ const link = item.link;
+ if (!link) {
+ continue;
+ }
+ if (processedLinks.has(link)) {
+ skipped.push(link);
+ continue;
+ }
+ try {
+ let article = getFallbackArticle(item);
+ let extractionWarning = "";
+ try {
+ const extractedArticle = await extractArticle(link);
+ article = {
+ ...article,
+ ...extractedArticle,
+ title: extractedArticle.title || article.title,
+ description: extractedArticle.description || article.description,
+ imageUrl: extractedArticle.imageUrl || article.imageUrl,
+ text: extractedArticle.text || article.text
+ };
+ } catch (error) {
+ extractionWarning = error instanceof Error ? error.message : "Article extraction failed";
+ }
+ const translated = await translateArticle({
+ title: article.title || item.title || "",
+ description: article.description || item.description || "",
+ text: article.text || item.description || ""
+ });
+ const storedImage = article.imageUrl ? await downloadAndStoreImage(article.imageUrl) : null;
+ const body = [...translated.body];
+ if (storedImage?.image) {
+ body.splice(1, 0, `[image:${storedImage.image}|${translated.title}]`);
+ }
+ const post = await publishPost({
+ title: translated.title,
+ slug: slugify(`${translated.title}-${Date.now()}`),
+ category: translated.category,
+ readTime: translated.readTime,
+ excerpt: translated.excerpt,
+ body,
+ status: "pending",
+ image: storedImage?.image,
+ thumbnail: storedImage?.thumbnail,
+ sourceUrl: link
+ });
+ processedLinks.add(link);
+ created.push({
+ slug: post.slug,
+ title: post.title,
+ source: link,
+ sourceImage: article.imageUrl || void 0,
+ image: storedImage?.image,
+ thumbnail: storedImage?.thumbnail,
+ warning: extractionWarning || void 0
+ });
+ } catch (error) {
+ failed.push({ source: link, error: error instanceof Error ? error.message : "Unknown error" });
+ }
+ }
+ const latestCms = await readCms();
+ latestCms.settings.agents = {
+ ...latestCms.settings.agents,
+ tass: {
+ processedLinks: Array.from(processedLinks)
+ }
+ };
+ await writeCms(latestCms);
+ await closeBrowser();
+ return { created, failed, skipped };
+};
+
+export { getTassFeedLinks as g, runTassAgent as r };
diff --git a/dist/server/entry.mjs b/dist/server/entry.mjs
new file mode 100644
index 0000000..1c16a2b
--- /dev/null
+++ b/dist/server/entry.mjs
@@ -0,0 +1,62 @@
+import { renderers } from './renderers.mjs';
+import { c as createExports, s as serverEntrypointModule } from './chunks/_@astrojs-ssr-adapter_BSED7qqF.mjs';
+import { manifest } from './manifest_Bsmp4TY0.mjs';
+
+const _page0 = () => import('./pages/_image.astro.mjs');
+const _page1 = () => import('./pages/admin/login.astro.mjs');
+const _page2 = () => import('./pages/admin.astro.mjs');
+const _page3 = () => import('./pages/api/admin/login.astro.mjs');
+const _page4 = () => import('./pages/api/admin/logout.astro.mjs');
+const _page5 = () => import('./pages/api/admin/save.astro.mjs');
+const _page6 = () => import('./pages/api/admin/upload.astro.mjs');
+const _page7 = () => import('./pages/api/agents/tass.astro.mjs');
+const _page8 = () => import('./pages/api/posts/publish.astro.mjs');
+const _page9 = () => import('./pages/categories/_category_.astro.mjs');
+const _page10 = () => import('./pages/posts/_slug_.astro.mjs');
+const _page11 = () => import('./pages/posts.astro.mjs');
+const _page12 = () => import('./pages/search.astro.mjs');
+const _page13 = () => import('./pages/_page_.astro.mjs');
+const _page14 = () => import('./pages/index.astro.mjs');
+
+const pageMap = new Map([
+ ["node_modules/astro/dist/assets/endpoint/node.js", _page0],
+ ["src/pages/admin/login.astro", _page1],
+ ["src/pages/admin/index.astro", _page2],
+ ["src/pages/api/admin/login.ts", _page3],
+ ["src/pages/api/admin/logout.ts", _page4],
+ ["src/pages/api/admin/save.ts", _page5],
+ ["src/pages/api/admin/upload.ts", _page6],
+ ["src/pages/api/agents/tass.ts", _page7],
+ ["src/pages/api/posts/publish.ts", _page8],
+ ["src/pages/categories/[category].astro", _page9],
+ ["src/pages/posts/[slug].astro", _page10],
+ ["src/pages/posts/index.astro", _page11],
+ ["src/pages/search/index.astro", _page12],
+ ["src/pages/[page].astro", _page13],
+ ["src/pages/index.astro", _page14]
+]);
+const serverIslandMap = new Map();
+const _manifest = Object.assign(manifest, {
+ pageMap,
+ serverIslandMap,
+ renderers,
+ middleware: () => import('./_noop-middleware.mjs')
+});
+const _args = {
+ "mode": "standalone",
+ "client": "file:///Users/drfelfel/ruseemrooz/dist/client/",
+ "server": "file:///Users/drfelfel/ruseemrooz/dist/server/",
+ "host": false,
+ "port": 4321,
+ "assets": "_astro"
+};
+const _exports = createExports(_manifest, _args);
+const handler = _exports['handler'];
+const startServer = _exports['startServer'];
+const options = _exports['options'];
+const _start = 'start';
+{
+ serverEntrypointModule[_start](_manifest, _args);
+}
+
+export { handler, options, pageMap, startServer };
diff --git a/dist/server/manifest_Bsmp4TY0.mjs b/dist/server/manifest_Bsmp4TY0.mjs
new file mode 100644
index 0000000..8d50eea
--- /dev/null
+++ b/dist/server/manifest_Bsmp4TY0.mjs
@@ -0,0 +1,101 @@
+import '@astrojs/internal-helpers/path';
+import 'cookie';
+import 'kleur/colors';
+import { N as NOOP_MIDDLEWARE_FN } from './chunks/astro-designed-error-pages_DsfhG0WT.mjs';
+import 'es-module-lexer';
+import { g as decodeKey } from './chunks/astro/server_DrnzQWw3.mjs';
+import 'clsx';
+
+function sanitizeParams(params) {
+ return Object.fromEntries(
+ Object.entries(params).map(([key, value]) => {
+ if (typeof value === "string") {
+ return [key, value.normalize().replace(/#/g, "%23").replace(/\?/g, "%3F")];
+ }
+ return [key, value];
+ })
+ );
+}
+function getParameter(part, params) {
+ if (part.spread) {
+ return params[part.content.slice(3)] || "";
+ }
+ if (part.dynamic) {
+ if (!params[part.content]) {
+ throw new TypeError(`Missing parameter: ${part.content}`);
+ }
+ return params[part.content];
+ }
+ return part.content.normalize().replace(/\?/g, "%3F").replace(/#/g, "%23").replace(/%5B/g, "[").replace(/%5D/g, "]");
+}
+function getSegment(segment, params) {
+ const segmentPath = segment.map((part) => getParameter(part, params)).join("");
+ return segmentPath ? "/" + segmentPath : "";
+}
+function getRouteGenerator(segments, addTrailingSlash) {
+ return (params) => {
+ const sanitizedParams = sanitizeParams(params);
+ let trailing = "";
+ if (addTrailingSlash === "always" && segments.length) {
+ trailing = "/";
+ }
+ const path = segments.map((segment) => getSegment(segment, sanitizedParams)).join("") + trailing;
+ return path || "/";
+ };
+}
+
+function deserializeRouteData(rawRouteData) {
+ return {
+ route: rawRouteData.route,
+ type: rawRouteData.type,
+ pattern: new RegExp(rawRouteData.pattern),
+ params: rawRouteData.params,
+ component: rawRouteData.component,
+ generate: getRouteGenerator(rawRouteData.segments, rawRouteData._meta.trailingSlash),
+ pathname: rawRouteData.pathname || void 0,
+ segments: rawRouteData.segments,
+ prerender: rawRouteData.prerender,
+ redirect: rawRouteData.redirect,
+ redirectRoute: rawRouteData.redirectRoute ? deserializeRouteData(rawRouteData.redirectRoute) : void 0,
+ fallbackRoutes: rawRouteData.fallbackRoutes.map((fallback) => {
+ return deserializeRouteData(fallback);
+ }),
+ isIndex: rawRouteData.isIndex
+ };
+}
+
+function deserializeManifest(serializedManifest) {
+ const routes = [];
+ for (const serializedRoute of serializedManifest.routes) {
+ routes.push({
+ ...serializedRoute,
+ routeData: deserializeRouteData(serializedRoute.routeData)
+ });
+ const route = serializedRoute;
+ route.routeData = deserializeRouteData(serializedRoute.routeData);
+ }
+ const assets = new Set(serializedManifest.assets);
+ const componentMetadata = new Map(serializedManifest.componentMetadata);
+ const inlinedScripts = new Map(serializedManifest.inlinedScripts);
+ const clientDirectives = new Map(serializedManifest.clientDirectives);
+ const serverIslandNameMap = new Map(serializedManifest.serverIslandNameMap);
+ const key = decodeKey(serializedManifest.key);
+ return {
+ // in case user middleware exists, this no-op middleware will be reassigned (see plugin-ssr.ts)
+ middleware() {
+ return { onRequest: NOOP_MIDDLEWARE_FN };
+ },
+ ...serializedManifest,
+ assets,
+ componentMetadata,
+ inlinedScripts,
+ clientDirectives,
+ routes,
+ serverIslandNameMap,
+ key
+ };
+}
+
+const manifest = deserializeManifest({"hrefRoot":"file:///Users/drfelfel/ruseemrooz/","adapterName":"@astrojs/node","routes":[{"file":"","links":[],"scripts":[],"styles":[],"routeData":{"type":"endpoint","isIndex":false,"route":"/_image","pattern":"^\\/_image$","segments":[[{"content":"_image","dynamic":false,"spread":false}]],"params":[],"component":"node_modules/astro/dist/assets/endpoint/node.js","pathname":"/_image","prerender":false,"fallbackRoutes":[],"_meta":{"trailingSlash":"ignore"}}},{"file":"","links":[],"scripts":[],"styles":[{"type":"external","src":"/_astro/_page_.RC7LoXVl.css"}],"routeData":{"route":"/admin/login","isIndex":false,"type":"page","pattern":"^\\/admin\\/login\\/?$","segments":[[{"content":"admin","dynamic":false,"spread":false}],[{"content":"login","dynamic":false,"spread":false}]],"params":[],"component":"src/pages/admin/login.astro","pathname":"/admin/login","prerender":false,"fallbackRoutes":[],"_meta":{"trailingSlash":"ignore"}}},{"file":"","links":[],"scripts":[{"type":"external","value":"/_astro/hoisted.ClNlXW51.js"}],"styles":[{"type":"external","src":"/_astro/_page_.RC7LoXVl.css"}],"routeData":{"route":"/admin","isIndex":true,"type":"page","pattern":"^\\/admin\\/?$","segments":[[{"content":"admin","dynamic":false,"spread":false}]],"params":[],"component":"src/pages/admin/index.astro","pathname":"/admin","prerender":false,"fallbackRoutes":[],"_meta":{"trailingSlash":"ignore"}}},{"file":"","links":[],"scripts":[],"styles":[],"routeData":{"route":"/api/admin/login","isIndex":false,"type":"endpoint","pattern":"^\\/api\\/admin\\/login\\/?$","segments":[[{"content":"api","dynamic":false,"spread":false}],[{"content":"admin","dynamic":false,"spread":false}],[{"content":"login","dynamic":false,"spread":false}]],"params":[],"component":"src/pages/api/admin/login.ts","pathname":"/api/admin/login","prerender":false,"fallbackRoutes":[],"_meta":{"trailingSlash":"ignore"}}},{"file":"","links":[],"scripts":[],"styles":[],"routeData":{"route":"/api/admin/logout","isIndex":false,"type":"endpoint","pattern":"^\\/api\\/admin\\/logout\\/?$","segments":[[{"content":"api","dynamic":false,"spread":false}],[{"content":"admin","dynamic":false,"spread":false}],[{"content":"logout","dynamic":false,"spread":false}]],"params":[],"component":"src/pages/api/admin/logout.ts","pathname":"/api/admin/logout","prerender":false,"fallbackRoutes":[],"_meta":{"trailingSlash":"ignore"}}},{"file":"","links":[],"scripts":[],"styles":[],"routeData":{"route":"/api/admin/save","isIndex":false,"type":"endpoint","pattern":"^\\/api\\/admin\\/save\\/?$","segments":[[{"content":"api","dynamic":false,"spread":false}],[{"content":"admin","dynamic":false,"spread":false}],[{"content":"save","dynamic":false,"spread":false}]],"params":[],"component":"src/pages/api/admin/save.ts","pathname":"/api/admin/save","prerender":false,"fallbackRoutes":[],"_meta":{"trailingSlash":"ignore"}}},{"file":"","links":[],"scripts":[],"styles":[],"routeData":{"route":"/api/admin/upload","isIndex":false,"type":"endpoint","pattern":"^\\/api\\/admin\\/upload\\/?$","segments":[[{"content":"api","dynamic":false,"spread":false}],[{"content":"admin","dynamic":false,"spread":false}],[{"content":"upload","dynamic":false,"spread":false}]],"params":[],"component":"src/pages/api/admin/upload.ts","pathname":"/api/admin/upload","prerender":false,"fallbackRoutes":[],"_meta":{"trailingSlash":"ignore"}}},{"file":"","links":[],"scripts":[],"styles":[],"routeData":{"route":"/api/agents/tass","isIndex":false,"type":"endpoint","pattern":"^\\/api\\/agents\\/tass\\/?$","segments":[[{"content":"api","dynamic":false,"spread":false}],[{"content":"agents","dynamic":false,"spread":false}],[{"content":"tass","dynamic":false,"spread":false}]],"params":[],"component":"src/pages/api/agents/tass.ts","pathname":"/api/agents/tass","prerender":false,"fallbackRoutes":[],"_meta":{"trailingSlash":"ignore"}}},{"file":"","links":[],"scripts":[],"styles":[],"routeData":{"route":"/api/posts/publish","isIndex":false,"type":"endpoint","pattern":"^\\/api\\/posts\\/publish\\/?$","segments":[[{"content":"api","dynamic":false,"spread":false}],[{"content":"posts","dynamic":false,"spread":false}],[{"content":"publish","dynamic":false,"spread":false}]],"params":[],"component":"src/pages/api/posts/publish.ts","pathname":"/api/posts/publish","prerender":false,"fallbackRoutes":[],"_meta":{"trailingSlash":"ignore"}}},{"file":"","links":[],"scripts":[],"styles":[{"type":"external","src":"/_astro/_page_.RC7LoXVl.css"}],"routeData":{"route":"/categories/[category]","isIndex":false,"type":"page","pattern":"^\\/categories\\/([^/]+?)\\/?$","segments":[[{"content":"categories","dynamic":false,"spread":false}],[{"content":"category","dynamic":true,"spread":false}]],"params":["category"],"component":"src/pages/categories/[category].astro","prerender":false,"fallbackRoutes":[],"_meta":{"trailingSlash":"ignore"}}},{"file":"","links":[],"scripts":[],"styles":[{"type":"external","src":"/_astro/_page_.RC7LoXVl.css"}],"routeData":{"route":"/posts/[slug]","isIndex":false,"type":"page","pattern":"^\\/posts\\/([^/]+?)\\/?$","segments":[[{"content":"posts","dynamic":false,"spread":false}],[{"content":"slug","dynamic":true,"spread":false}]],"params":["slug"],"component":"src/pages/posts/[slug].astro","prerender":false,"fallbackRoutes":[],"_meta":{"trailingSlash":"ignore"}}},{"file":"","links":[],"scripts":[],"styles":[{"type":"external","src":"/_astro/_page_.RC7LoXVl.css"}],"routeData":{"route":"/posts","isIndex":true,"type":"page","pattern":"^\\/posts\\/?$","segments":[[{"content":"posts","dynamic":false,"spread":false}]],"params":[],"component":"src/pages/posts/index.astro","pathname":"/posts","prerender":false,"fallbackRoutes":[],"_meta":{"trailingSlash":"ignore"}}},{"file":"","links":[],"scripts":[],"styles":[{"type":"external","src":"/_astro/_page_.RC7LoXVl.css"}],"routeData":{"route":"/search","isIndex":true,"type":"page","pattern":"^\\/search\\/?$","segments":[[{"content":"search","dynamic":false,"spread":false}]],"params":[],"component":"src/pages/search/index.astro","pathname":"/search","prerender":false,"fallbackRoutes":[],"_meta":{"trailingSlash":"ignore"}}},{"file":"","links":[],"scripts":[],"styles":[{"type":"external","src":"/_astro/_page_.RC7LoXVl.css"}],"routeData":{"route":"/[page]","isIndex":false,"type":"page","pattern":"^\\/([^/]+?)\\/?$","segments":[[{"content":"page","dynamic":true,"spread":false}]],"params":["page"],"component":"src/pages/[page].astro","prerender":false,"fallbackRoutes":[],"_meta":{"trailingSlash":"ignore"}}},{"file":"","links":[],"scripts":[],"styles":[{"type":"external","src":"/_astro/_page_.RC7LoXVl.css"}],"routeData":{"route":"/","isIndex":true,"type":"page","pattern":"^\\/$","segments":[],"params":[],"component":"src/pages/index.astro","pathname":"/","prerender":false,"fallbackRoutes":[],"_meta":{"trailingSlash":"ignore"}}}],"base":"/","trailingSlash":"ignore","compressHTML":true,"componentMetadata":[["/Users/drfelfel/ruseemrooz/src/pages/admin/login.astro",{"propagation":"none","containsHead":true}],["/Users/drfelfel/ruseemrooz/src/pages/[page].astro",{"propagation":"none","containsHead":true}],["/Users/drfelfel/ruseemrooz/src/pages/admin/index.astro",{"propagation":"none","containsHead":true}],["/Users/drfelfel/ruseemrooz/src/pages/categories/[category].astro",{"propagation":"none","containsHead":true}],["/Users/drfelfel/ruseemrooz/src/pages/index.astro",{"propagation":"none","containsHead":true}],["/Users/drfelfel/ruseemrooz/src/pages/posts/[slug].astro",{"propagation":"none","containsHead":true}],["/Users/drfelfel/ruseemrooz/src/pages/posts/index.astro",{"propagation":"none","containsHead":true}],["/Users/drfelfel/ruseemrooz/src/pages/search/index.astro",{"propagation":"none","containsHead":true}]],"renderers":[],"clientDirectives":[["idle","(()=>{var l=(o,t)=>{let i=async()=>{await(await o())()},e=typeof t.value==\"object\"?t.value:void 0,s={timeout:e==null?void 0:e.timeout};\"requestIdleCallback\"in window?window.requestIdleCallback(i,s):setTimeout(i,s.timeout||200)};(self.Astro||(self.Astro={})).idle=l;window.dispatchEvent(new Event(\"astro:idle\"));})();"],["load","(()=>{var e=async t=>{await(await t())()};(self.Astro||(self.Astro={})).load=e;window.dispatchEvent(new Event(\"astro:load\"));})();"],["media","(()=>{var s=(i,t)=>{let a=async()=>{await(await i())()};if(t.value){let e=matchMedia(t.value);e.matches?a():e.addEventListener(\"change\",a,{once:!0})}};(self.Astro||(self.Astro={})).media=s;window.dispatchEvent(new Event(\"astro:media\"));})();"],["only","(()=>{var e=async t=>{await(await t())()};(self.Astro||(self.Astro={})).only=e;window.dispatchEvent(new Event(\"astro:only\"));})();"],["visible","(()=>{var l=(s,i,o)=>{let r=async()=>{await(await s())()},t=typeof i.value==\"object\"?i.value:void 0,c={rootMargin:t==null?void 0:t.rootMargin},n=new IntersectionObserver(e=>{for(let a of e)if(a.isIntersecting){n.disconnect(),r();break}},c);for(let e of o.children)n.observe(e)};(self.Astro||(self.Astro={})).visible=l;window.dispatchEvent(new Event(\"astro:visible\"));})();"]],"entryModules":{"\u0000@astro-page:node_modules/astro/dist/assets/endpoint/node@_@js":"pages/_image.astro.mjs","\u0000@astro-page:src/pages/[page]@_@astro":"pages/_page_.astro.mjs","\u0000@astro-page:src/pages/admin/index@_@astro":"pages/admin.astro.mjs","\u0000@astro-page:src/pages/admin/login@_@astro":"pages/admin/login.astro.mjs","\u0000@astro-page:src/pages/api/admin/login@_@ts":"pages/api/admin/login.astro.mjs","\u0000@astro-page:src/pages/api/admin/logout@_@ts":"pages/api/admin/logout.astro.mjs","\u0000@astro-page:src/pages/api/admin/save@_@ts":"pages/api/admin/save.astro.mjs","\u0000@astro-page:src/pages/api/admin/upload@_@ts":"pages/api/admin/upload.astro.mjs","\u0000@astro-page:src/pages/api/agents/tass@_@ts":"pages/api/agents/tass.astro.mjs","\u0000@astro-page:src/pages/api/posts/publish@_@ts":"pages/api/posts/publish.astro.mjs","\u0000@astro-page:src/pages/categories/[category]@_@astro":"pages/categories/_category_.astro.mjs","\u0000@astro-page:src/pages/index@_@astro":"pages/index.astro.mjs","\u0000@astro-page:src/pages/posts/[slug]@_@astro":"pages/posts/_slug_.astro.mjs","\u0000@astro-page:src/pages/posts/index@_@astro":"pages/posts.astro.mjs","\u0000@astro-page:src/pages/search/index@_@astro":"pages/search.astro.mjs","\u0000@astrojs-ssr-virtual-entry":"entry.mjs","\u0000@astro-renderers":"renderers.mjs","\u0000noop-middleware":"_noop-middleware.mjs","\u0000@astrojs-ssr-adapter":"_@astrojs-ssr-adapter.mjs","/Users/drfelfel/ruseemrooz/node_modules/astro/dist/env/setup.js":"chunks/astro/env-setup_Cr6XTFvb.mjs","\u0000@astrojs-manifest":"manifest_Bsmp4TY0.mjs","/astro/hoisted.js?q=0":"_astro/hoisted.ClNlXW51.js","astro:scripts/before-hydration.js":""},"inlinedScripts":[],"assets":["/_astro/_page_.RC7LoXVl.css","/_astro/hoisted.ClNlXW51.js","/assets/logo.png","/uploads/0dfaf688-a3f5-4914-b0a5-14a720c212bf.webp","/uploads/10d5c9bf-849e-4ddd-a7d8-dd5dc6a2e666.webp","/uploads/15a3a254-12db-4851-a434-cae6e631838d.webp","/uploads/23ed9cea-411b-4169-a905-a40bd503d8c6.webp","/uploads/2d77aee7-e390-4ca2-8100-d0e6528c145a.webp","/uploads/3b043c9d-57bf-46f2-9fb2-9c8be53bbcf5.webp","/uploads/553d2a2a-b924-401e-988d-1db34a907355.webp","/uploads/70cc796b-ee3c-49c8-8939-d0fe0dde1554.webp","/uploads/7c59fcfa-93cc-4d1d-b2be-d491eb482302.webp","/uploads/9de7af87-a5f2-4299-bc4e-6c3a17ee9f4d.webp","/uploads/ad228b50-fe89-40a6-b17d-87440c24d6a5.webp","/uploads/af246b9a-cf64-43c2-934d-edb5d8f0b8ee.webp","/uploads/d0858db4-fcc3-43b4-b120-53cc41a4473c.webp","/uploads/d3a18a75-4a6f-48c6-b3a5-c10f6fe798d7.webp","/uploads/eaebf85a-c702-4eb6-ae0d-02a752761ea5.webp","/uploads/eb8a9dae-70da-4887-8a36-75f660ccd42a.webp","/uploads/f1b83771-f192-46f1-8629-6982d42c3b48.webp","/uploads/fcdcd26a-1029-46ec-adae-8013cbe48022.webp","/uploads/thumbs/0dfaf688-a3f5-4914-b0a5-14a720c212bf-thumb.webp","/uploads/thumbs/10d5c9bf-849e-4ddd-a7d8-dd5dc6a2e666-thumb.webp","/uploads/thumbs/15a3a254-12db-4851-a434-cae6e631838d-thumb.webp","/uploads/thumbs/23ed9cea-411b-4169-a905-a40bd503d8c6-thumb.webp","/uploads/thumbs/2d77aee7-e390-4ca2-8100-d0e6528c145a-thumb.webp","/uploads/thumbs/3b043c9d-57bf-46f2-9fb2-9c8be53bbcf5-thumb.webp","/uploads/thumbs/553d2a2a-b924-401e-988d-1db34a907355-thumb.webp","/uploads/thumbs/70cc796b-ee3c-49c8-8939-d0fe0dde1554-thumb.webp","/uploads/thumbs/7c59fcfa-93cc-4d1d-b2be-d491eb482302-thumb.webp","/uploads/thumbs/9de7af87-a5f2-4299-bc4e-6c3a17ee9f4d-thumb.webp","/uploads/thumbs/ad228b50-fe89-40a6-b17d-87440c24d6a5-thumb.webp","/uploads/thumbs/af246b9a-cf64-43c2-934d-edb5d8f0b8ee-thumb.webp","/uploads/thumbs/d0858db4-fcc3-43b4-b120-53cc41a4473c-thumb.webp","/uploads/thumbs/d3a18a75-4a6f-48c6-b3a5-c10f6fe798d7-thumb.webp","/uploads/thumbs/eaebf85a-c702-4eb6-ae0d-02a752761ea5-thumb.webp","/uploads/thumbs/eb8a9dae-70da-4887-8a36-75f660ccd42a-thumb.webp","/uploads/thumbs/f1b83771-f192-46f1-8629-6982d42c3b48-thumb.webp","/uploads/thumbs/fcdcd26a-1029-46ec-adae-8013cbe48022-thumb.webp"],"buildFormat":"directory","checkOrigin":false,"serverIslandNameMap":[],"key":"OhI/yogBBHSD6O6vam4wRfoY752RA0hpsC3pYM9PZfk=","experimentalEnvGetSecretEnabled":false});
+
+export { manifest };
diff --git a/dist/server/pages/_image.astro.mjs b/dist/server/pages/_image.astro.mjs
new file mode 100644
index 0000000..ca139ea
--- /dev/null
+++ b/dist/server/pages/_image.astro.mjs
@@ -0,0 +1,1151 @@
+import { readFile } from 'node:fs/promises';
+import { isAbsolute } from 'node:path';
+import { fileURLToPath } from 'node:url';
+import '@astrojs/internal-helpers/path';
+import { A as AstroError, f as NoImageMetadata, F as FailedToFetchRemoteImageDimensions, g as ExpectedImageOptions, h as ExpectedImage, i as ExpectedNotESMImage, r as resolveSrc, j as isRemoteImage, k as isCoreRemotePath, l as isESMImportedImage, m as isLocalService, D as DEFAULT_HASH_PROPS, n as InvalidImageService, o as ImageMissingAlt, p as isRemoteAllowed } from '../chunks/astro/assets-service_BZIICsh3.mjs';
+import { c as createComponent, m as maybeRenderHead, a as addAttribute, s as spreadAttributes, r as renderTemplate, b as createAstro } from '../chunks/astro/server_DrnzQWw3.mjs';
+import 'clsx';
+import * as mime from 'mrmime';
+export { renderers } from '../renderers.mjs';
+
+function isImageMetadata(src) {
+ return src.fsPath && !("fsPath" in src);
+}
+
+const decoder = new TextDecoder();
+const toUTF8String = (input, start = 0, end = input.length) => decoder.decode(input.slice(start, end));
+const toHexString = (input, start = 0, end = input.length) => input.slice(start, end).reduce((memo, i) => memo + ("0" + i.toString(16)).slice(-2), "");
+const readInt16LE = (input, offset = 0) => {
+ const val = input[offset] + input[offset + 1] * 2 ** 8;
+ return val | (val & 2 ** 15) * 131070;
+};
+const readUInt16BE = (input, offset = 0) => input[offset] * 2 ** 8 + input[offset + 1];
+const readUInt16LE = (input, offset = 0) => input[offset] + input[offset + 1] * 2 ** 8;
+const readUInt24LE = (input, offset = 0) => input[offset] + input[offset + 1] * 2 ** 8 + input[offset + 2] * 2 ** 16;
+const readInt32LE = (input, offset = 0) => input[offset] + input[offset + 1] * 2 ** 8 + input[offset + 2] * 2 ** 16 + (input[offset + 3] << 24);
+const readUInt32BE = (input, offset = 0) => input[offset] * 2 ** 24 + input[offset + 1] * 2 ** 16 + input[offset + 2] * 2 ** 8 + input[offset + 3];
+const readUInt32LE = (input, offset = 0) => input[offset] + input[offset + 1] * 2 ** 8 + input[offset + 2] * 2 ** 16 + input[offset + 3] * 2 ** 24;
+const methods = {
+ readUInt16BE,
+ readUInt16LE,
+ readUInt32BE,
+ readUInt32LE
+};
+function readUInt(input, bits, offset, isBigEndian) {
+ offset = offset || 0;
+ const endian = isBigEndian ? "BE" : "LE";
+ const methodName = "readUInt" + bits + endian;
+ return methods[methodName](input, offset);
+}
+function readBox(buffer, offset) {
+ if (buffer.length - offset < 4) return;
+ const boxSize = readUInt32BE(buffer, offset);
+ if (buffer.length - offset < boxSize) return;
+ return {
+ name: toUTF8String(buffer, 4 + offset, 8 + offset),
+ offset,
+ size: boxSize
+ };
+}
+function findBox(buffer, boxName, offset) {
+ while (offset < buffer.length) {
+ const box = readBox(buffer, offset);
+ if (!box) break;
+ if (box.name === boxName) return box;
+ offset += box.size;
+ }
+}
+
+const BMP = {
+ validate: (input) => toUTF8String(input, 0, 2) === "BM",
+ calculate: (input) => ({
+ height: Math.abs(readInt32LE(input, 22)),
+ width: readUInt32LE(input, 18)
+ })
+};
+
+const TYPE_ICON = 1;
+const SIZE_HEADER$1 = 2 + 2 + 2;
+const SIZE_IMAGE_ENTRY = 1 + 1 + 1 + 1 + 2 + 2 + 4 + 4;
+function getSizeFromOffset(input, offset) {
+ const value = input[offset];
+ return value === 0 ? 256 : value;
+}
+function getImageSize$1(input, imageIndex) {
+ const offset = SIZE_HEADER$1 + imageIndex * SIZE_IMAGE_ENTRY;
+ return {
+ height: getSizeFromOffset(input, offset + 1),
+ width: getSizeFromOffset(input, offset)
+ };
+}
+const ICO = {
+ validate(input) {
+ const reserved = readUInt16LE(input, 0);
+ const imageCount = readUInt16LE(input, 4);
+ if (reserved !== 0 || imageCount === 0) return false;
+ const imageType = readUInt16LE(input, 2);
+ return imageType === TYPE_ICON;
+ },
+ calculate(input) {
+ const nbImages = readUInt16LE(input, 4);
+ const imageSize = getImageSize$1(input, 0);
+ if (nbImages === 1) return imageSize;
+ const imgs = [imageSize];
+ for (let imageIndex = 1; imageIndex < nbImages; imageIndex += 1) {
+ imgs.push(getImageSize$1(input, imageIndex));
+ }
+ return {
+ height: imageSize.height,
+ images: imgs,
+ width: imageSize.width
+ };
+ }
+};
+
+const TYPE_CURSOR = 2;
+const CUR = {
+ validate(input) {
+ const reserved = readUInt16LE(input, 0);
+ const imageCount = readUInt16LE(input, 4);
+ if (reserved !== 0 || imageCount === 0) return false;
+ const imageType = readUInt16LE(input, 2);
+ return imageType === TYPE_CURSOR;
+ },
+ calculate: (input) => ICO.calculate(input)
+};
+
+const DDS = {
+ validate: (input) => readUInt32LE(input, 0) === 542327876,
+ calculate: (input) => ({
+ height: readUInt32LE(input, 12),
+ width: readUInt32LE(input, 16)
+ })
+};
+
+const gifRegexp = /^GIF8[79]a/;
+const GIF = {
+ validate: (input) => gifRegexp.test(toUTF8String(input, 0, 6)),
+ calculate: (input) => ({
+ height: readUInt16LE(input, 8),
+ width: readUInt16LE(input, 6)
+ })
+};
+
+const brandMap = {
+ avif: "avif",
+ mif1: "heif",
+ msf1: "heif",
+ // hief-sequence
+ heic: "heic",
+ heix: "heic",
+ hevc: "heic",
+ // heic-sequence
+ hevx: "heic"
+ // heic-sequence
+};
+function detectBrands(buffer, start, end) {
+ let brandsDetected = {};
+ for (let i = start; i <= end; i += 4) {
+ const brand = toUTF8String(buffer, i, i + 4);
+ if (brand in brandMap) {
+ brandsDetected[brand] = 1;
+ }
+ }
+ if ("avif" in brandsDetected) {
+ return "avif";
+ } else if ("heic" in brandsDetected || "heix" in brandsDetected || "hevc" in brandsDetected || "hevx" in brandsDetected) {
+ return "heic";
+ } else if ("mif1" in brandsDetected || "msf1" in brandsDetected) {
+ return "heif";
+ }
+}
+const HEIF = {
+ validate(buffer) {
+ const ftype = toUTF8String(buffer, 4, 8);
+ const brand = toUTF8String(buffer, 8, 12);
+ return "ftyp" === ftype && brand in brandMap;
+ },
+ calculate(buffer) {
+ const metaBox = findBox(buffer, "meta", 0);
+ const iprpBox = metaBox && findBox(buffer, "iprp", metaBox.offset + 12);
+ const ipcoBox = iprpBox && findBox(buffer, "ipco", iprpBox.offset + 8);
+ const ispeBox = ipcoBox && findBox(buffer, "ispe", ipcoBox.offset + 8);
+ if (ispeBox) {
+ return {
+ height: readUInt32BE(buffer, ispeBox.offset + 16),
+ width: readUInt32BE(buffer, ispeBox.offset + 12),
+ type: detectBrands(buffer, 8, metaBox.offset)
+ };
+ }
+ throw new TypeError("Invalid HEIF, no size found");
+ }
+};
+
+const SIZE_HEADER = 4 + 4;
+const FILE_LENGTH_OFFSET = 4;
+const ENTRY_LENGTH_OFFSET = 4;
+const ICON_TYPE_SIZE = {
+ ICON: 32,
+ "ICN#": 32,
+ // m => 16 x 16
+ "icm#": 16,
+ icm4: 16,
+ icm8: 16,
+ // s => 16 x 16
+ "ics#": 16,
+ ics4: 16,
+ ics8: 16,
+ is32: 16,
+ s8mk: 16,
+ icp4: 16,
+ // l => 32 x 32
+ icl4: 32,
+ icl8: 32,
+ il32: 32,
+ l8mk: 32,
+ icp5: 32,
+ ic11: 32,
+ // h => 48 x 48
+ ich4: 48,
+ ich8: 48,
+ ih32: 48,
+ h8mk: 48,
+ // . => 64 x 64
+ icp6: 64,
+ ic12: 32,
+ // t => 128 x 128
+ it32: 128,
+ t8mk: 128,
+ ic07: 128,
+ // . => 256 x 256
+ ic08: 256,
+ ic13: 256,
+ // . => 512 x 512
+ ic09: 512,
+ ic14: 512,
+ // . => 1024 x 1024
+ ic10: 1024
+};
+function readImageHeader(input, imageOffset) {
+ const imageLengthOffset = imageOffset + ENTRY_LENGTH_OFFSET;
+ return [
+ toUTF8String(input, imageOffset, imageLengthOffset),
+ readUInt32BE(input, imageLengthOffset)
+ ];
+}
+function getImageSize(type) {
+ const size = ICON_TYPE_SIZE[type];
+ return { width: size, height: size, type };
+}
+const ICNS = {
+ validate: (input) => toUTF8String(input, 0, 4) === "icns",
+ calculate(input) {
+ const inputLength = input.length;
+ const fileLength = readUInt32BE(input, FILE_LENGTH_OFFSET);
+ let imageOffset = SIZE_HEADER;
+ let imageHeader = readImageHeader(input, imageOffset);
+ let imageSize = getImageSize(imageHeader[0]);
+ imageOffset += imageHeader[1];
+ if (imageOffset === fileLength) return imageSize;
+ const result = {
+ height: imageSize.height,
+ images: [imageSize],
+ width: imageSize.width
+ };
+ while (imageOffset < fileLength && imageOffset < inputLength) {
+ imageHeader = readImageHeader(input, imageOffset);
+ imageSize = getImageSize(imageHeader[0]);
+ imageOffset += imageHeader[1];
+ result.images.push(imageSize);
+ }
+ return result;
+ }
+};
+
+const J2C = {
+ // TODO: this doesn't seem right. SIZ marker doesn't have to be right after the SOC
+ validate: (input) => toHexString(input, 0, 4) === "ff4fff51",
+ calculate: (input) => ({
+ height: readUInt32BE(input, 12),
+ width: readUInt32BE(input, 8)
+ })
+};
+
+const JP2 = {
+ validate(input) {
+ if (readUInt32BE(input, 4) !== 1783636e3 || readUInt32BE(input, 0) < 1) return false;
+ const ftypBox = findBox(input, "ftyp", 0);
+ if (!ftypBox) return false;
+ return readUInt32BE(input, ftypBox.offset + 4) === 1718909296;
+ },
+ calculate(input) {
+ const jp2hBox = findBox(input, "jp2h", 0);
+ const ihdrBox = jp2hBox && findBox(input, "ihdr", jp2hBox.offset + 8);
+ if (ihdrBox) {
+ return {
+ height: readUInt32BE(input, ihdrBox.offset + 8),
+ width: readUInt32BE(input, ihdrBox.offset + 12)
+ };
+ }
+ throw new TypeError("Unsupported JPEG 2000 format");
+ }
+};
+
+const EXIF_MARKER = "45786966";
+const APP1_DATA_SIZE_BYTES = 2;
+const EXIF_HEADER_BYTES = 6;
+const TIFF_BYTE_ALIGN_BYTES = 2;
+const BIG_ENDIAN_BYTE_ALIGN = "4d4d";
+const LITTLE_ENDIAN_BYTE_ALIGN = "4949";
+const IDF_ENTRY_BYTES = 12;
+const NUM_DIRECTORY_ENTRIES_BYTES = 2;
+function isEXIF(input) {
+ return toHexString(input, 2, 6) === EXIF_MARKER;
+}
+function extractSize(input, index) {
+ return {
+ height: readUInt16BE(input, index),
+ width: readUInt16BE(input, index + 2)
+ };
+}
+function extractOrientation(exifBlock, isBigEndian) {
+ const idfOffset = 8;
+ const offset = EXIF_HEADER_BYTES + idfOffset;
+ const idfDirectoryEntries = readUInt(exifBlock, 16, offset, isBigEndian);
+ for (let directoryEntryNumber = 0; directoryEntryNumber < idfDirectoryEntries; directoryEntryNumber++) {
+ const start = offset + NUM_DIRECTORY_ENTRIES_BYTES + directoryEntryNumber * IDF_ENTRY_BYTES;
+ const end = start + IDF_ENTRY_BYTES;
+ if (start > exifBlock.length) {
+ return;
+ }
+ const block = exifBlock.slice(start, end);
+ const tagNumber = readUInt(block, 16, 0, isBigEndian);
+ if (tagNumber === 274) {
+ const dataFormat = readUInt(block, 16, 2, isBigEndian);
+ if (dataFormat !== 3) {
+ return;
+ }
+ const numberOfComponents = readUInt(block, 32, 4, isBigEndian);
+ if (numberOfComponents !== 1) {
+ return;
+ }
+ return readUInt(block, 16, 8, isBigEndian);
+ }
+ }
+}
+function validateExifBlock(input, index) {
+ const exifBlock = input.slice(APP1_DATA_SIZE_BYTES, index);
+ const byteAlign = toHexString(
+ exifBlock,
+ EXIF_HEADER_BYTES,
+ EXIF_HEADER_BYTES + TIFF_BYTE_ALIGN_BYTES
+ );
+ const isBigEndian = byteAlign === BIG_ENDIAN_BYTE_ALIGN;
+ const isLittleEndian = byteAlign === LITTLE_ENDIAN_BYTE_ALIGN;
+ if (isBigEndian || isLittleEndian) {
+ return extractOrientation(exifBlock, isBigEndian);
+ }
+}
+function validateInput(input, index) {
+ if (index > input.length) {
+ throw new TypeError("Corrupt JPG, exceeded buffer limits");
+ }
+}
+const JPG = {
+ validate: (input) => toHexString(input, 0, 2) === "ffd8",
+ calculate(input) {
+ input = input.slice(4);
+ let orientation;
+ let next;
+ while (input.length) {
+ const i = readUInt16BE(input, 0);
+ if (input[i] !== 255) {
+ input = input.slice(i);
+ continue;
+ }
+ if (isEXIF(input)) {
+ orientation = validateExifBlock(input, i);
+ }
+ validateInput(input, i);
+ next = input[i + 1];
+ if (next === 192 || next === 193 || next === 194) {
+ const size = extractSize(input, i + 5);
+ if (!orientation) {
+ return size;
+ }
+ return {
+ height: size.height,
+ orientation,
+ width: size.width
+ };
+ }
+ input = input.slice(i + 2);
+ }
+ throw new TypeError("Invalid JPG, no size found");
+ }
+};
+
+const KTX = {
+ validate: (input) => {
+ const signature = toUTF8String(input, 1, 7);
+ return ["KTX 11", "KTX 20"].includes(signature);
+ },
+ calculate: (input) => {
+ const type = input[5] === 49 ? "ktx" : "ktx2";
+ const offset = type === "ktx" ? 36 : 20;
+ return {
+ height: readUInt32LE(input, offset + 4),
+ width: readUInt32LE(input, offset),
+ type
+ };
+ }
+};
+
+const pngSignature = "PNG\r\n\n";
+const pngImageHeaderChunkName = "IHDR";
+const pngFriedChunkName = "CgBI";
+const PNG = {
+ validate(input) {
+ if (pngSignature === toUTF8String(input, 1, 8)) {
+ let chunkName = toUTF8String(input, 12, 16);
+ if (chunkName === pngFriedChunkName) {
+ chunkName = toUTF8String(input, 28, 32);
+ }
+ if (chunkName !== pngImageHeaderChunkName) {
+ throw new TypeError("Invalid PNG");
+ }
+ return true;
+ }
+ return false;
+ },
+ calculate(input) {
+ if (toUTF8String(input, 12, 16) === pngFriedChunkName) {
+ return {
+ height: readUInt32BE(input, 36),
+ width: readUInt32BE(input, 32)
+ };
+ }
+ return {
+ height: readUInt32BE(input, 20),
+ width: readUInt32BE(input, 16)
+ };
+ }
+};
+
+const PNMTypes = {
+ P1: "pbm/ascii",
+ P2: "pgm/ascii",
+ P3: "ppm/ascii",
+ P4: "pbm",
+ P5: "pgm",
+ P6: "ppm",
+ P7: "pam",
+ PF: "pfm"
+};
+const handlers = {
+ default: (lines) => {
+ let dimensions = [];
+ while (lines.length > 0) {
+ const line = lines.shift();
+ if (line[0] === "#") {
+ continue;
+ }
+ dimensions = line.split(" ");
+ break;
+ }
+ if (dimensions.length === 2) {
+ return {
+ height: parseInt(dimensions[1], 10),
+ width: parseInt(dimensions[0], 10)
+ };
+ } else {
+ throw new TypeError("Invalid PNM");
+ }
+ },
+ pam: (lines) => {
+ const size = {};
+ while (lines.length > 0) {
+ const line = lines.shift();
+ if (line.length > 16 || line.charCodeAt(0) > 128) {
+ continue;
+ }
+ const [key, value] = line.split(" ");
+ if (key && value) {
+ size[key.toLowerCase()] = parseInt(value, 10);
+ }
+ if (size.height && size.width) {
+ break;
+ }
+ }
+ if (size.height && size.width) {
+ return {
+ height: size.height,
+ width: size.width
+ };
+ } else {
+ throw new TypeError("Invalid PAM");
+ }
+ }
+};
+const PNM = {
+ validate: (input) => toUTF8String(input, 0, 2) in PNMTypes,
+ calculate(input) {
+ const signature = toUTF8String(input, 0, 2);
+ const type = PNMTypes[signature];
+ const lines = toUTF8String(input, 3).split(/[\r\n]+/);
+ const handler = handlers[type] || handlers.default;
+ return handler(lines);
+ }
+};
+
+const PSD = {
+ validate: (input) => toUTF8String(input, 0, 4) === "8BPS",
+ calculate: (input) => ({
+ height: readUInt32BE(input, 14),
+ width: readUInt32BE(input, 18)
+ })
+};
+
+const svgReg = /