@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.
- package/bin/flare.js +9 -1
- package/dist/index.js +991 -344
- package/package.json +2 -2
- package/templates/app/app/admin/page.tsx +9 -61
- package/templates/app/app/dashboard/account/layout.tsx +30 -0
- package/templates/app/app/dashboard/account/page.tsx +11 -31
- package/templates/app/app/dashboard/account/password/page.tsx +22 -0
- package/templates/app/app/dashboard/account/security/page.tsx +34 -0
- package/templates/app/app/dashboard/account/sessions/page.tsx +9 -0
- package/templates/app/app/dashboard/actions.ts +246 -0
- package/templates/app/app/dashboard/import-actions.ts +91 -0
- package/templates/app/app/dashboard/layout.tsx +66 -39
- package/templates/app/app/dashboard/observability/page.tsx +175 -0
- package/templates/app/app/dashboard/page.tsx +108 -27
- package/templates/app/app/verify-email/page.tsx +33 -0
- package/templates/app/components/account/account-nav.tsx +39 -0
- package/templates/app/components/account/{security-settings.tsx → sections.tsx} +507 -525
- package/templates/app/components/auth/otp-input.tsx +95 -0
- package/templates/app/components/auth/password-field.tsx +116 -0
- package/templates/app/components/auth/password-forms.tsx +76 -79
- package/templates/app/components/auth/sign-in-flow.tsx +306 -315
- package/templates/app/components/auth/sign-up-form.tsx +93 -101
- package/templates/app/components/auth/two-factor-form.tsx +139 -124
- package/templates/app/components/auth/use-resend.ts +44 -0
- package/templates/app/components/auth/verify-email-form.tsx +135 -0
- package/templates/app/components/dashboard/column-menu.tsx +66 -0
- package/templates/app/components/dashboard/dashboard-header.tsx +115 -0
- package/templates/app/components/dashboard/dashboard-sidebar.tsx +228 -0
- package/templates/app/components/dashboard/dropzone.tsx +92 -0
- package/templates/app/components/dashboard/editable.ts +12 -0
- package/templates/app/components/dashboard/export-button.tsx +41 -0
- package/templates/app/components/{admin → dashboard}/fields/file-field.tsx +22 -18
- package/templates/app/components/dashboard/import-dialog.tsx +540 -0
- package/templates/app/components/dashboard/inline-cell.tsx +165 -0
- package/templates/app/components/dashboard/local-time.tsx +77 -0
- package/templates/app/components/dashboard/page-header.tsx +57 -0
- package/templates/app/components/{admin → dashboard}/query.ts +2 -0
- package/templates/app/components/dashboard/record-actions.tsx +113 -0
- package/templates/app/components/dashboard/record-detail.tsx +200 -0
- package/templates/app/components/dashboard/resource-chart.tsx +91 -0
- package/templates/app/components/dashboard/resource-form-dialog.tsx +80 -0
- package/templates/app/components/{admin → dashboard}/resource-form-page.tsx +61 -55
- package/templates/app/components/{admin → dashboard}/resource-form.tsx +165 -139
- package/templates/app/components/dashboard/resource-stats.tsx +48 -0
- package/templates/app/components/{admin → dashboard}/resource-table.tsx +152 -28
- package/templates/app/components/{admin → dashboard}/row-actions.tsx +56 -11
- package/templates/app/components/dashboard/save-view-button.tsx +86 -0
- package/templates/app/components/dashboard/stat-card.tsx +69 -0
- package/templates/app/components/dashboard/table-selection.tsx +185 -0
- package/templates/app/components/dashboard/traffic-chart.tsx +32 -0
- package/templates/app/components/dashboard/user-menu.tsx +91 -0
- package/templates/app/components/ui/sonner.tsx +33 -13
- package/templates/app/db/flare-schema.ts +55 -1
- package/templates/app/lib/analytics.ts +121 -0
- package/templates/app/lib/api.ts +12 -0
- package/templates/app/lib/audit.ts +80 -0
- package/templates/app/lib/auth-config.ts +38 -33
- package/templates/app/lib/auth.ts +34 -0
- package/templates/app/lib/cache.ts +83 -83
- package/templates/app/lib/csv.ts +24 -0
- package/templates/app/lib/dashboard-nav.ts +15 -0
- package/templates/app/lib/dashboard.ts +165 -0
- package/templates/app/lib/password-rules.ts +159 -0
- package/templates/app/lib/site.ts +56 -47
- package/templates/app/lib/views.ts +29 -0
- package/templates/app/migrations/0000_init.sql +25 -2
- package/templates/app/migrations/meta/0000_snapshot.json +159 -1
- package/templates/app/migrations/meta/_journal.json +1 -1
- package/templates/app/vite.config.ts +41 -31
- package/templates/app/worker-configuration.d.ts +15764 -0
- package/templates/locks/package-lock.json +9189 -0
- package/templates/locks/pnpm-lock.yaml +6347 -0
- package/templates/locks/yarn.lock +3548 -0
- package/templates/app/app/admin/actions.ts +0 -113
- package/templates/app/app/admin/layout.tsx +0 -32
- package/templates/app/components/admin/admin-header.tsx +0 -78
- package/templates/app/components/admin/resource-nav.tsx +0 -120
- package/templates/app/components/dashboard/dashboard-nav.tsx +0 -35
- package/templates/app/lib/admin-nav.ts +0 -15
- package/templates/app/lib/admin.ts +0 -103
- /package/templates/app/components/{admin → dashboard}/fields/country-field.tsx +0 -0
- /package/templates/app/components/{admin → dashboard}/fields/date-field.tsx +0 -0
- /package/templates/app/components/{admin → dashboard}/fields/field-widget.tsx +0 -0
- /package/templates/app/components/{admin → dashboard}/fields/multi-select-field.tsx +0 -0
- /package/templates/app/components/{admin → dashboard}/fields/phone-field.tsx +0 -0
- /package/templates/app/components/{admin → dashboard}/fields/relation-field.tsx +0 -0
- /package/templates/app/components/{admin → dashboard}/resource-icon.tsx +0 -0
- /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
|
-
/**
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
"
|
|
33
|
-
"
|
|
34
|
-
"
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
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
|
-
|
|
97
|
-
|
|
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": "
|
|
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": {},
|
|
@@ -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
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
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
|
+
}));
|