@flaredev/cli 0.2.0 → 0.4.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 (88) hide show
  1. package/bin/flare.js +9 -1
  2. package/dist/index.js +991 -344
  3. package/package.json +2 -2
  4. package/templates/app/app/admin/page.tsx +9 -61
  5. package/templates/app/app/dashboard/account/layout.tsx +30 -0
  6. package/templates/app/app/dashboard/account/page.tsx +11 -31
  7. package/templates/app/app/dashboard/account/password/page.tsx +22 -0
  8. package/templates/app/app/dashboard/account/security/page.tsx +34 -0
  9. package/templates/app/app/dashboard/account/sessions/page.tsx +9 -0
  10. package/templates/app/app/dashboard/actions.ts +246 -0
  11. package/templates/app/app/dashboard/import-actions.ts +91 -0
  12. package/templates/app/app/dashboard/layout.tsx +66 -39
  13. package/templates/app/app/dashboard/observability/page.tsx +175 -0
  14. package/templates/app/app/dashboard/page.tsx +108 -27
  15. package/templates/app/app/verify-email/page.tsx +33 -0
  16. package/templates/app/components/account/account-nav.tsx +39 -0
  17. package/templates/app/components/account/{security-settings.tsx → sections.tsx} +507 -525
  18. package/templates/app/components/auth/otp-input.tsx +95 -0
  19. package/templates/app/components/auth/password-field.tsx +116 -0
  20. package/templates/app/components/auth/password-forms.tsx +76 -79
  21. package/templates/app/components/auth/sign-in-flow.tsx +306 -315
  22. package/templates/app/components/auth/sign-up-form.tsx +93 -101
  23. package/templates/app/components/auth/two-factor-form.tsx +139 -124
  24. package/templates/app/components/auth/use-resend.ts +44 -0
  25. package/templates/app/components/auth/verify-email-form.tsx +135 -0
  26. package/templates/app/components/dashboard/column-menu.tsx +66 -0
  27. package/templates/app/components/dashboard/dashboard-header.tsx +115 -0
  28. package/templates/app/components/dashboard/dashboard-sidebar.tsx +228 -0
  29. package/templates/app/components/dashboard/dropzone.tsx +92 -0
  30. package/templates/app/components/dashboard/editable.ts +12 -0
  31. package/templates/app/components/dashboard/export-button.tsx +41 -0
  32. package/templates/app/components/{admin → dashboard}/fields/file-field.tsx +22 -18
  33. package/templates/app/components/dashboard/import-dialog.tsx +540 -0
  34. package/templates/app/components/dashboard/inline-cell.tsx +165 -0
  35. package/templates/app/components/dashboard/local-time.tsx +77 -0
  36. package/templates/app/components/dashboard/page-header.tsx +57 -0
  37. package/templates/app/components/{admin → dashboard}/query.ts +2 -0
  38. package/templates/app/components/dashboard/record-actions.tsx +113 -0
  39. package/templates/app/components/dashboard/record-detail.tsx +200 -0
  40. package/templates/app/components/dashboard/resource-chart.tsx +91 -0
  41. package/templates/app/components/dashboard/resource-form-dialog.tsx +80 -0
  42. package/templates/app/components/{admin → dashboard}/resource-form-page.tsx +61 -55
  43. package/templates/app/components/{admin → dashboard}/resource-form.tsx +165 -139
  44. package/templates/app/components/dashboard/resource-stats.tsx +48 -0
  45. package/templates/app/components/{admin → dashboard}/resource-table.tsx +152 -28
  46. package/templates/app/components/{admin → dashboard}/row-actions.tsx +56 -11
  47. package/templates/app/components/dashboard/save-view-button.tsx +86 -0
  48. package/templates/app/components/dashboard/stat-card.tsx +69 -0
  49. package/templates/app/components/dashboard/table-selection.tsx +185 -0
  50. package/templates/app/components/dashboard/traffic-chart.tsx +32 -0
  51. package/templates/app/components/dashboard/user-menu.tsx +91 -0
  52. package/templates/app/components/ui/sonner.tsx +33 -13
  53. package/templates/app/db/flare-schema.ts +55 -1
  54. package/templates/app/lib/analytics.ts +121 -0
  55. package/templates/app/lib/api.ts +12 -0
  56. package/templates/app/lib/audit.ts +80 -0
  57. package/templates/app/lib/auth-config.ts +38 -33
  58. package/templates/app/lib/auth.ts +34 -0
  59. package/templates/app/lib/cache.ts +83 -83
  60. package/templates/app/lib/csv.ts +24 -0
  61. package/templates/app/lib/dashboard-nav.ts +15 -0
  62. package/templates/app/lib/dashboard.ts +165 -0
  63. package/templates/app/lib/password-rules.ts +159 -0
  64. package/templates/app/lib/site.ts +56 -47
  65. package/templates/app/lib/views.ts +29 -0
  66. package/templates/app/migrations/0000_init.sql +25 -2
  67. package/templates/app/migrations/meta/0000_snapshot.json +159 -1
  68. package/templates/app/migrations/meta/_journal.json +1 -1
  69. package/templates/app/vite.config.ts +41 -31
  70. package/templates/app/worker-configuration.d.ts +15764 -0
  71. package/templates/locks/package-lock.json +9189 -0
  72. package/templates/locks/pnpm-lock.yaml +6347 -0
  73. package/templates/locks/yarn.lock +3548 -0
  74. package/templates/app/app/admin/actions.ts +0 -113
  75. package/templates/app/app/admin/layout.tsx +0 -32
  76. package/templates/app/components/admin/admin-header.tsx +0 -78
  77. package/templates/app/components/admin/resource-nav.tsx +0 -120
  78. package/templates/app/components/dashboard/dashboard-nav.tsx +0 -35
  79. package/templates/app/lib/admin-nav.ts +0 -15
  80. package/templates/app/lib/admin.ts +0 -103
  81. /package/templates/app/components/{admin → dashboard}/fields/country-field.tsx +0 -0
  82. /package/templates/app/components/{admin → dashboard}/fields/date-field.tsx +0 -0
  83. /package/templates/app/components/{admin → dashboard}/fields/field-widget.tsx +0 -0
  84. /package/templates/app/components/{admin → dashboard}/fields/multi-select-field.tsx +0 -0
  85. /package/templates/app/components/{admin → dashboard}/fields/phone-field.tsx +0 -0
  86. /package/templates/app/components/{admin → dashboard}/fields/relation-field.tsx +0 -0
  87. /package/templates/app/components/{admin → dashboard}/resource-icon.tsx +0 -0
  88. /package/templates/app/components/{admin → dashboard}/resource-table-toolbar.tsx +0 -0
@@ -1,47 +1,56 @@
1
- import type { ThemeName } from "./theme";
2
-
3
- /**
4
- * What the site says about itself: the home page, the sign-in screens' side panels and
5
- * the page titles read from here. Edit freely.
6
- */
7
- export const site = {
8
- name: "__APP_NAME__",
9
- tagline: "The fastest way to run your business",
10
- description: "__APP_NAME__ keeps your team, your customers and your data in one place, and it's live on the edge in every region.",
11
- /**
12
- * The look of the whole app: sign-in screens, dashboard, admin and home page.
13
- * One of: default, coral, amber, sky, mono, emerald. FLARE_THEME (shell or .env)
14
- * overrides it when the app is built or run with `flare dev`.
15
- */
16
- theme: "__THEME__" as ThemeName,
17
- /** The small pill above the home page headline; set to null to hide it. */
18
- announcement: { label: "New", text: "Passkeys and two-factor sign-in are here", href: "/sign-up" } as { label: string; text: string; href: string } | null,
19
- /** The home page's feature grid (icon names from lucide.dev). */
20
- features: [
21
- { icon: "zap", title: "Fast everywhere", description: "Served from the edge in every region, so pages load in milliseconds wherever your customers are." },
22
- { icon: "shield-check", title: "Secure by default", description: "Passkeys, two-factor sign-in, roles and rate limits, switched on from day one." },
23
- { icon: "layout-dashboard", title: "One place for your data", description: "Every record your team keeps, with search, filters and an admin built in." },
24
- { icon: "users", title: "Built for teams", description: "Invite your team, give each person the right access, and see who changed what." },
25
- { icon: "plug", title: "Connects to your tools", description: "A documented API for every record, ready for your scripts and integrations." },
26
- { icon: "credit-card", title: "Billing when you need it", description: "Plans, checkout and invoices through Stripe, without building a billing system." },
27
- ],
28
- /** Placeholder names for the logo strip: replace them with your customers (or remove the strip). */
29
- customers: ["Acme", "Globex", "Initech", "Vandelay", "Northwind", "Brightline"],
30
- /** Shown beside the sign-up form. */
31
- highlights: [
32
- "Set up in minutes, with nothing to install",
33
- "Secure sign-in with passkeys and two-factor",
34
- "Your data stays yours: export it any time",
35
- ],
36
- /** Shown on the sign-in screen of themes with a quote panel. */
37
- testimonial: {
38
- quote: "We moved our whole workflow over in an afternoon. The team hasn't looked back.",
39
- author: "Alex Rivera",
40
- role: "Head of Operations",
41
- },
42
- links: {
43
- terms: "/terms",
44
- privacy: "/privacy",
45
- support: "mailto:support@example.com",
46
- },
47
- };
1
+ import type { ThemeName } from "./theme";
2
+
3
+ /**
4
+ * What the site says about itself: the home page, the sign-in screens' side panels and
5
+ * the page titles read from here. Edit freely.
6
+ */
7
+ export const site = {
8
+ name: "__APP_NAME__",
9
+ tagline: "The fastest way to run your business",
10
+ description: "__APP_NAME__ keeps your team, your customers and your data in one place, and it's live on the edge in every region.",
11
+ /**
12
+ * The look of the whole app: sign-in screens, dashboard, admin and home page.
13
+ * One of: default, coral, amber, sky, mono, emerald. FLARE_THEME (shell or .env)
14
+ * overrides it when the app is built or run with `flare dev`.
15
+ */
16
+ theme: "__THEME__" as ThemeName,
17
+ /** How the dashboard behaves. */
18
+ dashboard: {
19
+ /**
20
+ * Where a record is created and edited: in a dialog over the list ("modal", the
21
+ * default, which keeps the list, its filters and your place in it), or on its own
22
+ * page ("page", better for long forms).
23
+ */
24
+ forms: "modal" as "modal" | "page",
25
+ },
26
+ /** The small pill above the home page headline; set to null to hide it. */
27
+ announcement: { label: "New", text: "Passkeys and two-factor sign-in are here", href: "/sign-up" } as { label: string; text: string; href: string } | null,
28
+ /** The home page's feature grid (icon names from lucide.dev). */
29
+ features: [
30
+ { icon: "zap", title: "Fast everywhere", description: "Served from the edge in every region, so pages load in milliseconds wherever your customers are." },
31
+ { icon: "shield-check", title: "Secure by default", description: "Passkeys, two-factor sign-in, roles and rate limits, switched on from day one." },
32
+ { icon: "layout-dashboard", title: "One place for your data", description: "Every record your team keeps, with search, filters and an admin built in." },
33
+ { icon: "users", title: "Built for teams", description: "Invite your team, give each person the right access, and see who changed what." },
34
+ { icon: "plug", title: "Connects to your tools", description: "A documented API for every record, ready for your scripts and integrations." },
35
+ { icon: "credit-card", title: "Billing when you need it", description: "Plans, checkout and invoices through Stripe, without building a billing system." },
36
+ ],
37
+ /** Placeholder names for the logo strip: replace them with your customers (or remove the strip). */
38
+ customers: ["Acme", "Globex", "Initech", "Vandelay", "Northwind", "Brightline"],
39
+ /** Shown beside the sign-up form. */
40
+ highlights: [
41
+ "Set up in minutes, with nothing to install",
42
+ "Secure sign-in with passkeys and two-factor",
43
+ "Your data stays yours: export it any time",
44
+ ],
45
+ /** Shown on the sign-in screen of themes with a quote panel. */
46
+ testimonial: {
47
+ quote: "We moved our whole workflow over in an afternoon. The team hasn't looked back.",
48
+ author: "Alex Rivera",
49
+ role: "Head of Operations",
50
+ },
51
+ links: {
52
+ terms: "/terms",
53
+ privacy: "/privacy",
54
+ support: "mailto:support@example.com",
55
+ },
56
+ };
@@ -0,0 +1,29 @@
1
+ import { and, asc, eq } from "drizzle-orm";
2
+ import { getDb } from "@/db";
3
+ import { savedView } from "@/db/flare-schema";
4
+ import { dashboardSession } from "./dashboard";
5
+
6
+ export interface SavedView {
7
+ id: string;
8
+ resource: string;
9
+ name: string;
10
+ query: string;
11
+ }
12
+
13
+ /** How many views one person may keep per resource, so the sidebar stays a sidebar. */
14
+ export const MAX_VIEWS = 12;
15
+
16
+ /** This person's saved views, oldest first so the sidebar doesn't reshuffle. */
17
+ export async function listViews(resource?: string): Promise<SavedView[]> {
18
+ const { session } = await dashboardSession();
19
+ if (!session) return [];
20
+ const where = resource
21
+ ? and(eq(savedView.userId, session.user.id), eq(savedView.resource, resource))
22
+ : eq(savedView.userId, session.user.id);
23
+ const rows = await getDb()
24
+ .select({ id: savedView.id, resource: savedView.resource, name: savedView.name, query: savedView.query })
25
+ .from(savedView)
26
+ .where(where)
27
+ .orderBy(asc(savedView.createdAt));
28
+ return rows;
29
+ }
@@ -87,11 +87,34 @@ CREATE TABLE `verification` (
87
87
  );
88
88
  --> statement-breakpoint
89
89
  CREATE INDEX `verification_identifier_idx` ON `verification` (`identifier`);--> statement-breakpoint
90
+ CREATE TABLE `audit_log` (
91
+ `id` text PRIMARY KEY NOT NULL,
92
+ `action` text NOT NULL,
93
+ `resource` text NOT NULL,
94
+ `record_id` text,
95
+ `record_label` text,
96
+ `user_id` text,
97
+ `user_email` text,
98
+ `changes` text,
99
+ `ip` text,
100
+ `created_at` integer DEFAULT (cast(unixepoch('subsecond') * 1000 as integer)) NOT NULL
101
+ );
102
+ --> statement-breakpoint
103
+ CREATE INDEX `audit_log_created_at_idx` ON `audit_log` (`created_at`);--> statement-breakpoint
104
+ CREATE INDEX `audit_log_resource_idx` ON `audit_log` (`resource`);--> statement-breakpoint
90
105
  CREATE TABLE `role` (
91
106
  `name` text PRIMARY KEY NOT NULL,
92
107
  `label` text NOT NULL,
93
108
  `created_at` integer DEFAULT (cast(unixepoch('subsecond') * 1000 as integer)) NOT NULL
94
109
  );
95
110
  --> statement-breakpoint
96
- -- Built-in roles: lib/admin.ts lets both into /admin. Add more with `flare role:add`.
97
- INSERT INTO `role` (`name`, `label`) VALUES ('admin', 'Admin'), ('staff', 'Staff');
111
+ CREATE TABLE `saved_view` (
112
+ `id` text PRIMARY KEY NOT NULL,
113
+ `user_id` text NOT NULL,
114
+ `resource` text NOT NULL,
115
+ `name` text NOT NULL,
116
+ `query` text NOT NULL,
117
+ `created_at` integer DEFAULT (cast(unixepoch('subsecond') * 1000 as integer)) NOT NULL
118
+ );
119
+ --> statement-breakpoint
120
+ CREATE INDEX `saved_view_user_idx` ON `saved_view` (`user_id`,`resource`);
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": "6",
3
3
  "dialect": "sqlite",
4
- "id": "79194ad1-2287-43f7-8f37-64fded59352b",
4
+ "id": "06909070-5897-4787-87c1-4e86bf5f98d3",
5
5
  "prevId": "00000000-0000-0000-0000-000000000000",
6
6
  "tables": {
7
7
  "account": {
@@ -606,6 +606,102 @@
606
606
  "uniqueConstraints": {},
607
607
  "checkConstraints": {}
608
608
  },
609
+ "audit_log": {
610
+ "name": "audit_log",
611
+ "columns": {
612
+ "id": {
613
+ "name": "id",
614
+ "type": "text",
615
+ "primaryKey": true,
616
+ "notNull": true,
617
+ "autoincrement": false
618
+ },
619
+ "action": {
620
+ "name": "action",
621
+ "type": "text",
622
+ "primaryKey": false,
623
+ "notNull": true,
624
+ "autoincrement": false
625
+ },
626
+ "resource": {
627
+ "name": "resource",
628
+ "type": "text",
629
+ "primaryKey": false,
630
+ "notNull": true,
631
+ "autoincrement": false
632
+ },
633
+ "record_id": {
634
+ "name": "record_id",
635
+ "type": "text",
636
+ "primaryKey": false,
637
+ "notNull": false,
638
+ "autoincrement": false
639
+ },
640
+ "record_label": {
641
+ "name": "record_label",
642
+ "type": "text",
643
+ "primaryKey": false,
644
+ "notNull": false,
645
+ "autoincrement": false
646
+ },
647
+ "user_id": {
648
+ "name": "user_id",
649
+ "type": "text",
650
+ "primaryKey": false,
651
+ "notNull": false,
652
+ "autoincrement": false
653
+ },
654
+ "user_email": {
655
+ "name": "user_email",
656
+ "type": "text",
657
+ "primaryKey": false,
658
+ "notNull": false,
659
+ "autoincrement": false
660
+ },
661
+ "changes": {
662
+ "name": "changes",
663
+ "type": "text",
664
+ "primaryKey": false,
665
+ "notNull": false,
666
+ "autoincrement": false
667
+ },
668
+ "ip": {
669
+ "name": "ip",
670
+ "type": "text",
671
+ "primaryKey": false,
672
+ "notNull": false,
673
+ "autoincrement": false
674
+ },
675
+ "created_at": {
676
+ "name": "created_at",
677
+ "type": "integer",
678
+ "primaryKey": false,
679
+ "notNull": true,
680
+ "autoincrement": false,
681
+ "default": "(cast(unixepoch('subsecond') * 1000 as integer))"
682
+ }
683
+ },
684
+ "indexes": {
685
+ "audit_log_created_at_idx": {
686
+ "name": "audit_log_created_at_idx",
687
+ "columns": [
688
+ "created_at"
689
+ ],
690
+ "isUnique": false
691
+ },
692
+ "audit_log_resource_idx": {
693
+ "name": "audit_log_resource_idx",
694
+ "columns": [
695
+ "resource"
696
+ ],
697
+ "isUnique": false
698
+ }
699
+ },
700
+ "foreignKeys": {},
701
+ "compositePrimaryKeys": {},
702
+ "uniqueConstraints": {},
703
+ "checkConstraints": {}
704
+ },
609
705
  "role": {
610
706
  "name": "role",
611
707
  "columns": {
@@ -637,6 +733,68 @@
637
733
  "compositePrimaryKeys": {},
638
734
  "uniqueConstraints": {},
639
735
  "checkConstraints": {}
736
+ },
737
+ "saved_view": {
738
+ "name": "saved_view",
739
+ "columns": {
740
+ "id": {
741
+ "name": "id",
742
+ "type": "text",
743
+ "primaryKey": true,
744
+ "notNull": true,
745
+ "autoincrement": false
746
+ },
747
+ "user_id": {
748
+ "name": "user_id",
749
+ "type": "text",
750
+ "primaryKey": false,
751
+ "notNull": true,
752
+ "autoincrement": false
753
+ },
754
+ "resource": {
755
+ "name": "resource",
756
+ "type": "text",
757
+ "primaryKey": false,
758
+ "notNull": true,
759
+ "autoincrement": false
760
+ },
761
+ "name": {
762
+ "name": "name",
763
+ "type": "text",
764
+ "primaryKey": false,
765
+ "notNull": true,
766
+ "autoincrement": false
767
+ },
768
+ "query": {
769
+ "name": "query",
770
+ "type": "text",
771
+ "primaryKey": false,
772
+ "notNull": true,
773
+ "autoincrement": false
774
+ },
775
+ "created_at": {
776
+ "name": "created_at",
777
+ "type": "integer",
778
+ "primaryKey": false,
779
+ "notNull": true,
780
+ "autoincrement": false,
781
+ "default": "(cast(unixepoch('subsecond') * 1000 as integer))"
782
+ }
783
+ },
784
+ "indexes": {
785
+ "saved_view_user_idx": {
786
+ "name": "saved_view_user_idx",
787
+ "columns": [
788
+ "user_id",
789
+ "resource"
790
+ ],
791
+ "isUnique": false
792
+ }
793
+ },
794
+ "foreignKeys": {},
795
+ "compositePrimaryKeys": {},
796
+ "uniqueConstraints": {},
797
+ "checkConstraints": {}
640
798
  }
641
799
  },
642
800
  "views": {},
@@ -5,7 +5,7 @@
5
5
  {
6
6
  "idx": 0,
7
7
  "version": "6",
8
- "when": 1790089765410,
8
+ "when": 1790112915429,
9
9
  "tag": "0000_init",
10
10
  "breakpoints": true
11
11
  }
@@ -1,31 +1,41 @@
1
- import { defineConfig, loadEnv } from "vite";
2
- import vinext from "vinext";
3
- import { cloudflare } from "@cloudflare/vite-plugin";
4
- import { kvDataAdapter } from "@vinext/cloudflare/cache/kv-data-adapter";
5
-
6
- export default defineConfig(({ mode }) => ({
7
- // FLARE_THEME (shell or .env) picks the theme for this build; see lib/theme.ts.
8
- define: { __FLARE_THEME__: JSON.stringify(loadEnv(mode, process.cwd(), "FLARE_").FLARE_THEME ?? "") },
9
- // `flare dev --tunnel` serves the dev server on a https://*.trycloudflare.com URL.
10
- server: { allowedHosts: [".trycloudflare.com"] },
11
- plugins: [
12
- vinext({
13
- cache: {
14
- // Data cache (`cached()`, fetch, "use cache") in Workers KV, shared by every colo.
15
- // Entries live a day unless a tag revalidates them first; a colo may reuse its own
16
- // copy for a minute after a write elsewhere.
17
- data: kvDataAdapter({ ttlSeconds: 86_400, entryCacheTtlSeconds: 60 }),
18
- // No `cdn` adapter yet. vinext's Workers Cache adapter (cdnAdapter) is experimental
19
- // in the 1.0 beta: behind it, any page that redirects fails with "Too many
20
- // redirects", and nothing is cached without an experimental two-stage deploy.
21
- // See "Caching" in the Flare docs before turning it on.
22
- },
23
- }),
24
- cloudflare({
25
- viteEnvironment: {
26
- name: "rsc",
27
- childEnvironments: ["ssr"],
28
- },
29
- }),
30
- ],
31
- }));
1
+ import { defineConfig, loadEnv } from "vite";
2
+ import vinext from "vinext";
3
+ import { cloudflare } from "@cloudflare/vite-plugin";
4
+ import { kvDataAdapter } from "@vinext/cloudflare/cache/kv-data-adapter";
5
+
6
+ export default defineConfig(({ mode }) => ({
7
+ // FLARE_THEME (shell or .env) picks the theme for this build; see lib/theme.ts.
8
+ define: { __FLARE_THEME__: JSON.stringify(loadEnv(mode, process.cwd(), "FLARE_").FLARE_THEME ?? "") },
9
+ // `flare dev --tunnel` serves the dev server on a https://*.trycloudflare.com URL.
10
+ server: { allowedHosts: [".trycloudflare.com"] },
11
+ optimizeDeps: {
12
+ /*
13
+ * Packages that ship their own "use client" components are left out of dependency
14
+ * pre-bundling. Bundled, the browser and the server each end up with their own copy
15
+ * of the same module, and a client-side navigation lands on the wrong one — which is
16
+ * the "Cannot read properties of null (reading 'useSyncExternalStore')" you get once
17
+ * and never again after a reload.
18
+ */
19
+ exclude: ["lucide-react", "vinext", "radix-ui", "@radix-ui/react-slot", "sonner", "cmdk", "react-day-picker", "@flaredev/core"],
20
+ },
21
+ plugins: [
22
+ vinext({
23
+ cache: {
24
+ // Data cache (`cached()`, fetch, "use cache") in Workers KV, shared by every colo.
25
+ // Entries live a day unless a tag revalidates them first; a colo may reuse its own
26
+ // copy for a minute after a write elsewhere.
27
+ data: kvDataAdapter({ ttlSeconds: 86_400, entryCacheTtlSeconds: 60 }),
28
+ // No `cdn` adapter yet. vinext's Workers Cache adapter (cdnAdapter) is experimental
29
+ // in the 1.0 beta: behind it, any page that redirects fails with "Too many
30
+ // redirects", and nothing is cached without an experimental two-stage deploy.
31
+ // See "Caching" in the Flare docs before turning it on.
32
+ },
33
+ }),
34
+ cloudflare({
35
+ viteEnvironment: {
36
+ name: "rsc",
37
+ childEnvironments: ["ssr"],
38
+ },
39
+ }),
40
+ ],
41
+ }));