create-swift-rust 0.4.0 → 0.7.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 (65) hide show
  1. package/dist/index.js +74 -938
  2. package/dist/index.js.map +1 -1
  3. package/package.json +6 -4
  4. package/templates/default/README.md +29 -0
  5. package/templates/default/_env.example +5 -0
  6. package/templates/default/_env.local +5 -0
  7. package/templates/default/_gitignore +14 -0
  8. package/templates/default/biome.json +12 -0
  9. package/templates/default/globals.d.ts +19 -0
  10. package/templates/default/package.json +30 -0
  11. package/templates/default/postcss.config.mjs +7 -0
  12. package/templates/default/public/blog/introducing-swift-rust.svg +12 -0
  13. package/templates/default/public/og-image.svg +25 -0
  14. package/templates/default/public/samples/axelborg-towers-modern-architecture.jpg +0 -0
  15. package/templates/default/public/samples/brown-high-skyscrapers.jpg +0 -0
  16. package/templates/default/public/samples/close-up-portrait-gorgeous-young-woman.jpg +0 -0
  17. package/templates/default/public/samples/full-shot-woman-taking-selfie.jpg +0 -0
  18. package/templates/default/public/samples/happy-black-parents-with-kids-making-video-call-smart-phone-home.jpg +0 -0
  19. package/templates/default/public/samples/metaverse-concept-collage-design.jpg +0 -0
  20. package/templates/default/src/app/about/layout.tsx +5 -0
  21. package/templates/default/src/app/about/page.tsx +74 -0
  22. package/templates/default/src/app/about/team/page.tsx +88 -0
  23. package/templates/default/src/app/api/contact/route.ts +15 -0
  24. package/templates/default/src/app/api/health/route.ts +7 -0
  25. package/templates/default/src/app/api/posts/[id]/route.ts +33 -0
  26. package/templates/default/src/app/api/posts/route.ts +29 -0
  27. package/templates/default/src/app/api/search/route.ts +15 -0
  28. package/templates/default/src/app/api/stats/route.ts +16 -0
  29. package/templates/default/src/app/blog/[slug]/page.tsx +116 -0
  30. package/templates/default/src/app/blog/page.tsx +101 -0
  31. package/templates/default/src/app/blog/tag/[tag]/page.tsx +82 -0
  32. package/templates/default/src/app/contact/page.tsx +131 -0
  33. package/templates/default/src/app/dashboard/analytics/page.tsx +103 -0
  34. package/templates/default/src/app/dashboard/layout.tsx +11 -0
  35. package/templates/default/src/app/dashboard/loading.tsx +18 -0
  36. package/templates/default/src/app/dashboard/page.tsx +181 -0
  37. package/templates/default/src/app/dashboard/posts/[id]/not-found.tsx +23 -0
  38. package/templates/default/src/app/dashboard/posts/[id]/page.tsx +87 -0
  39. package/templates/default/src/app/dashboard/posts/new/page.tsx +103 -0
  40. package/templates/default/src/app/dashboard/posts/page.tsx +81 -0
  41. package/templates/default/src/app/dashboard/settings/page.tsx +119 -0
  42. package/templates/default/src/app/error.tsx +42 -0
  43. package/templates/default/src/app/favicon.ico +0 -0
  44. package/templates/default/src/app/favicon.svg +8 -0
  45. package/templates/default/src/app/fonts/page.tsx +610 -0
  46. package/templates/default/src/app/globals.css +238 -0
  47. package/templates/default/src/app/images/page.tsx +56 -0
  48. package/templates/default/src/app/layout.tsx +44 -0
  49. package/templates/default/src/app/loading.tsx +13 -0
  50. package/templates/default/src/app/not-found.tsx +25 -0
  51. package/templates/default/src/app/page.tsx +410 -0
  52. package/templates/default/src/app/videos/page.tsx +182 -0
  53. package/templates/default/src/components/font-studio.tsx +378 -0
  54. package/templates/default/src/components/footer.tsx +75 -0
  55. package/templates/default/src/components/logo.tsx +26 -0
  56. package/templates/default/src/components/nav.tsx +102 -0
  57. package/templates/default/src/components/sidebar.tsx +109 -0
  58. package/templates/default/src/lib/blur.ts +5 -0
  59. package/templates/default/src/lib/format.ts +31 -0
  60. package/templates/default/src/lib/posts.ts +132 -0
  61. package/templates/default/src/lib/site.config.ts +39 -0
  62. package/templates/default/src/lib/stats.ts +25 -0
  63. package/templates/default/swift-rust.config.json +8 -0
  64. package/templates/default/tsconfig.json +29 -0
  65. package/templates/default/vercel.json +23 -0
@@ -0,0 +1,109 @@
1
+ import { Link } from "swift-rust";
2
+ import { Logo } from "./logo";
3
+
4
+ const ITEMS = [
5
+ {
6
+ section: "Workspace",
7
+ links: [
8
+ { href: "/dashboard", label: "Overview", icon: IconHome },
9
+ { href: "/dashboard/analytics", label: "Analytics", icon: IconChart },
10
+ { href: "/dashboard/posts", label: "Posts", icon: IconDoc },
11
+ ],
12
+ },
13
+ {
14
+ section: "Settings",
15
+ links: [
16
+ { href: "/dashboard/posts/new", label: "New post", icon: IconPlus },
17
+ { href: "/dashboard/settings", label: "Settings", icon: IconCog },
18
+ ],
19
+ },
20
+ ];
21
+
22
+ export function Sidebar({ pathname = "" }: { pathname?: string }) {
23
+ return (
24
+ <aside className="sticky top-14 hidden h-[calc(100vh-3.5rem)] w-60 shrink-0 self-start flex-col border-r border-[var(--color-border)] bg-[var(--color-surface)] lg:flex">
25
+ <div className="flex h-14 items-center border-b border-[var(--color-border)] px-5">
26
+ <Logo />
27
+ </div>
28
+ <nav className="flex-1 space-y-8 overflow-y-auto p-5">
29
+ {ITEMS.map((group) => (
30
+ <div key={group.section}>
31
+ <h3 className="mb-2 px-2 text-[0.7rem] font-semibold uppercase tracking-wider text-[var(--color-fg-subtle)]">
32
+ {group.section}
33
+ </h3>
34
+ <ul className="space-y-0.5">
35
+ {group.links.map((link) => {
36
+ const active =
37
+ link.href === "/dashboard"
38
+ ? pathname === "/dashboard"
39
+ : pathname.startsWith(link.href);
40
+ const Icon = link.icon;
41
+ return (
42
+ <li key={link.href}>
43
+ <Link
44
+ href={link.href}
45
+ className={`flex items-center gap-2.5 rounded-md px-2.5 py-1.5 text-[0.875rem] transition-colors ${
46
+ active
47
+ ? "bg-[var(--color-accent-soft)] font-medium text-[var(--color-accent)]"
48
+ : "text-[var(--color-fg-muted)] hover:bg-[var(--color-surface-2)] hover:text-[var(--color-fg)]"
49
+ }`}
50
+ >
51
+ <Icon className="h-4 w-4" />
52
+ {link.label}
53
+ </Link>
54
+ </li>
55
+ );
56
+ })}
57
+ </ul>
58
+ </div>
59
+ ))}
60
+ </nav>
61
+ <div className="border-t border-[var(--color-border)] p-4">
62
+ <div className="rounded-lg border border-[var(--color-border)] bg-[var(--color-surface-2)] p-3">
63
+ <p className="text-[0.75rem] font-medium text-[var(--color-fg)]">Pro tip</p>
64
+ <p className="mt-1 text-[0.75rem] leading-relaxed text-[var(--color-fg-muted)]">
65
+ Press <kbd className="rounded border border-[var(--color-border)] bg-[var(--color-surface)] px-1 font-mono text-[0.6875rem]">⌘K</kbd> for quick search.
66
+ </p>
67
+ </div>
68
+ </div>
69
+ </aside>
70
+ );
71
+ }
72
+
73
+ function IconHome({ className = "" }: { className?: string }) {
74
+ return (
75
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.75" className={className}>
76
+ <path d="M3 11l9-7 9 7v9a2 2 0 0 1-2 2h-4v-7H9v7H5a2 2 0 0 1-2-2z" strokeLinejoin="round" />
77
+ </svg>
78
+ );
79
+ }
80
+ function IconChart({ className = "" }: { className?: string }) {
81
+ return (
82
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.75" className={className}>
83
+ <path d="M3 21h18M6 17v-4m6 4V7m6 10v-7" strokeLinecap="round" />
84
+ </svg>
85
+ );
86
+ }
87
+ function IconDoc({ className = "" }: { className?: string }) {
88
+ return (
89
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.75" className={className}>
90
+ <path d="M6 3h9l4 4v14a1 1 0 0 1-1 1H6a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1Z" strokeLinejoin="round" />
91
+ <path d="M14 3v5h5M8 13h8M8 17h6" strokeLinecap="round" />
92
+ </svg>
93
+ );
94
+ }
95
+ function IconPlus({ className = "" }: { className?: string }) {
96
+ return (
97
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.75" className={className}>
98
+ <path d="M12 5v14M5 12h14" strokeLinecap="round" />
99
+ </svg>
100
+ );
101
+ }
102
+ function IconCog({ className = "" }: { className?: string }) {
103
+ return (
104
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.75" className={className}>
105
+ <circle cx="12" cy="12" r="3" />
106
+ <path d="M19.4 15a1.7 1.7 0 0 0 .3 1.8l.1.1a2 2 0 1 1-2.8 2.8l-.1-.1a1.7 1.7 0 0 0-1.8-.3 1.7 1.7 0 0 0-1 1.5V21a2 2 0 1 1-4 0v-.1a1.7 1.7 0 0 0-1-1.5 1.7 1.7 0 0 0-1.8.3l-.1.1a2 2 0 1 1-2.8-2.8l.1-.1a1.7 1.7 0 0 0 .3-1.8 1.7 1.7 0 0 0-1.5-1H3a2 2 0 1 1 0-4h.1a1.7 1.7 0 0 0 1.5-1 1.7 1.7 0 0 0-.3-1.8l-.1-.1a2 2 0 1 1 2.8-2.8l.1.1a1.7 1.7 0 0 0 1.8.3h0a1.7 1.7 0 0 0 1-1.5V3a2 2 0 1 1 4 0v.1a1.7 1.7 0 0 0 1 1.5 1.7 1.7 0 0 0 1.8-.3l.1-.1a2 2 0 1 1 2.8 2.8l-.1.1a1.7 1.7 0 0 0-.3 1.8v0a1.7 1.7 0 0 0 1.5 1H21a2 2 0 1 1 0 4h-.1a1.7 1.7 0 0 0-1.5 1Z" />
107
+ </svg>
108
+ );
109
+ }
@@ -0,0 +1,5 @@
1
+ /** Soft-orange blur placeholder for photo <Image>s. */
2
+ export const BLUR = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAADElEQVR4nGP4PckGAARVAcq6orJtAAAAAElFTkSuQmCC";
3
+
4
+ /** Fully transparent placeholder — for logos/icons with transparent edges. */
5
+ export const BLUR_TRANSPARENT = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAC0lEQVR4nGNgAAIAAAUAAXpeqz8AAAAASUVORK5CYII=";
@@ -0,0 +1,31 @@
1
+ export function formatDate(input: string | number | Date): string {
2
+ const d = new Date(input);
3
+ return d.toLocaleDateString("en-US", {
4
+ year: "numeric",
5
+ month: "long",
6
+ day: "numeric",
7
+ });
8
+ }
9
+
10
+ export function formatNumber(n: number): string {
11
+ if (n >= 1_000_000) return `${(n / 1_000_000).toFixed(1)}M`;
12
+ if (n >= 1_000) return `${(n / 1_000).toFixed(1)}k`;
13
+ return n.toString();
14
+ }
15
+
16
+ export function formatPercent(n: number, digits = 1): string {
17
+ return `${n.toFixed(digits)}%`;
18
+ }
19
+
20
+ export function relativeTime(input: string | number | Date): string {
21
+ const ms = Date.now() - new Date(input).getTime();
22
+ const s = Math.floor(ms / 1000);
23
+ if (s < 60) return "just now";
24
+ const m = Math.floor(s / 60);
25
+ if (m < 60) return `${m}m ago`;
26
+ const h = Math.floor(m / 60);
27
+ if (h < 24) return `${h}h ago`;
28
+ const d = Math.floor(h / 24);
29
+ if (d < 30) return `${d}d ago`;
30
+ return formatDate(input);
31
+ }
@@ -0,0 +1,132 @@
1
+ export type Post = {
2
+ slug: string;
3
+ title: string;
4
+ excerpt: string;
5
+ date: string;
6
+ readingTime: string;
7
+ author: { name: string; role: string };
8
+ tags: string[];
9
+ body: string;
10
+ cover?: string;
11
+ };
12
+
13
+ export const posts: Post[] = [
14
+ {
15
+ slug: "introducing-swift-rust",
16
+ title: "Introducing Swift Rust",
17
+ cover: "/blog/introducing-swift-rust.svg",
18
+ excerpt:
19
+ "A full-stack React framework powered with Rust + Bun, designed to feel as familiar as the one you already use.",
20
+ date: "2026-05-28",
21
+ readingTime: "4 min read",
22
+ author: { name: "The Swift Rust Team", role: "Engineering" },
23
+ tags: ["announcement", "framework"],
24
+ body: `Today we are open-sourcing Swift Rust, a full-stack React framework powered with Rust + Bun that takes the parts of Next.js that everyone copied and reimplements them on top of a Rust runtime.
25
+
26
+ The pitch is simple: write TSX, get streaming SSR, file-system routing, and a single binary at deploy. 10x faster than Next.js. No more pinning the lockfile to dodge a Node deprecation. No more debugging why your edge function works in one region and not another.
27
+
28
+ We built it because we wanted to use it. The repo is live, the docs are live, and the demo you are reading is rendered by it. If you have ever thought "I wish this was just a Rust binary", we made it one.`,
29
+ },
30
+ {
31
+ slug: "four-rendering-modes",
32
+ title: "Why we shipped four rendering modes",
33
+ excerpt:
34
+ "SSR + WASM, SSR only, SSR + HTMX, and full WASM. One isn't enough. Here is why.",
35
+ date: "2026-05-20",
36
+ readingTime: "7 min read",
37
+ author: { name: "Alex Chen", role: "Founding Engineer" },
38
+ tags: ["architecture", "ssr", "wasm"],
39
+ body: `The original Next.js had one mode: SSR. Then it added SSG. Then ISR. Then RSC. Then... you get the idea. We did not want to ship a framework with ten modes and a flowchart.
40
+
41
+ Instead, we picked four and made each one excellent:
42
+
43
+ - SSR + WASM is the default. Server-rendered HTML, hydrated with a tiny WASM runtime that gives you client-side islands.
44
+ - SSR only has no client JavaScript at all. Good for documentation sites, blogs, and anywhere you don't need interactivity.
45
+ - SSR + HTMX is for teams that want progressive enhancement without giving up server rendering.
46
+ - Full WASM SPA is for when you genuinely need an app. The whole thing runs in the browser.
47
+
48
+ Pick the one that fits. Change it later in one line of config.`,
49
+ },
50
+ {
51
+ slug: "streaming-ssr-in-rust",
52
+ title: "Streaming SSR in Rust, without the headache",
53
+ excerpt:
54
+ "How we built a streaming SSR pipeline that handles async components and Suspense correctly.",
55
+ date: "2026-05-12",
56
+ readingTime: "9 min read",
57
+ author: { name: "Priya Singh", role: "Runtime Engineer" },
58
+ tags: ["ssr", "rust", "internals"],
59
+ body: `Streaming SSR is one of those features that sounds easy and is, in fact, not. The list of things that can go wrong is long: async components that need to suspend, Suspense boundaries that need to flush, hydration mismatches when the server and client disagree about what to render.
60
+
61
+ We started from React's renderToReadableStream and built on top of it. The result is a pipeline that:
62
+
63
+ 1. Starts sending HTML within a few milliseconds of the request landing.
64
+ 2. Streams async segments as they resolve, with no buffer in between.
65
+ 3. Hydrates the page progressively, so the user can interact with above-the-fold content immediately.
66
+
67
+ The hardest part was getting the error boundary semantics right. We use Rust-style error codes (SR-E0001, SR-E0401, etc.) that are emitted at compile time by our SWC plugin, and a sleek dark error overlay that shows up in the dev server.`,
68
+ },
69
+ {
70
+ slug: "fonts-without-fout",
71
+ title: "Fonts, without the FOUT",
72
+ excerpt:
73
+ "2,071 Google fonts, automatic subsetting, and zero layout shift.",
74
+ date: "2026-05-04",
75
+ readingTime: "3 min read",
76
+ author: { name: "Maria Lopez", role: "Design Systems" },
77
+ tags: ["fonts", "performance"],
78
+ body: `Fonts are the most common cause of layout shift on the web. Most frameworks let you load a font, but they do not give you a way to make sure it does not cause a flicker when it loads.
79
+
80
+ Swift Rust's font component downloads the font at build time, subsets it to the characters you actually use, and emits a CSS variable that you can use in your styles. The browser uses the fallback font for the first paint, and swaps in the real font when it loads, with no layout shift.
81
+
82
+ We support 2,071 Google fonts out of the box, including Geist, Geist Mono, Inter, Manrope, and IBM Plex. Local fonts work too.`,
83
+ },
84
+ {
85
+ slug: "the-swc-plugin",
86
+ title: "Inside the SWC plugin",
87
+ excerpt:
88
+ "How we turned a 200-millisecond build into a 12-millisecond build.",
89
+ date: "2026-04-26",
90
+ readingTime: "11 min read",
91
+ author: { name: "Jordan Kim", role: "Compiler Engineer" },
92
+ tags: ["compiler", "swc", "performance"],
93
+ body: `The single biggest performance win in the framework is the SWC plugin. It runs at build time, and it does three things:
94
+
95
+ 1. Strips "use client" / "use server" directives and replaces them with virtual modules.
96
+ 2. Collects imports for the bundler, so it knows exactly what to ship.
97
+ 3. Emits error codes (SR-E0001 through SR-E1404) for common mistakes, so the dev server can show you a friendly error message instead of a stack trace.
98
+
99
+ The plugin is powered with Rust and uses swc_core. It compiles in about a second, and it runs in about 12 milliseconds per file. That is the difference between a 200-millisecond incremental build and a 12-millisecond one.`,
100
+ },
101
+ {
102
+ slug: "single-binary-deploy",
103
+ title: "Deploy one binary, scale forever",
104
+ excerpt:
105
+ "How compiling your app to a Rust binary changes the way you ship.",
106
+ date: "2026-04-18",
107
+ readingTime: "5 min read",
108
+ author: { name: "Sam Rivera", role: "Infrastructure" },
109
+ tags: ["deployment", "infrastructure"],
110
+ body: `When you build a Swift Rust app, the output is a single binary. No Node, no edge runtime, no package manager at deploy time. You copy the binary to a server, run it, and it serves.
111
+
112
+ This has three big consequences:
113
+
114
+ 1. Cold starts are measured in microseconds, not seconds.
115
+ 2. Memory usage is measured in tens of megabytes, not hundreds.
116
+ 3. There is no "did you forget to install something" at deploy time.
117
+
118
+ The binary is also a CLI. Run it with no arguments, and it starts a dev server. Run it with --build, and it produces a production binary. Run it with --start, and it serves the production build.`,
119
+ },
120
+ ];
121
+
122
+ export function getPost(slug: string): Post | undefined {
123
+ return posts.find((p) => p.slug === slug);
124
+ }
125
+
126
+ export function getPostsByTag(tag: string): Post[] {
127
+ return posts.filter((p) => p.tags.includes(tag));
128
+ }
129
+
130
+ export function getAllTags(): string[] {
131
+ return Array.from(new Set(posts.flatMap((p) => p.tags))).sort();
132
+ }
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Single source of truth for external URLs and the framework version.
3
+ *
4
+ * Override URLs at build time with environment variables (e.g. in the Vercel
5
+ * dashboard or a local .env). The version is read from the installed
6
+ * `swift-rust` package, so it updates automatically on every release — no
7
+ * hand-editing "v0.1.0" across the site.
8
+ */
9
+ import { createRequire } from "node:module";
10
+ import { join } from "node:path";
11
+
12
+ function swiftRustVersion(): string {
13
+ try {
14
+ // Anchor the resolver to the project root (cwd) rather than import.meta.url:
15
+ // the dev server imports modules with a cache-busting query string, which
16
+ // makes import.meta.url an invalid base for module resolution.
17
+ const require = createRequire(join(process.cwd(), "noop.js"));
18
+ const v = (require("swift-rust/package.json") as { version?: string }).version;
19
+ if (v) return v;
20
+ } catch {
21
+ // ignore
22
+ }
23
+ return "0.0.0";
24
+ }
25
+
26
+ const GITHUB = process.env.SWIFT_RUST_GITHUB_URL ?? "https://github.com/colesites/swift-rust";
27
+
28
+ export const siteConfig = {
29
+ /** Marketing / main site. */
30
+ url: process.env.SWIFT_RUST_SITE_URL ?? "https://swift-rust-self.vercel.app",
31
+ /** Documentation site. */
32
+ docsUrl: process.env.SWIFT_RUST_DOCS_URL ?? "https://docs-swift-rust.vercel.app",
33
+ /** Source repository. */
34
+ githubUrl: GITHUB,
35
+ /** GitHub issues. Derived from githubUrl unless explicitly overridden. */
36
+ issuesUrl: process.env.SWIFT_RUST_ISSUES_URL ?? `${GITHUB}/issues`,
37
+ /** Framework version, read from the installed swift-rust package. */
38
+ version: swiftRustVersion(),
39
+ } as const;
@@ -0,0 +1,25 @@
1
+ export type Stat = {
2
+ label: string;
3
+ value: string;
4
+ delta: number;
5
+ context: string;
6
+ };
7
+
8
+ export const stats: Stat[] = [
9
+ { label: "Total requests", value: "2.4M", delta: 12.4, context: "Last 30 days" },
10
+ { label: "Avg latency", value: "14ms", delta: -8.1, context: "p50, streaming SSR" },
11
+ { label: "Cache hit rate", value: "97.2%", delta: 0.4, context: "Edge cache" },
12
+ { label: "Error rate", value: "0.03%", delta: -22.0, context: "5xx responses" },
13
+ ];
14
+
15
+ export const trafficSeries: Array<{ day: string; requests: number }> = [
16
+ { day: "Mon", requests: 312000 },
17
+ { day: "Tue", requests: 298000 },
18
+ { day: "Wed", requests: 354000 },
19
+ { day: "Thu", requests: 401000 },
20
+ { day: "Fri", requests: 482000 },
21
+ { day: "Sat", requests: 367000 },
22
+ { day: "Sun", requests: 221000 },
23
+ ];
24
+
25
+ export const trafficMax = Math.max(...trafficSeries.map((d) => d.requests));
@@ -0,0 +1,8 @@
1
+ {
2
+ "rendering": "ssr",
3
+ "reactStrictMode": true,
4
+ "experimental": {
5
+ "pdf": true,
6
+ "wasm": true
7
+ }
8
+ }
@@ -0,0 +1,29 @@
1
+ {
2
+ "extends": "swift-rust/tsconfig.base.json",
3
+ "compilerOptions": {
4
+ "ignoreDeprecations": "6.0",
5
+ "lib": [
6
+ "ESNext",
7
+ "DOM",
8
+ "DOM.Iterable"
9
+ ],
10
+ "jsx": "preserve",
11
+ "baseUrl": ".",
12
+ "paths": {
13
+ "@/*": [
14
+ "./src/*"
15
+ ]
16
+ }
17
+ },
18
+ "include": [
19
+ "src/**/*",
20
+ "globals.d.ts",
21
+ ".swift-rust/types/**/*"
22
+ ],
23
+ "exclude": [
24
+ "node_modules",
25
+ "dist",
26
+ ".swift-rust",
27
+ ".turbo"
28
+ ]
29
+ }
@@ -0,0 +1,23 @@
1
+ {
2
+ "$schema": "https://openapi.vercel.sh/vercel.json",
3
+ "buildCommand": "bun run build",
4
+ "installCommand": "rm -rf node_modules && bun install",
5
+ "outputDirectory": ".vercel/output",
6
+ "framework": null,
7
+ "trailingSlash": false,
8
+ "cleanUrls": true,
9
+ "headers": [
10
+ {
11
+ "source": "/_swift-rust/static/(.*)",
12
+ "headers": [
13
+ { "key": "Cache-Control", "value": "public, max-age=31536000, immutable" }
14
+ ]
15
+ },
16
+ {
17
+ "source": "/fonts/(.*)",
18
+ "headers": [
19
+ { "key": "Cache-Control", "value": "public, max-age=31536000, immutable" }
20
+ ]
21
+ }
22
+ ]
23
+ }