@fluid-app/fluid-cli-portal 0.1.7 → 0.1.9
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/dist/index.d.mts +607 -1
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +1474 -35
- package/dist/index.mjs.map +1 -1
- package/dist/pull-p1mSVa5W.mjs +1148 -0
- package/dist/pull-p1mSVa5W.mjs.map +1 -0
- package/dist/vite-plugin.d.mts +63 -0
- package/dist/vite-plugin.d.mts.map +1 -0
- package/dist/vite-plugin.mjs +227 -0
- package/dist/vite-plugin.mjs.map +1 -0
- package/package.json +8 -3
- package/templates/base/.oxlintrc.json +9 -0
- package/templates/base/src/index.css +1 -156
- package/templates/base/src/main.tsx +3 -40
- package/templates/base/src/portal.config.ts +18 -10
- package/templates/base/src/screens/ExampleForm.tsx +4 -11
- package/templates/fullstack/package.json.template +2 -5
- package/templates/fullstack/vite.config.ts +2 -0
- package/templates/starter/package.json.template +3 -1
- package/templates/starter/vite.config.ts +19 -3
- package/templates/base/src/App.tsx +0 -18
- package/templates/fullstack/eslint.config.js +0 -13
- package/templates/fullstack/src/fluid.config.ts.template +0 -62
- package/templates/starter/src/fluid.config.ts.template +0 -62
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fluid-app/fluid-cli-portal",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.9",
|
|
4
4
|
"description": "Fluid CLI plugin for building and deploying portal applications",
|
|
5
5
|
"files": [
|
|
6
6
|
"dist",
|
|
@@ -13,6 +13,10 @@
|
|
|
13
13
|
".": {
|
|
14
14
|
"import": "./dist/index.mjs",
|
|
15
15
|
"types": "./dist/index.d.mts"
|
|
16
|
+
},
|
|
17
|
+
"./vite-plugin": {
|
|
18
|
+
"import": "./dist/vite-plugin.mjs",
|
|
19
|
+
"types": "./dist/vite-plugin.d.mts"
|
|
16
20
|
}
|
|
17
21
|
},
|
|
18
22
|
"publishConfig": {
|
|
@@ -26,6 +30,7 @@
|
|
|
26
30
|
"fs-extra": "^11.2.0",
|
|
27
31
|
"handlebars": "^4.7.8",
|
|
28
32
|
"ora": "^8.0.0",
|
|
33
|
+
"p-limit": "^7.3.0",
|
|
29
34
|
"prompts": "^2.4.2",
|
|
30
35
|
"tsx": "^4.21.0",
|
|
31
36
|
"@fluid-app/fluid-cli": "0.1.1"
|
|
@@ -39,8 +44,8 @@
|
|
|
39
44
|
"jest": "^29.7.0",
|
|
40
45
|
"tsdown": "^0.21.0",
|
|
41
46
|
"typescript": "^5",
|
|
42
|
-
"@fluid-app/
|
|
43
|
-
"@fluid-app/
|
|
47
|
+
"@fluid-app/typescript-config": "0.0.0",
|
|
48
|
+
"@fluid-app/fluidos-api-client": "0.1.0"
|
|
44
49
|
},
|
|
45
50
|
"engines": {
|
|
46
51
|
"node": ">=18.0.0"
|
|
@@ -1,159 +1,4 @@
|
|
|
1
1
|
@import "tailwindcss";
|
|
2
2
|
@import "@fluid-app/portal-sdk/globals.css";
|
|
3
|
-
@source "../node_modules/@fluid-app/portal-sdk/dist/**/*.mjs";
|
|
4
3
|
|
|
5
|
-
/*
|
|
6
|
-
* Fallback design tokens (OKLCH format).
|
|
7
|
-
*
|
|
8
|
-
* The theme engine injects higher-specificity CSS via [data-theme="..."]
|
|
9
|
-
* which overrides these at runtime for both light and dark modes.
|
|
10
|
-
*
|
|
11
|
-
* Canonical variables use the --color- prefix to match the css-generator
|
|
12
|
-
* output and the widgets tailwind.config.ts color palette registration.
|
|
13
|
-
* Short-name aliases (--background, --foreground, etc.) are included for
|
|
14
|
-
* backward compatibility with shadcn/Radix components.
|
|
15
|
-
*/
|
|
16
|
-
|
|
17
|
-
/*
|
|
18
|
-
* Register CSS variables as Tailwind theme values.
|
|
19
|
-
* `inline` means Tailwind won't emit its own --color-* declarations —
|
|
20
|
-
* we define them ourselves in :root below (and the theme engine overrides
|
|
21
|
-
* them at runtime via [data-theme="..."]).
|
|
22
|
-
*/
|
|
23
|
-
@theme inline {
|
|
24
|
-
--color-background: var(--background);
|
|
25
|
-
--color-foreground: var(--foreground);
|
|
26
|
-
--color-primary: var(--primary);
|
|
27
|
-
--color-primary-foreground: var(--primary-foreground);
|
|
28
|
-
--color-secondary: var(--secondary);
|
|
29
|
-
--color-secondary-foreground: var(--secondary-foreground);
|
|
30
|
-
--color-muted: var(--muted);
|
|
31
|
-
--color-muted-foreground: var(--muted-foreground);
|
|
32
|
-
--color-accent: var(--accent);
|
|
33
|
-
--color-accent-foreground: var(--accent-foreground);
|
|
34
|
-
--color-destructive: var(--destructive);
|
|
35
|
-
--color-destructive-foreground: var(--destructive-foreground);
|
|
36
|
-
--color-border: var(--border);
|
|
37
|
-
--color-input: var(--input);
|
|
38
|
-
--color-ring: var(--ring);
|
|
39
|
-
--color-sidebar: var(--sidebar);
|
|
40
|
-
--color-sidebar-foreground: var(--sidebar-foreground);
|
|
41
|
-
--color-sidebar-primary: var(--sidebar-primary);
|
|
42
|
-
--color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
|
|
43
|
-
--color-sidebar-accent: var(--sidebar-accent);
|
|
44
|
-
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
|
|
45
|
-
--color-sidebar-border: var(--sidebar-border);
|
|
46
|
-
--color-sidebar-ring: var(--sidebar-ring);
|
|
47
|
-
--radius: var(--radius);
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
:root {
|
|
51
|
-
--color-background: oklch(1 0 0);
|
|
52
|
-
--color-foreground: oklch(0.145 0 0);
|
|
53
|
-
--color-primary: oklch(0.205 0 0);
|
|
54
|
-
--color-primary-foreground: oklch(0.985 0 0);
|
|
55
|
-
--color-secondary: oklch(0.97 0 0);
|
|
56
|
-
--color-secondary-foreground: oklch(0.205 0 0);
|
|
57
|
-
--color-muted: oklch(0.97 0 0);
|
|
58
|
-
--color-muted-foreground: oklch(0.556 0 0);
|
|
59
|
-
--color-accent: oklch(0.97 0 0);
|
|
60
|
-
--color-accent-foreground: oklch(0.205 0 0);
|
|
61
|
-
--color-destructive: oklch(0.577 0.245 27.325);
|
|
62
|
-
--color-destructive-foreground: oklch(0.985 0 0);
|
|
63
|
-
--color-border: oklch(0.922 0 0);
|
|
64
|
-
|
|
65
|
-
/* Short-name aliases (match css-generator globalCSSOverride) */
|
|
66
|
-
--background: var(--color-background);
|
|
67
|
-
--foreground: var(--color-foreground);
|
|
68
|
-
--primary: var(--color-primary);
|
|
69
|
-
--primary-foreground: var(--color-primary-foreground);
|
|
70
|
-
--secondary: var(--color-secondary);
|
|
71
|
-
--secondary-foreground: var(--color-secondary-foreground);
|
|
72
|
-
--muted: var(--color-muted);
|
|
73
|
-
--muted-foreground: var(--color-muted-foreground);
|
|
74
|
-
--accent: var(--color-accent);
|
|
75
|
-
--accent-foreground: var(--color-accent-foreground);
|
|
76
|
-
--destructive: var(--color-destructive);
|
|
77
|
-
--destructive-foreground: var(--color-destructive-foreground);
|
|
78
|
-
--border: var(--color-border);
|
|
79
|
-
--input: var(--color-border);
|
|
80
|
-
--ring: var(--color-primary);
|
|
81
|
-
--radius: 0.625rem;
|
|
82
|
-
|
|
83
|
-
/* Sidebar aliases */
|
|
84
|
-
--sidebar: var(--color-muted);
|
|
85
|
-
--sidebar-foreground: var(--color-muted-foreground);
|
|
86
|
-
--sidebar-primary: var(--color-primary);
|
|
87
|
-
--sidebar-primary-foreground: var(--color-primary-foreground);
|
|
88
|
-
--sidebar-accent: var(--color-accent);
|
|
89
|
-
--sidebar-accent-foreground: var(--color-accent-foreground);
|
|
90
|
-
--sidebar-border: var(--color-border);
|
|
91
|
-
--sidebar-ring: var(--color-primary);
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
/*
|
|
95
|
-
* Dark-mode fallback tokens.
|
|
96
|
-
* Applied when the user toggles dark mode but no API theme is loaded.
|
|
97
|
-
* The theme engine will override these with richer palette-derived values
|
|
98
|
-
* once a theme is fetched from the API.
|
|
99
|
-
*/
|
|
100
|
-
[data-theme-mode="dark"] {
|
|
101
|
-
--color-background: oklch(0.145 0 0);
|
|
102
|
-
--color-foreground: oklch(0.985 0 0);
|
|
103
|
-
--color-primary: oklch(0.985 0 0);
|
|
104
|
-
--color-primary-foreground: oklch(0.205 0 0);
|
|
105
|
-
--color-secondary: oklch(0.215 0 0);
|
|
106
|
-
--color-secondary-foreground: oklch(0.985 0 0);
|
|
107
|
-
--color-muted: oklch(0.215 0 0);
|
|
108
|
-
--color-muted-foreground: oklch(0.708 0 0);
|
|
109
|
-
--color-accent: oklch(0.215 0 0);
|
|
110
|
-
--color-accent-foreground: oklch(0.985 0 0);
|
|
111
|
-
--color-destructive: oklch(0.577 0.245 27.325);
|
|
112
|
-
--color-destructive-foreground: oklch(0.985 0 0);
|
|
113
|
-
--color-border: oklch(0.3 0 0);
|
|
114
|
-
|
|
115
|
-
--background: var(--color-background);
|
|
116
|
-
--foreground: var(--color-foreground);
|
|
117
|
-
--primary: var(--color-primary);
|
|
118
|
-
--primary-foreground: var(--color-primary-foreground);
|
|
119
|
-
--secondary: var(--color-secondary);
|
|
120
|
-
--secondary-foreground: var(--color-secondary-foreground);
|
|
121
|
-
--muted: var(--color-muted);
|
|
122
|
-
--muted-foreground: var(--color-muted-foreground);
|
|
123
|
-
--accent: var(--color-accent);
|
|
124
|
-
--accent-foreground: var(--color-accent-foreground);
|
|
125
|
-
--destructive: var(--color-destructive);
|
|
126
|
-
--destructive-foreground: var(--color-destructive-foreground);
|
|
127
|
-
--border: var(--color-border);
|
|
128
|
-
--input: var(--color-border);
|
|
129
|
-
--ring: var(--color-primary);
|
|
130
|
-
|
|
131
|
-
--sidebar: var(--color-muted);
|
|
132
|
-
--sidebar-foreground: var(--color-muted-foreground);
|
|
133
|
-
--sidebar-primary: var(--color-primary);
|
|
134
|
-
--sidebar-primary-foreground: var(--color-primary-foreground);
|
|
135
|
-
--sidebar-accent: var(--color-accent);
|
|
136
|
-
--sidebar-accent-foreground: var(--color-accent-foreground);
|
|
137
|
-
--sidebar-border: var(--color-border);
|
|
138
|
-
--sidebar-ring: var(--color-primary);
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
@layer base {
|
|
142
|
-
* {
|
|
143
|
-
@apply border-border;
|
|
144
|
-
}
|
|
145
|
-
body {
|
|
146
|
-
@apply bg-background text-foreground;
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
/* Hide scrollbars while preserving scroll functionality */
|
|
151
|
-
@layer utilities {
|
|
152
|
-
.scrollbar-none {
|
|
153
|
-
-ms-overflow-style: none;
|
|
154
|
-
scrollbar-width: none;
|
|
155
|
-
}
|
|
156
|
-
.scrollbar-none::-webkit-scrollbar {
|
|
157
|
-
display: none;
|
|
158
|
-
}
|
|
159
|
-
}
|
|
4
|
+
/* Add your custom styles below */
|
|
@@ -1,42 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
FluidAuthProvider,
|
|
5
|
-
FluidProvider,
|
|
6
|
-
RequireAuth,
|
|
7
|
-
} from "@fluid-app/portal-sdk";
|
|
8
|
-
import { authConfig, fluidConfig } from "./fluid.config";
|
|
9
|
-
import App from "./App";
|
|
1
|
+
import { createPortal } from "@fluid-app/portal-sdk";
|
|
2
|
+
import { customPages } from "./portal.config";
|
|
10
3
|
import "./index.css";
|
|
11
4
|
|
|
12
|
-
|
|
13
|
-
const rootElement = document.getElementById("root");
|
|
14
|
-
if (!rootElement) {
|
|
15
|
-
throw new Error("Root element not found");
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* App Wrapper with Authentication
|
|
20
|
-
*
|
|
21
|
-
* The provider hierarchy is:
|
|
22
|
-
* 1. FluidAuthProvider - Handles token extraction, validation, storage
|
|
23
|
-
* 2. FluidProvider - Fluid SDK context (API client, theme, QueryClientProvider)
|
|
24
|
-
* 3. RequireAuth - Only renders children when authenticated
|
|
25
|
-
*
|
|
26
|
-
* Authentication flow:
|
|
27
|
-
* 1. Parent Fluid app opens this portal with ?fluidUserToken=... in URL
|
|
28
|
-
* 2. FluidAuthProvider extracts token, validates it, stores in cookie/localStorage
|
|
29
|
-
* 3. Token is removed from URL (security)
|
|
30
|
-
* 4. RequireAuth shows loading while checking, then renders app or error
|
|
31
|
-
*/
|
|
32
|
-
createRoot(rootElement).render(
|
|
33
|
-
<StrictMode>
|
|
34
|
-
<FluidAuthProvider config={authConfig}>
|
|
35
|
-
<FluidProvider config={fluidConfig}>
|
|
36
|
-
<RequireAuth>
|
|
37
|
-
<App />
|
|
38
|
-
</RequireAuth>
|
|
39
|
-
</FluidProvider>
|
|
40
|
-
</FluidAuthProvider>
|
|
41
|
-
</StrictMode>,
|
|
42
|
-
);
|
|
5
|
+
createPortal({ customPages });
|
|
@@ -1,22 +1,18 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Portal Configuration
|
|
3
3
|
*
|
|
4
|
-
* Register custom page components
|
|
5
|
-
* when a navigation item's slug matches the key in this map.
|
|
4
|
+
* Register custom page components and widget plugins here.
|
|
6
5
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
6
|
+
* - customPages: rendered by AppShell when a navigation slug matches
|
|
7
|
+
* - customWidgets: registered as plugin widgets in the builder and SDK
|
|
9
8
|
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* 2. Import it below and add it to customPages
|
|
13
|
-
* 3. Add a matching entry to src/navigation.config.ts
|
|
9
|
+
* Navigation structure is defined in navigation.config.ts and synced
|
|
10
|
+
* to the Fluid OS API on `fluid deploy`.
|
|
14
11
|
*/
|
|
15
12
|
|
|
16
13
|
import type { ComponentType } from "react";
|
|
14
|
+
import type { WidgetManifest } from "@fluid-app/portal-core/registries";
|
|
17
15
|
import { DashboardScreen } from "./screens/Dashboard";
|
|
18
|
-
// Uncomment after installing form deps (pnpm add react-hook-form zod @hookform/resolvers):
|
|
19
|
-
// import { ExampleFormScreen } from "./screens/ExampleForm";
|
|
20
16
|
|
|
21
17
|
/**
|
|
22
18
|
* Custom page components keyed by navigation slug.
|
|
@@ -30,3 +26,15 @@ export const customPages: Record<string, ComponentType> = {
|
|
|
30
26
|
// Add more custom screens here:
|
|
31
27
|
// "example-form": ExampleFormScreen,
|
|
32
28
|
};
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Custom widget plugins.
|
|
32
|
+
*
|
|
33
|
+
* Each manifest registers a widget in both the builder palette
|
|
34
|
+
* and the SDK runtime. Add manifests here after scaffolding
|
|
35
|
+
* with `fluid widget create <name>`.
|
|
36
|
+
*/
|
|
37
|
+
export const customWidgets: WidgetManifest[] = [
|
|
38
|
+
// Import and add widget manifests here:
|
|
39
|
+
// stockTicker,
|
|
40
|
+
];
|
|
@@ -1,16 +1,10 @@
|
|
|
1
|
-
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
2
|
-
// @ts-nocheck — This file requires optional dependencies (react-hook-form, zod, @hookform/resolvers).
|
|
3
|
-
// Install them before removing this directive: pnpm add react-hook-form zod @hookform/resolvers
|
|
4
1
|
/**
|
|
5
2
|
* Example Form Screen
|
|
6
3
|
*
|
|
7
4
|
* Demonstrates how to build forms in a portal using
|
|
8
5
|
* React Hook Form + Zod for validation.
|
|
9
6
|
*
|
|
10
|
-
*
|
|
11
|
-
* pnpm add react-hook-form zod @hookform/resolvers
|
|
12
|
-
*
|
|
13
|
-
* Then uncomment the nav entry and import in portal.config.ts.
|
|
7
|
+
* Uncomment the nav entry and import in portal.config.ts to enable.
|
|
14
8
|
*/
|
|
15
9
|
|
|
16
10
|
import { useForm } from "react-hook-form";
|
|
@@ -51,10 +45,9 @@ export function ExampleFormScreen() {
|
|
|
51
45
|
resolver: zodResolver(contactFormSchema),
|
|
52
46
|
});
|
|
53
47
|
|
|
54
|
-
const onSubmit = async (
|
|
48
|
+
const onSubmit = async (_data: ContactFormData) => {
|
|
55
49
|
// Replace with your API call, e.g.:
|
|
56
|
-
// const result = await apiClient.post("/contacts",
|
|
57
|
-
console.log("Form submitted:", data);
|
|
50
|
+
// const result = await apiClient.post("/contacts", _data);
|
|
58
51
|
|
|
59
52
|
// Simulate API delay
|
|
60
53
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
@@ -77,7 +70,7 @@ export function ExampleFormScreen() {
|
|
|
77
70
|
<div className="border-border bg-background rounded-lg border p-6 shadow-sm">
|
|
78
71
|
{isSubmitSuccessful ? (
|
|
79
72
|
<div className="border-accent bg-accent text-accent-foreground rounded-lg border p-4 text-sm">
|
|
80
|
-
Form submitted successfully!
|
|
73
|
+
Form submitted successfully!
|
|
81
74
|
</div>
|
|
82
75
|
) : (
|
|
83
76
|
<form onSubmit={handleSubmit(onSubmit)} className="space-y-4">
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
"start": "node dist/server/entry.js",
|
|
10
10
|
"preview": "vite preview",
|
|
11
11
|
"typecheck": "tsc --noEmit",
|
|
12
|
-
"lint": "
|
|
12
|
+
"lint": "oxlint",
|
|
13
13
|
"db:generate": "drizzle-kit generate",
|
|
14
14
|
"db:migrate": "drizzle-kit migrate",
|
|
15
15
|
"db:push": "drizzle-kit push",
|
|
@@ -33,7 +33,6 @@
|
|
|
33
33
|
},
|
|
34
34
|
"devDependencies": {
|
|
35
35
|
"@fluid-app/fluid-cli-portal": "{{sdkVersion}}",
|
|
36
|
-
"@eslint/js": "^9.0.0",
|
|
37
36
|
"@hono/vite-dev-server": "^0.25.0",
|
|
38
37
|
"@tailwindcss/vite": "^4.0.0",
|
|
39
38
|
"esbuild": "^0.25.0",
|
|
@@ -42,11 +41,9 @@
|
|
|
42
41
|
"@types/react-dom": "^19.0.0",
|
|
43
42
|
"@vitejs/plugin-react": "^4.3.0",
|
|
44
43
|
"drizzle-kit": "^0.31.0",
|
|
45
|
-
"
|
|
46
|
-
"eslint-plugin-react-hooks": "^5.0.0",
|
|
44
|
+
"oxlint": "^1.51.0",
|
|
47
45
|
"tailwindcss": "^4.0.0",
|
|
48
46
|
"typescript": "^5.6.0",
|
|
49
|
-
"typescript-eslint": "^8.0.0",
|
|
50
47
|
"vite": "^6.0.0",
|
|
51
48
|
"vitest": "^4.0.0"
|
|
52
49
|
},
|
|
@@ -2,11 +2,13 @@ import { defineConfig } from "vite";
|
|
|
2
2
|
import react from "@vitejs/plugin-react";
|
|
3
3
|
import tailwindcss from "@tailwindcss/vite";
|
|
4
4
|
import devServer from "@hono/vite-dev-server";
|
|
5
|
+
import { fluidManifestPlugin } from "@fluid-app/portal-sdk/vite";
|
|
5
6
|
|
|
6
7
|
export default defineConfig({
|
|
7
8
|
plugins: [
|
|
8
9
|
react(),
|
|
9
10
|
tailwindcss(),
|
|
11
|
+
fluidManifestPlugin(),
|
|
10
12
|
devServer({
|
|
11
13
|
entry: "src/server/index.ts",
|
|
12
14
|
// Only send /api/* requests to Hono — let Vite handle everything else
|
|
@@ -7,7 +7,8 @@
|
|
|
7
7
|
"dev": "vite",
|
|
8
8
|
"build": "tsc -b && vite build",
|
|
9
9
|
"preview": "vite preview",
|
|
10
|
-
"typecheck": "tsc --noEmit"
|
|
10
|
+
"typecheck": "tsc --noEmit",
|
|
11
|
+
"lint": "oxlint"
|
|
11
12
|
},
|
|
12
13
|
"dependencies": {
|
|
13
14
|
"@fluid-app/portal-sdk": "{{sdkVersion}}",
|
|
@@ -24,6 +25,7 @@
|
|
|
24
25
|
"@types/react-dom": "^19.0.0",
|
|
25
26
|
"@vitejs/plugin-react": "^4.3.0",
|
|
26
27
|
"tailwindcss": "^4.0.0",
|
|
28
|
+
"oxlint": "^1.51.0",
|
|
27
29
|
"typescript": "^5.6.0",
|
|
28
30
|
"vite": "^6.0.0"
|
|
29
31
|
},
|
|
@@ -1,12 +1,28 @@
|
|
|
1
1
|
import { defineConfig } from "vite";
|
|
2
2
|
import react from "@vitejs/plugin-react";
|
|
3
3
|
import tailwindcss from "@tailwindcss/vite";
|
|
4
|
+
import { fluidManifestPlugin } from "@fluid-app/portal-sdk/vite";
|
|
4
5
|
|
|
5
|
-
// https://vite.dev/config/
|
|
6
6
|
export default defineConfig({
|
|
7
|
-
plugins: [react(), tailwindcss()],
|
|
7
|
+
plugins: [react(), tailwindcss(), fluidManifestPlugin()],
|
|
8
8
|
build: {
|
|
9
|
-
// Target modern browsers for smaller bundles
|
|
10
9
|
target: "esnext",
|
|
10
|
+
chunkSizeWarningLimit: 600,
|
|
11
|
+
rollupOptions: {
|
|
12
|
+
output: {
|
|
13
|
+
manualChunks(id) {
|
|
14
|
+
if (
|
|
15
|
+
id.includes("node_modules/react-dom") ||
|
|
16
|
+
id.includes("node_modules/react/") ||
|
|
17
|
+
id.includes("node_modules/scheduler")
|
|
18
|
+
) {
|
|
19
|
+
return "vendor";
|
|
20
|
+
}
|
|
21
|
+
if (id.includes("node_modules/@tanstack/react-query")) {
|
|
22
|
+
return "query";
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
},
|
|
11
27
|
},
|
|
12
28
|
});
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import { AppShell } from "@fluid-app/portal-sdk";
|
|
2
|
-
import { customPages } from "./portal.config";
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Main App component
|
|
6
|
-
*
|
|
7
|
-
* Uses the AppShell from @fluid-app/portal-sdk which provides:
|
|
8
|
-
* - Collapsible sidebar with navigation from your Fluid profile
|
|
9
|
-
* - Header with sub-tabs and theme mode toggle
|
|
10
|
-
* - Polished content area with rounded corners and shadow
|
|
11
|
-
* - Path-based routing for SPA navigation
|
|
12
|
-
*
|
|
13
|
-
* Custom pages are registered in portal.config.ts.
|
|
14
|
-
* Navigation and themes are fetched from the Fluid API.
|
|
15
|
-
*/
|
|
16
|
-
export default function App() {
|
|
17
|
-
return <AppShell customPages={customPages} />;
|
|
18
|
-
}
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import js from "@eslint/js";
|
|
2
|
-
import tseslint from "typescript-eslint";
|
|
3
|
-
import reactHooks from "eslint-plugin-react-hooks";
|
|
4
|
-
|
|
5
|
-
export default tseslint.config(
|
|
6
|
-
js.configs.recommended,
|
|
7
|
-
...tseslint.configs.recommended,
|
|
8
|
-
{
|
|
9
|
-
plugins: { "react-hooks": reactHooks },
|
|
10
|
-
rules: reactHooks.configs.recommended.rules,
|
|
11
|
-
},
|
|
12
|
-
{ ignores: ["dist/", "*.config.*"] },
|
|
13
|
-
);
|
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
import type { FluidSDKConfig, FluidAuthConfig } from "@fluid-app/portal-sdk";
|
|
2
|
-
import { getStoredToken } from "@fluid-app/portal-sdk";
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Fluid SDK Configuration
|
|
6
|
-
*
|
|
7
|
-
* This file contains the configuration for connecting to your Fluid Commerce account.
|
|
8
|
-
* Authentication is handled by FluidAuthProvider, which automatically:
|
|
9
|
-
* - Extracts tokens from URL parameters (passed by parent Fluid app)
|
|
10
|
-
* - Stores tokens in cookies and localStorage
|
|
11
|
-
* - Validates token expiration
|
|
12
|
-
*/
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* Authentication configuration
|
|
16
|
-
*
|
|
17
|
-
* Pattern: safety-satisfies-over-type-annotation
|
|
18
|
-
* Using `satisfies FluidAuthConfig` validates the object shape against the type
|
|
19
|
-
* WITHOUT widening the inferred types. This means:
|
|
20
|
-
* - TypeScript checks that all required properties are present
|
|
21
|
-
* - TypeScript checks that property types are compatible
|
|
22
|
-
* - The actual inferred types remain as specific as possible
|
|
23
|
-
*
|
|
24
|
-
* Compare to type annotation (`const authConfig: FluidAuthConfig = {...}`):
|
|
25
|
-
* - Type annotation widens types to match the interface exactly
|
|
26
|
-
* - `satisfies` preserves literal types while validating structure
|
|
27
|
-
*/
|
|
28
|
-
export const authConfig = {
|
|
29
|
-
// Auth failure defaults: SDK automatically redirects to https://auth.fluid.app
|
|
30
|
-
// with a redirect_url back to the current page. To customize:
|
|
31
|
-
// onAuthFailure: () => { window.location.href = "/custom-login"; },
|
|
32
|
-
// authUrl: "https://custom-auth.example.com",
|
|
33
|
-
} satisfies FluidAuthConfig;
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* API client configuration
|
|
37
|
-
*
|
|
38
|
-
* Pattern: safety-satisfies-over-type-annotation
|
|
39
|
-
* See authConfig above for explanation of the `satisfies` pattern.
|
|
40
|
-
*
|
|
41
|
-
* This config is passed to FluidProvider to configure the API client.
|
|
42
|
-
* The callbacks (getAuthToken, onAuthError) are called by the SDK
|
|
43
|
-
* when making API requests.
|
|
44
|
-
*/
|
|
45
|
-
export const fluidConfig = {
|
|
46
|
-
/**
|
|
47
|
-
* Base URL for the Fluid API
|
|
48
|
-
* Change this to your Fluid instance URL if self-hosted
|
|
49
|
-
*/
|
|
50
|
-
baseUrl: import.meta.env.VITE_API_URL ?? "https://api.fluid.app",
|
|
51
|
-
|
|
52
|
-
/**
|
|
53
|
-
* Returns the authentication token for API requests
|
|
54
|
-
* This reads from the token stored by FluidAuthProvider
|
|
55
|
-
*/
|
|
56
|
-
getAuthToken: () => {
|
|
57
|
-
return getStoredToken() ?? null;
|
|
58
|
-
},
|
|
59
|
-
|
|
60
|
-
// 401 errors default: SDK automatically redirects to https://auth.fluid.app
|
|
61
|
-
// To customize: onAuthError: () => { clearTokens(); window.location.reload(); },
|
|
62
|
-
} satisfies FluidSDKConfig;
|
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
import type { FluidSDKConfig, FluidAuthConfig } from "@fluid-app/portal-sdk";
|
|
2
|
-
import { getStoredToken } from "@fluid-app/portal-sdk";
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Fluid SDK Configuration
|
|
6
|
-
*
|
|
7
|
-
* This file contains the configuration for connecting to your Fluid Commerce account.
|
|
8
|
-
* Authentication is handled by FluidAuthProvider, which automatically:
|
|
9
|
-
* - Extracts tokens from URL parameters (passed by parent Fluid app)
|
|
10
|
-
* - Stores tokens in cookies and localStorage
|
|
11
|
-
* - Validates token expiration
|
|
12
|
-
*/
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* Authentication configuration
|
|
16
|
-
*
|
|
17
|
-
* Pattern: safety-satisfies-over-type-annotation
|
|
18
|
-
* Using `satisfies FluidAuthConfig` validates the object shape against the type
|
|
19
|
-
* WITHOUT widening the inferred types. This means:
|
|
20
|
-
* - TypeScript checks that all required properties are present
|
|
21
|
-
* - TypeScript checks that property types are compatible
|
|
22
|
-
* - The actual inferred types remain as specific as possible
|
|
23
|
-
*
|
|
24
|
-
* Compare to type annotation (`const authConfig: FluidAuthConfig = {...}`):
|
|
25
|
-
* - Type annotation widens types to match the interface exactly
|
|
26
|
-
* - `satisfies` preserves literal types while validating structure
|
|
27
|
-
*/
|
|
28
|
-
export const authConfig = {
|
|
29
|
-
// Auth failure defaults: SDK automatically redirects to https://auth.fluid.app
|
|
30
|
-
// with a redirect_url back to the current page. To customize:
|
|
31
|
-
// onAuthFailure: () => { window.location.href = "/custom-login"; },
|
|
32
|
-
// authUrl: "https://custom-auth.example.com",
|
|
33
|
-
} satisfies FluidAuthConfig;
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* API client configuration
|
|
37
|
-
*
|
|
38
|
-
* Pattern: safety-satisfies-over-type-annotation
|
|
39
|
-
* See authConfig above for explanation of the `satisfies` pattern.
|
|
40
|
-
*
|
|
41
|
-
* This config is passed to FluidProvider to configure the API client.
|
|
42
|
-
* The callbacks (getAuthToken, onAuthError) are called by the SDK
|
|
43
|
-
* when making API requests.
|
|
44
|
-
*/
|
|
45
|
-
export const fluidConfig = {
|
|
46
|
-
/**
|
|
47
|
-
* Base URL for the Fluid API
|
|
48
|
-
* Change this to your Fluid instance URL if self-hosted
|
|
49
|
-
*/
|
|
50
|
-
baseUrl: import.meta.env.VITE_API_URL ?? "https://api.fluid.app",
|
|
51
|
-
|
|
52
|
-
/**
|
|
53
|
-
* Returns the authentication token for API requests
|
|
54
|
-
* This reads from the token stored by FluidAuthProvider
|
|
55
|
-
*/
|
|
56
|
-
getAuthToken: () => {
|
|
57
|
-
return getStoredToken() ?? null;
|
|
58
|
-
},
|
|
59
|
-
|
|
60
|
-
// 401 errors default: SDK automatically redirects to https://auth.fluid.app
|
|
61
|
-
// To customize: onAuthError: () => { clearTokens(); window.location.reload(); },
|
|
62
|
-
} satisfies FluidSDKConfig;
|