litestar-vite-plugin 0.24.1 → 0.26.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. package/README.md +6 -6
  2. package/dist/js/astro.d.ts +6 -0
  3. package/dist/js/astro.js +14 -77
  4. package/dist/js/dev-server-index.html +2 -2
  5. package/dist/js/helpers/csrf.d.ts +2 -0
  6. package/dist/js/helpers/csrf.js +28 -1
  7. package/dist/js/helpers/htmx.js +105 -2
  8. package/dist/js/index.d.ts +6 -0
  9. package/dist/js/index.js +75 -128
  10. package/dist/js/inertia-helpers/index.d.ts +27 -9
  11. package/dist/js/install-hint.d.ts +17 -0
  12. package/dist/js/install-hint.js +35 -1
  13. package/dist/js/nuxt.d.ts +6 -0
  14. package/dist/js/nuxt.js +12 -72
  15. package/dist/js/shared/bridge-schema.d.ts +1 -0
  16. package/dist/js/shared/bridge-schema.js +41 -9
  17. package/dist/js/shared/constants.d.ts +4 -18
  18. package/dist/js/shared/constants.js +2 -6
  19. package/dist/js/shared/emit-page-props-types.d.ts +1 -1
  20. package/dist/js/shared/emit-page-props-types.js +51 -5
  21. package/dist/js/shared/emit-schemas-types.d.ts +1 -1
  22. package/dist/js/shared/emit-schemas-types.js +86 -15
  23. package/dist/js/shared/emit-static-props-types.d.ts +1 -1
  24. package/dist/js/shared/emit-static-props-types.js +2 -2
  25. package/dist/js/shared/typegen-cache.d.ts +10 -11
  26. package/dist/js/shared/typegen-cache.js +10 -1
  27. package/dist/js/shared/typegen-core.d.ts +24 -0
  28. package/dist/js/shared/typegen-core.js +101 -45
  29. package/dist/js/shared/typegen-plugin.d.ts +26 -0
  30. package/dist/js/shared/typegen-plugin.js +191 -129
  31. package/dist/js/shared/vite-compat.d.ts +23 -15
  32. package/dist/js/shared/vite-compat.js +12 -6
  33. package/dist/js/sveltekit.d.ts +6 -0
  34. package/dist/js/sveltekit.js +14 -77
  35. package/package.json +14 -8
package/README.md CHANGED
@@ -43,12 +43,12 @@ litestar run --reload
43
43
 
44
44
  ## Documentation
45
45
 
46
- - **[Usage Guide](https://litestar-org.github.io/litestar-vite/latest/usage/)**: Core concepts, configuration, and type generation.
47
- - **[Inertia](https://litestar-org.github.io/litestar-vite/latest/inertia/)**: Fullstack protocols and SSR.
48
- - **[Frameworks](https://litestar-org.github.io/litestar-vite/latest/frameworks/)**: Guides for React, Vue, Svelte, Angular, Astro, and Nuxt.
49
- - **[Reference](https://litestar-org.github.io/litestar-vite/latest/reference/)**: API and CLI documentation.
46
+ - **[Usage Guide](https://litestar-org.github.io/litestar-vite/usage/index.html)**: Core concepts, configuration, and type generation.
47
+ - **[Inertia](https://litestar-org.github.io/litestar-vite/frameworks/inertia/index.html)**: Fullstack protocols and SSR.
48
+ - **[Frameworks](https://litestar-org.github.io/litestar-vite/frameworks/index.html)**: Guides for React, Vue, Svelte, Angular, Astro, and Nuxt.
49
+ - **[Reference](https://litestar-org.github.io/litestar-vite/reference/index.html)**: API and CLI documentation.
50
50
 
51
- For a full list of changes, see the [Changelog](https://litestar-org.github.io/litestar-vite/latest/changelog.html).
51
+ For a full list of changes, see the [Changelog](https://litestar-org.github.io/litestar-vite/changelog.html).
52
52
 
53
53
  ## Common Commands
54
54
 
@@ -76,7 +76,7 @@ litestar-vite now defaults Inertia apps to script-element bootstrap. Inertia v3
76
76
 
77
77
  ## Links
78
78
 
79
- - Docs: <https://litestar-org.github.io/litestar-vite/latest/>
79
+ - Docs: <https://litestar-org.github.io/litestar-vite/>
80
80
  - Examples: `examples/` (React, Vue, Svelte, HTMX, Inertia, Astro, Nuxt, SvelteKit, Angular)
81
81
  - Real-world example: [litestar-fullstack](https://github.com/litestar-org/litestar-fullstack)
82
82
  - Issues: <https://github.com/litestar-org/litestar-vite/issues/>
@@ -170,6 +170,12 @@ export interface AstroTypesConfig {
170
170
  * @default false
171
171
  */
172
172
  globalRoute?: boolean;
173
+ /**
174
+ * Fail Vite when type generation fails.
175
+ *
176
+ * Defaults to true during build and false during dev.
177
+ */
178
+ failOnError?: boolean;
173
179
  /**
174
180
  * Debounce time in milliseconds for type regeneration.
175
181
  *
package/dist/js/astro.js CHANGED
@@ -1,9 +1,9 @@
1
1
  import fs from "node:fs";
2
2
  import path from "node:path";
3
3
  import { readBridgeConfig } from "./shared/bridge-schema.js";
4
- import { DEBOUNCE_MS } from "./shared/constants.js";
5
4
  import { normalizeHost, resolveHotFilePath, resolveLitestarPort } from "./shared/network.js";
6
- import { createLitestarTypeGenPlugin } from "./shared/typegen-plugin.js";
5
+ import { createLitestarTypeGenPlugin, resolveTypesConfig } from "./shared/typegen-plugin.js";
6
+ import { hmrServerConfig } from "./shared/vite-compat.js";
7
7
  function resolveConfig(config = {}) {
8
8
  let hotFile;
9
9
  let proxyMode = "vite";
@@ -37,74 +37,13 @@ function resolveConfig(config = {}) {
37
37
  if (resolvedLitestarPort !== null) {
38
38
  litestarPort = resolvedLitestarPort;
39
39
  }
40
- let typesConfig = false;
41
- const defaultTypesOutput = "src/generated";
42
- const buildTypeDefaults = (output) => ({
43
- openapiPath: path.join(output, "openapi.json"),
44
- routesPath: path.join(output, "routes.json"),
45
- pagePropsPath: path.join(output, "inertia-pages.json"),
46
- schemasTsPath: path.join(output, "schemas.ts")
40
+ const typesConfig = resolveTypesConfig({
41
+ requested: config.types,
42
+ pythonConfig: pythonTypesConfig ?? void 0,
43
+ defaultOutput: "src/generated",
44
+ mergePythonWhenTrue: true,
45
+ mergePythonForObject: true
47
46
  });
48
- if (config.types === true) {
49
- const output = pythonTypesConfig?.output ?? defaultTypesOutput;
50
- const defaults = buildTypeDefaults(output);
51
- typesConfig = {
52
- enabled: true,
53
- output,
54
- openapiPath: pythonTypesConfig?.openapiPath ?? defaults.openapiPath,
55
- routesPath: pythonTypesConfig?.routesPath ?? defaults.routesPath,
56
- pagePropsPath: pythonTypesConfig?.pagePropsPath ?? defaults.pagePropsPath,
57
- schemasTsPath: pythonTypesConfig?.schemasTsPath ?? defaults.schemasTsPath,
58
- generateZod: pythonTypesConfig?.generateZod ?? false,
59
- generateSdk: pythonTypesConfig?.generateSdk ?? true,
60
- generateRoutes: pythonTypesConfig?.generateRoutes ?? true,
61
- generatePageProps: pythonTypesConfig?.generatePageProps ?? true,
62
- generateSchemas: pythonTypesConfig?.generateSchemas ?? true,
63
- globalRoute: pythonTypesConfig?.globalRoute ?? false,
64
- debounce: DEBOUNCE_MS
65
- };
66
- } else if (typeof config.types === "object" && config.types !== null) {
67
- const userProvidedOutput = Object.hasOwn(config.types, "output");
68
- const output = config.types.output ?? pythonTypesConfig?.output ?? defaultTypesOutput;
69
- const defaults = buildTypeDefaults(output);
70
- const openapiFallback = userProvidedOutput ? defaults.openapiPath : pythonTypesConfig?.openapiPath ?? defaults.openapiPath;
71
- const routesFallback = userProvidedOutput ? defaults.routesPath : pythonTypesConfig?.routesPath ?? defaults.routesPath;
72
- const pagePropsFallback = userProvidedOutput ? defaults.pagePropsPath : pythonTypesConfig?.pagePropsPath ?? defaults.pagePropsPath;
73
- const schemasFallback = userProvidedOutput ? defaults.schemasTsPath : pythonTypesConfig?.schemasTsPath ?? defaults.schemasTsPath;
74
- typesConfig = {
75
- enabled: config.types.enabled ?? true,
76
- output,
77
- openapiPath: config.types.openapiPath ?? openapiFallback,
78
- routesPath: config.types.routesPath ?? routesFallback,
79
- pagePropsPath: config.types.pagePropsPath ?? pagePropsFallback,
80
- schemasTsPath: config.types.schemasTsPath ?? schemasFallback,
81
- generateZod: config.types.generateZod ?? pythonTypesConfig?.generateZod ?? false,
82
- generateSdk: config.types.generateSdk ?? pythonTypesConfig?.generateSdk ?? true,
83
- generateRoutes: config.types.generateRoutes ?? pythonTypesConfig?.generateRoutes ?? true,
84
- generatePageProps: config.types.generatePageProps ?? pythonTypesConfig?.generatePageProps ?? true,
85
- generateSchemas: config.types.generateSchemas ?? pythonTypesConfig?.generateSchemas ?? true,
86
- globalRoute: config.types.globalRoute ?? pythonTypesConfig?.globalRoute ?? false,
87
- debounce: config.types.debounce ?? DEBOUNCE_MS
88
- };
89
- } else if (config.types !== false && pythonTypesConfig?.enabled) {
90
- const output = pythonTypesConfig.output ?? defaultTypesOutput;
91
- const defaults = buildTypeDefaults(output);
92
- typesConfig = {
93
- enabled: true,
94
- output,
95
- openapiPath: pythonTypesConfig.openapiPath ?? defaults.openapiPath,
96
- routesPath: pythonTypesConfig.routesPath ?? defaults.routesPath,
97
- pagePropsPath: pythonTypesConfig.pagePropsPath ?? defaults.pagePropsPath,
98
- schemasTsPath: pythonTypesConfig.schemasTsPath ?? defaults.schemasTsPath,
99
- generateZod: pythonTypesConfig.generateZod ?? false,
100
- generateSdk: pythonTypesConfig.generateSdk ?? true,
101
- generateRoutes: pythonTypesConfig.generateRoutes ?? true,
102
- generatePageProps: pythonTypesConfig.generatePageProps ?? true,
103
- generateSchemas: pythonTypesConfig.generateSchemas ?? true,
104
- globalRoute: pythonTypesConfig.globalRoute ?? false,
105
- debounce: DEBOUNCE_MS
106
- };
107
- }
108
47
  return {
109
48
  apiProxy: config.apiProxy ?? "http://localhost:8000",
110
49
  apiPrefix: config.apiPrefix ?? "/api",
@@ -136,14 +75,12 @@ function createProxyPlugin(config) {
136
75
  strictPort: true
137
76
  } : {},
138
77
  // Route HMR through the Litestar port so DevTools never sees the framework port.
139
- ...config.litestarPort !== void 0 ? {
140
- hmr: {
141
- protocol: "ws",
142
- host: "127.0.0.1",
143
- clientPort: config.litestarPort,
144
- path: hmrPath
145
- }
146
- } : {},
78
+ ...config.litestarPort !== void 0 ? hmrServerConfig({
79
+ protocol: "ws",
80
+ host: "127.0.0.1",
81
+ clientPort: config.litestarPort,
82
+ path: hmrPath
83
+ }) : {},
147
84
  proxy: {
148
85
  [config.apiPrefix]: {
149
86
  target: config.apiProxy,
@@ -7,8 +7,8 @@
7
7
  <link rel="preconnect" href="https://fonts.googleapis.com" />
8
8
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
9
9
  <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap" />
10
- <style rel="stylesheet" crossorigin>/*! tailwindcss v4.3.0 | MIT License | https://tailwindcss.com */
11
- @layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-space-y-reverse:0;--tw-border-style:solid;--tw-leading:initial;--tw-font-weight:initial;--tw-tracking:initial;--tw-duration:initial;--tw-translate-x:0;--tw-translate-y:0;--tw-translate-z:0}}}@layer theme{:root,:host{--font-sans:"Inter", system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;--font-mono:"JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;--spacing:.25rem;--container-lg:32rem;--text-xs:.75rem;--text-xs--line-height:calc(1 / .75);--text-sm:.875rem;--text-sm--line-height:calc(1.25 / .875);--text-base:1rem;--text-base--line-height:calc(1.5 / 1);--text-xl:1.25rem;--text-xl--line-height:calc(1.75 / 1.25);--text-2xl:1.5rem;--text-2xl--line-height:calc(2 / 1.5);--text-4xl:2.25rem;--text-4xl--line-height:calc(2.5 / 2.25);--font-weight-light:300;--font-weight-medium:500;--font-weight-semibold:600;--tracking-widest:.1em;--leading-relaxed:1.625;--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4, 0, .2, 1);--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono);--color-navy:#202235;--color-navy-surface:#2a2d40;--color-navy-surface-variant:#1a1c2e;--color-gold:#edb641;--color-gold-light:#ffd480;--color-on-surface:#f8fafc;--color-on-surface-muted:#cbd5e1;--color-on-surface-subtle:#94a3b8;--color-outline:#334155;--color-success:#4ade80;--radius-brand:8px;--radius-brand-lg:12px;--shadow-text-glow-dark:0 0 24px #edb64138}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;tab-size:4;line-height:1.5;font-family:var(--default-font-family,ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring{outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-intrinsic-size:1px){::placeholder{color:currentColor}@supports (color:color-mix(in lab, red, red)){::placeholder{color:color-mix(in oklab, currentcolor 50%, transparent)}}}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block:0}::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-day-field{padding-block:0}::-webkit-datetime-edit-hour-field{padding-block:0}::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-millisecond-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}::-webkit-calendar-picker-indicator{line-height:1}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){appearance:button}::file-selector-button{appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}}@layer components;@layer utilities{.absolute{position:absolute}.relative{position:relative}.mt-8{margin-top:calc(var(--spacing) * 8)}.mb-3{margin-bottom:calc(var(--spacing) * 3)}.mb-4{margin-bottom:calc(var(--spacing) * 4)}.mb-10{margin-bottom:calc(var(--spacing) * 10)}.mb-12{margin-bottom:calc(var(--spacing) * 12)}.ml-auto{margin-left:auto}.block{display:block}.flex{display:flex}.inline{display:inline}.inline-flex{display:inline-flex}.h-2\.5{height:calc(var(--spacing) * 2.5)}.h-3\.5{height:calc(var(--spacing) * 3.5)}.h-12{height:calc(var(--spacing) * 12)}.h-14{height:calc(var(--spacing) * 14)}.h-full{height:100%}.min-h-screen{min-height:100vh}.w-2\.5{width:calc(var(--spacing) * 2.5)}.w-3\.5{width:calc(var(--spacing) * 3.5)}.w-auto{width:auto}.w-full{width:100%}.max-w-lg{max-width:var(--container-lg)}.min-w-0{min-width:calc(var(--spacing) * 0)}.flex-1{flex:1}.flex-none{flex:none}.animate-pulse-dot{animation:2s ease-in-out infinite pulse-dot}.items-center{align-items:center}.justify-center{justify-content:center}.gap-2{gap:calc(var(--spacing) * 2)}.gap-3{gap:calc(var(--spacing) * 3)}.gap-6{gap:calc(var(--spacing) * 6)}:where(.space-y-8>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 8) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 8) * calc(1 - var(--tw-space-y-reverse)))}.rounded-brand{border-radius:var(--radius-brand)}.rounded-brand-lg{border-radius:var(--radius-brand-lg)}.rounded-full{border-radius:3.40282e38px}.border{border-style:var(--tw-border-style);border-width:1px}.border-b{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.border-l-\[3px\]{border-left-style:var(--tw-border-style);border-left-width:3px}.border-outline{border-color:var(--color-outline)}.border-l-gold{border-left-color:var(--color-gold)}.bg-navy-surface{background-color:var(--color-navy-surface)}.bg-navy-surface-variant{background-color:var(--color-navy-surface-variant)}.bg-success{background-color:var(--color-success)}.px-4{padding-inline:calc(var(--spacing) * 4)}.px-6{padding-inline:calc(var(--spacing) * 6)}.px-8{padding-inline:calc(var(--spacing) * 8)}.py-3{padding-block:calc(var(--spacing) * 3)}.py-5{padding-block:calc(var(--spacing) * 5)}.py-8{padding-block:calc(var(--spacing) * 8)}.py-16{padding-block:calc(var(--spacing) * 16)}.text-center{text-align:center}.font-mono{font-family:var(--font-mono)}.text-2xl{font-size:var(--text-2xl);line-height:var(--tw-leading,var(--text-2xl--line-height))}.text-base{font-size:var(--text-base);line-height:var(--tw-leading,var(--text-base--line-height))}.text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.text-xl{font-size:var(--text-xl);line-height:var(--tw-leading,var(--text-xl--line-height))}.text-xs{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}.leading-none{--tw-leading:1;line-height:1}.leading-relaxed{--tw-leading:var(--leading-relaxed);line-height:var(--leading-relaxed)}.font-light{--tw-font-weight:var(--font-weight-light);font-weight:var(--font-weight-light)}.font-medium{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.font-semibold{--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold)}.tracking-widest{--tw-tracking:var(--tracking-widest);letter-spacing:var(--tracking-widest)}.break-all{word-break:break-all}.text-gold{color:var(--color-gold)}.text-on-surface{color:var(--color-on-surface)}.text-on-surface-muted{color:var(--color-on-surface-muted)}.text-on-surface-subtle{color:var(--color-on-surface-subtle)}.uppercase{text-transform:uppercase}.opacity-60{opacity:.6}.transition-colors{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-opacity{transition-property:opacity;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-transform{transition-property:transform,translate,scale,rotate;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.duration-200{--tw-duration:.2s;transition-duration:.2s}.select-all{-webkit-user-select:all;user-select:all}.text-brand-glow{text-shadow:var(--shadow-text-glow-dark)}@media (hover:hover){.group-hover\:translate-x-0\.5:is(:where(.group):hover *){--tw-translate-x:calc(var(--spacing) * .5);translate:var(--tw-translate-x) var(--tw-translate-y)}.group-hover\:opacity-100:is(:where(.group):hover *){opacity:1}.hover\:bg-navy:hover{background-color:var(--color-navy)}.hover\:text-gold:hover{color:var(--color-gold)}.hover\:text-gold-light:hover{color:var(--color-gold-light)}.hover\:opacity-80:hover{opacity:.8}}@media (width>=40rem){.sm\:px-10{padding-inline:calc(var(--spacing) * 10)}.sm\:py-10{padding-block:calc(var(--spacing) * 10)}.sm\:text-4xl{font-size:var(--text-4xl);line-height:var(--tw-leading,var(--text-4xl--line-height))}}}html{font-family:var(--font-sans);-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}body{background-color:var(--color-navy);color:var(--color-on-surface-muted);background-image:radial-gradient(#f8fafc0a 1px,#0000 1px);background-size:24px 24px}@keyframes pulse-dot{0%,to{opacity:1;transform:scale(1)}50%{opacity:.55;transform:scale(.92)}}@property --tw-space-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-leading{syntax:"*";inherits:false}@property --tw-font-weight{syntax:"*";inherits:false}@property --tw-tracking{syntax:"*";inherits:false}@property --tw-duration{syntax:"*";inherits:false}@property --tw-translate-x{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-y{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-z{syntax:"*";inherits:false;initial-value:0}
10
+ <style rel="stylesheet" crossorigin>/*! tailwindcss v4.3.2 | MIT License | https://tailwindcss.com */
11
+ @layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-space-y-reverse:0;--tw-border-style:solid;--tw-leading:initial;--tw-font-weight:initial;--tw-tracking:initial;--tw-duration:initial;--tw-translate-x:0;--tw-translate-y:0;--tw-translate-z:0}}}@layer theme{:root,:host{--font-sans:"Inter", system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;--font-mono:"JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;--spacing:.25rem;--container-lg:32rem;--text-xs:.75rem;--text-xs--line-height:calc(1 / .75);--text-sm:.875rem;--text-sm--line-height:calc(1.25 / .875);--text-base:1rem;--text-base--line-height:calc(1.5 / 1);--text-xl:1.25rem;--text-xl--line-height:calc(1.75 / 1.25);--text-2xl:1.5rem;--text-2xl--line-height:calc(2 / 1.5);--text-4xl:2.25rem;--text-4xl--line-height:calc(2.5 / 2.25);--font-weight-light:300;--font-weight-medium:500;--font-weight-semibold:600;--tracking-widest:.1em;--leading-relaxed:1.625;--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4, 0, .2, 1);--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono);--color-navy:#202235;--color-navy-surface:#2a2d40;--color-navy-surface-variant:#1a1c2e;--color-gold:#edb641;--color-gold-light:#ffd480;--color-on-surface:#f8fafc;--color-on-surface-muted:#cbd5e1;--color-on-surface-subtle:#94a3b8;--color-outline:#334155;--color-success:#4ade80;--radius-brand:8px;--radius-brand-lg:12px;--shadow-text-glow-dark:0 0 24px #edb64138}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;tab-size:4;line-height:1.5;font-family:var(--default-font-family,ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring{outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-intrinsic-size:1px){::placeholder{color:currentColor}@supports (color:color-mix(in lab, red, red)){::placeholder{color:color-mix(in oklab, currentcolor 50%, transparent)}}}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block:0}::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-day-field{padding-block:0}::-webkit-datetime-edit-hour-field{padding-block:0}::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-millisecond-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}::-webkit-calendar-picker-indicator{line-height:1}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){appearance:button}::file-selector-button{appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}}@layer components;@layer utilities{.absolute{position:absolute}.relative{position:relative}.mt-8{margin-top:calc(var(--spacing) * 8)}.mb-3{margin-bottom:calc(var(--spacing) * 3)}.mb-4{margin-bottom:calc(var(--spacing) * 4)}.mb-10{margin-bottom:calc(var(--spacing) * 10)}.mb-12{margin-bottom:calc(var(--spacing) * 12)}.ml-auto{margin-left:auto}.block{display:block}.flex{display:flex}.inline{display:inline}.inline-flex{display:inline-flex}.h-2\.5{height:calc(var(--spacing) * 2.5)}.h-3\.5{height:calc(var(--spacing) * 3.5)}.h-12{height:calc(var(--spacing) * 12)}.h-14{height:calc(var(--spacing) * 14)}.h-full{height:100%}.min-h-screen{min-height:100vh}.w-2\.5{width:calc(var(--spacing) * 2.5)}.w-3\.5{width:calc(var(--spacing) * 3.5)}.w-auto{width:auto}.w-full{width:100%}.max-w-lg{max-width:var(--container-lg)}.min-w-0{min-width:0}.flex-1{flex:1}.flex-none{flex:none}.animate-pulse-dot{animation:2s ease-in-out infinite pulse-dot}.items-center{align-items:center}.justify-center{justify-content:center}.gap-2{gap:calc(var(--spacing) * 2)}.gap-3{gap:calc(var(--spacing) * 3)}.gap-6{gap:calc(var(--spacing) * 6)}:where(.space-y-8>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 8) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 8) * calc(1 - var(--tw-space-y-reverse)))}.rounded-brand{border-radius:var(--radius-brand)}.rounded-brand-lg{border-radius:var(--radius-brand-lg)}.rounded-full{border-radius:3.40282e38px}.border{border-style:var(--tw-border-style);border-width:1px}.border-b{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.border-l-\[3px\]{border-left-style:var(--tw-border-style);border-left-width:3px}.border-outline{border-color:var(--color-outline)}.border-l-gold{border-left-color:var(--color-gold)}.bg-navy-surface{background-color:var(--color-navy-surface)}.bg-navy-surface-variant{background-color:var(--color-navy-surface-variant)}.bg-success{background-color:var(--color-success)}.px-4{padding-inline:calc(var(--spacing) * 4)}.px-6{padding-inline:calc(var(--spacing) * 6)}.px-8{padding-inline:calc(var(--spacing) * 8)}.py-3{padding-block:calc(var(--spacing) * 3)}.py-5{padding-block:calc(var(--spacing) * 5)}.py-8{padding-block:calc(var(--spacing) * 8)}.py-16{padding-block:calc(var(--spacing) * 16)}.text-center{text-align:center}.font-mono{font-family:var(--font-mono)}.text-2xl{font-size:var(--text-2xl);line-height:var(--tw-leading,var(--text-2xl--line-height))}.text-base{font-size:var(--text-base);line-height:var(--tw-leading,var(--text-base--line-height))}.text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.text-xl{font-size:var(--text-xl);line-height:var(--tw-leading,var(--text-xl--line-height))}.text-xs{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}.leading-none{--tw-leading:1;line-height:1}.leading-relaxed{--tw-leading:var(--leading-relaxed);line-height:var(--leading-relaxed)}.font-light{--tw-font-weight:var(--font-weight-light);font-weight:var(--font-weight-light)}.font-medium{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.font-semibold{--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold)}.tracking-widest{--tw-tracking:var(--tracking-widest);letter-spacing:var(--tracking-widest)}.break-all{word-break:break-all}.text-gold{color:var(--color-gold)}.text-on-surface{color:var(--color-on-surface)}.text-on-surface-muted{color:var(--color-on-surface-muted)}.text-on-surface-subtle{color:var(--color-on-surface-subtle)}.uppercase{text-transform:uppercase}.opacity-60{opacity:.6}.transition-colors{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-opacity{transition-property:opacity;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-transform{transition-property:transform,translate,scale,rotate;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.duration-200{--tw-duration:.2s;transition-duration:.2s}.select-all{-webkit-user-select:all;user-select:all}.text-brand-glow{text-shadow:var(--shadow-text-glow-dark)}@media (hover:hover){.group-hover\:translate-x-0\.5:is(:where(.group):hover *){--tw-translate-x:calc(var(--spacing) * .5);translate:var(--tw-translate-x) var(--tw-translate-y)}.group-hover\:opacity-100:is(:where(.group):hover *){opacity:1}.hover\:bg-navy:hover{background-color:var(--color-navy)}.hover\:text-gold:hover{color:var(--color-gold)}.hover\:text-gold-light:hover{color:var(--color-gold-light)}.hover\:opacity-80:hover{opacity:.8}}@media (width>=40rem){.sm\:px-10{padding-inline:calc(var(--spacing) * 10)}.sm\:py-10{padding-block:calc(var(--spacing) * 10)}.sm\:text-4xl{font-size:var(--text-4xl);line-height:var(--tw-leading,var(--text-4xl--line-height))}}}html{font-family:var(--font-sans);-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}body{background-color:var(--color-navy);color:var(--color-on-surface-muted);background-image:radial-gradient(#f8fafc0a 1px,#0000 1px);background-size:24px 24px}@keyframes pulse-dot{0%,to{opacity:1;transform:scale(1)}50%{opacity:.55;transform:scale(.92)}}@property --tw-space-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-leading{syntax:"*";inherits:false}@property --tw-font-weight{syntax:"*";inherits:false}@property --tw-tracking{syntax:"*";inherits:false}@property --tw-duration{syntax:"*";inherits:false}@property --tw-translate-x{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-y{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-z{syntax:"*";inherits:false;initial-value:0}
12
12
  /*$vite$:1*/</style>
13
13
  </head>
14
14
  <body class="flex min-h-screen items-center justify-center px-6 py-16">
@@ -5,6 +5,7 @@
5
5
  * 1. window.__LITESTAR_CSRF__ (SPA mode)
6
6
  * 2. <meta name="csrf-token" content="..."> (template mode)
7
7
  * 3. Inertia shared props (Inertia mode)
8
+ * 4. csrftoken / XSRF-TOKEN cookie (SPA fallback)
8
9
  *
9
10
  * @module
10
11
  */
@@ -21,6 +22,7 @@ export declare const DEFAULT_CSRF_HEADER_NAME = "X-CSRFToken";
21
22
  * 1. window.__LITESTAR_CSRF__ (injected by SPA handler)
22
23
  * 2. <meta name="csrf-token"> element
23
24
  * 3. Inertia page props (if Inertia is present)
25
+ * 4. csrftoken / XSRF-TOKEN cookie
24
26
  *
25
27
  * @returns The CSRF token or empty string if not found
26
28
  *
@@ -5,6 +5,7 @@
5
5
  * 1. window.__LITESTAR_CSRF__ (SPA mode)
6
6
  * 2. <meta name="csrf-token" content="..."> (template mode)
7
7
  * 3. Inertia shared props (Inertia mode)
8
+ * 4. csrftoken / XSRF-TOKEN cookie (SPA fallback)
8
9
  *
9
10
  * @module
10
11
  */
@@ -42,6 +43,28 @@ function getInertiaToken() {
42
43
  }
43
44
  return undefined;
44
45
  }
46
+ function getCookie(name) {
47
+ if (typeof document === "undefined" || typeof document.cookie !== "string") {
48
+ return undefined;
49
+ }
50
+ const value = `; ${document.cookie}`;
51
+ const parts = value.split(`; ${name}=`);
52
+ if (parts.length === 2) {
53
+ const raw = parts.pop()?.split(";").shift();
54
+ if (raw !== undefined && raw.length > 0) {
55
+ try {
56
+ return decodeURIComponent(raw);
57
+ }
58
+ catch {
59
+ return raw;
60
+ }
61
+ }
62
+ }
63
+ return undefined;
64
+ }
65
+ function getCookieToken() {
66
+ return getCookie("csrftoken") ?? getCookie("XSRF-TOKEN");
67
+ }
45
68
  /**
46
69
  * Get the CSRF token from the page.
47
70
  *
@@ -49,6 +72,7 @@ function getInertiaToken() {
49
72
  * 1. window.__LITESTAR_CSRF__ (injected by SPA handler)
50
73
  * 2. <meta name="csrf-token"> element
51
74
  * 3. Inertia page props (if Inertia is present)
75
+ * 4. csrftoken / XSRF-TOKEN cookie
52
76
  *
53
77
  * @returns The CSRF token or empty string if not found
54
78
  *
@@ -79,11 +103,13 @@ export function getCsrfToken() {
79
103
  }
80
104
  const metaToken = getMetaToken();
81
105
  const inertiaToken = getInertiaToken();
82
- const token = metaToken ?? inertiaToken ?? "";
106
+ const cookieToken = getCookieToken();
107
+ const token = metaToken ?? inertiaToken ?? cookieToken ?? "";
83
108
  if (csrfTokenCache &&
84
109
  csrfTokenCache.windowToken === undefined &&
85
110
  csrfTokenCache.metaToken === metaToken &&
86
111
  csrfTokenCache.inertiaToken === inertiaToken &&
112
+ csrfTokenCache.cookieToken === cookieToken &&
87
113
  csrfTokenCache.token === token) {
88
114
  return csrfTokenCache.token;
89
115
  }
@@ -92,6 +118,7 @@ export function getCsrfToken() {
92
118
  windowToken: undefined,
93
119
  metaToken,
94
120
  inertiaToken,
121
+ cookieToken,
95
122
  };
96
123
  return token;
97
124
  }
@@ -213,7 +213,12 @@ const directives = [
213
213
  el.removeAttribute(name);
214
214
  }
215
215
  else {
216
- el.setAttribute(name, v === true ? "" : String(v));
216
+ const value = v === true ? "" : String(v);
217
+ if (isDangerousUrlAttribute(name, value)) {
218
+ el.removeAttribute(name);
219
+ return;
220
+ }
221
+ el.setAttribute(name, value);
217
222
  }
218
223
  };
219
224
  },
@@ -480,7 +485,101 @@ function stripStrings(s) {
480
485
  .replace(/"(?:[^"\\]|\\.)*"/g, "") // double-quoted strings
481
486
  .replace(/'(?:[^'\\]|\\.)*'/g, ""); // single-quoted strings
482
487
  }
488
+ function readQuotedLiteral(s, start) {
489
+ const quote = s[start];
490
+ if (quote !== "'" && quote !== '"' && quote !== "`")
491
+ return null;
492
+ let value = "";
493
+ for (let i = start + 1; i < s.length; i++) {
494
+ const ch = s[i];
495
+ if (ch === "\\") {
496
+ if (i + 1 >= s.length)
497
+ return null;
498
+ const escaped = readEscapedCharacter(s, i + 1);
499
+ if (!escaped)
500
+ return null;
501
+ value += escaped.value;
502
+ i = escaped.end - 1;
503
+ continue;
504
+ }
505
+ if (quote === "`" && ch === "$" && s[i + 1] === "{") {
506
+ return null;
507
+ }
508
+ if (ch === quote) {
509
+ return { end: i + 1, value };
510
+ }
511
+ value += ch;
512
+ }
513
+ return null;
514
+ }
515
+ function readEscapedCharacter(s, start) {
516
+ const ch = s[start];
517
+ if (ch === "u") {
518
+ if (s[start + 1] === "{") {
519
+ const close = s.indexOf("}", start + 2);
520
+ if (close === -1)
521
+ return null;
522
+ const codePoint = Number.parseInt(s.slice(start + 2, close), 16);
523
+ return Number.isFinite(codePoint) ? { end: close + 1, value: String.fromCodePoint(codePoint) } : null;
524
+ }
525
+ const codePoint = Number.parseInt(s.slice(start + 1, start + 5), 16);
526
+ return Number.isFinite(codePoint) ? { end: start + 5, value: String.fromCharCode(codePoint) } : null;
527
+ }
528
+ if (ch === "x") {
529
+ const codePoint = Number.parseInt(s.slice(start + 1, start + 3), 16);
530
+ return Number.isFinite(codePoint) ? { end: start + 3, value: String.fromCharCode(codePoint) } : null;
531
+ }
532
+ const escapes = { b: "\b", f: "\f", n: "\n", r: "\r", t: "\t", v: "\v" };
533
+ return { end: start + 1, value: escapes[ch] ?? ch };
534
+ }
535
+ function skipTrivia(s, start) {
536
+ let i = start;
537
+ while (i < s.length) {
538
+ while (/\s/.test(s[i] ?? ""))
539
+ i += 1;
540
+ if (s[i] === "/" && s[i + 1] === "/") {
541
+ i = s.indexOf("\n", i + 2);
542
+ if (i === -1)
543
+ return s.length;
544
+ continue;
545
+ }
546
+ if (s[i] === "/" && s[i + 1] === "*") {
547
+ const close = s.indexOf("*/", i + 2);
548
+ if (close === -1)
549
+ return s.length;
550
+ i = close + 2;
551
+ continue;
552
+ }
553
+ break;
554
+ }
555
+ return i;
556
+ }
557
+ function hasBlockedStringConcatenation(s) {
558
+ for (let i = 0; i < s.length; i++) {
559
+ const first = readQuotedLiteral(s, i);
560
+ if (!first)
561
+ continue;
562
+ const parts = [first.value];
563
+ let cursor = skipTrivia(s, first.end);
564
+ while (s[cursor] === "+") {
565
+ const nextStart = skipTrivia(s, cursor + 1);
566
+ const next = readQuotedLiteral(s, nextStart);
567
+ if (!next)
568
+ break;
569
+ parts.push(next.value);
570
+ cursor = skipTrivia(s, next.end);
571
+ }
572
+ if (parts.length > 1 && BLOCKED_GLOBALS.includes(parts.join(""))) {
573
+ return true;
574
+ }
575
+ i = first.end - 1;
576
+ }
577
+ return false;
578
+ }
483
579
  function isExpressionSafe(s) {
580
+ if (hasBlockedStringConcatenation(s)) {
581
+ return false;
582
+ }
484
583
  const stripped = stripStrings(s);
485
584
  return !BLOCKED_RE.test(stripped);
486
585
  }
@@ -525,6 +624,10 @@ function compileTextExpr(t) {
525
624
  const DANGEROUS_TAGS = new Set(["script", "style", "iframe", "object", "embed", "form", "meta", "link", "base"]);
526
625
  const DANGEROUS_ATTR_RE = /^on/i;
527
626
  const DANGEROUS_PROTO_RE = /^\s*javascript\s*:/i;
627
+ const URL_ATTRS = new Set(["href", "src", "action"]);
628
+ function isDangerousUrlAttribute(name, value) {
629
+ return URL_ATTRS.has(name.toLowerCase()) && DANGEROUS_PROTO_RE.test(value);
630
+ }
528
631
  function sanitizeHtml(html) {
529
632
  const tpl = document.createElement("template");
530
633
  tpl.innerHTML = html;
@@ -545,7 +648,7 @@ function sanitizeNode(node) {
545
648
  if (DANGEROUS_ATTR_RE.test(attr.name)) {
546
649
  el.removeAttribute(attr.name);
547
650
  }
548
- else if ((attr.name === "href" || attr.name === "src" || attr.name === "action") && DANGEROUS_PROTO_RE.test(attr.value)) {
651
+ else if (isDangerousUrlAttribute(attr.name, attr.value)) {
549
652
  el.removeAttribute(attr.name);
550
653
  }
551
654
  }
@@ -94,6 +94,12 @@ export interface TypesConfig {
94
94
  * @default false
95
95
  */
96
96
  globalRoute?: boolean;
97
+ /**
98
+ * Fail Vite when type generation fails.
99
+ *
100
+ * Defaults to true during `vite build` and false during `vite serve`.
101
+ */
102
+ failOnError?: boolean;
97
103
  /**
98
104
  * Debounce time in milliseconds for type regeneration.
99
105
  * Prevents regeneration from running too frequently when