@fluid-app/fluid-cli-portal 0.1.13 → 0.1.15

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.
@@ -1,33 +0,0 @@
1
- import { Hono } from "hono";
2
- import { secureHeaders } from "hono/secure-headers";
3
- import { cors } from "hono/cors";
4
- import { logger } from "hono/logger";
5
- import { apiRoutes } from "./routes/index";
6
-
7
- const app = new Hono();
8
-
9
- // Security middleware
10
- app.use("*", secureHeaders());
11
- app.use(
12
- "/api/*",
13
- cors({
14
- origin: process.env.ALLOWED_ORIGIN ?? "http://localhost:5173",
15
- }),
16
- );
17
-
18
- // Logging
19
- app.use("*", logger());
20
-
21
- // Global error handler
22
- app.onError((err, c) => {
23
- console.error(err);
24
- return c.json({ error: "Internal server error" }, 500);
25
- });
26
-
27
- // Health check
28
- app.get("/api/health", (c) => c.json({ status: "ok" }));
29
-
30
- // Mount API routes
31
- app.route("/api", apiRoutes);
32
-
33
- export default app;
@@ -1,123 +0,0 @@
1
- import { describe, it, expect } from "vitest";
2
- import app from "../index";
3
-
4
- describe("API Routes", () => {
5
- // Health check
6
- it("GET /api/health returns 200", async () => {
7
- const res = await app.request("/api/health");
8
- expect(res.status).toBe(200);
9
- const body = await res.json();
10
- expect(body).toEqual({ status: "ok" });
11
- });
12
-
13
- // List todos (empty)
14
- it("GET /api/todos returns empty array", async () => {
15
- const res = await app.request("/api/todos");
16
- expect(res.status).toBe(200);
17
- const body = await res.json();
18
- expect(body).toEqual([]);
19
- });
20
-
21
- // Create todo
22
- it("POST /api/todos creates a todo", async () => {
23
- const res = await app.request("/api/todos", {
24
- method: "POST",
25
- headers: { "Content-Type": "application/json" },
26
- body: JSON.stringify({ title: "Test todo" }),
27
- });
28
- expect(res.status).toBe(201);
29
- const body = await res.json();
30
- expect(body.title).toBe("Test todo");
31
- expect(body.completed).toBe(false);
32
- expect(body.id).toBeDefined();
33
- });
34
-
35
- // Create invalid (empty body)
36
- it("POST /api/todos with empty body returns 400", async () => {
37
- const res = await app.request("/api/todos", {
38
- method: "POST",
39
- headers: { "Content-Type": "application/json" },
40
- body: JSON.stringify({}),
41
- });
42
- expect(res.status).toBe(400);
43
- const body = await res.json();
44
- expect(body.error).toBe("Validation failed");
45
- expect(body.details).toBeDefined();
46
- });
47
-
48
- // Create invalid (empty title)
49
- it("POST /api/todos with empty title returns 400", async () => {
50
- const res = await app.request("/api/todos", {
51
- method: "POST",
52
- headers: { "Content-Type": "application/json" },
53
- body: JSON.stringify({ title: "" }),
54
- });
55
- expect(res.status).toBe(400);
56
- const body = await res.json();
57
- expect(body.error).toBe("Validation failed");
58
- });
59
-
60
- // Toggle todo
61
- it("PATCH /api/todos/:id toggles completion", async () => {
62
- // Create first
63
- const createRes = await app.request("/api/todos", {
64
- method: "POST",
65
- headers: { "Content-Type": "application/json" },
66
- body: JSON.stringify({ title: "Toggle me" }),
67
- });
68
- const created = await createRes.json();
69
-
70
- // Toggle
71
- const res = await app.request(`/api/todos/${created.id}`, {
72
- method: "PATCH",
73
- });
74
- expect(res.status).toBe(200);
75
- const body = await res.json();
76
- expect(body.completed).toBe(true);
77
- });
78
-
79
- // Toggle non-existent
80
- it("PATCH /api/todos/999 returns 404", async () => {
81
- const res = await app.request("/api/todos/999", { method: "PATCH" });
82
- expect(res.status).toBe(404);
83
- });
84
-
85
- // Toggle bad ID
86
- it("PATCH /api/todos/abc returns 400", async () => {
87
- const res = await app.request("/api/todos/abc", { method: "PATCH" });
88
- expect(res.status).toBe(400);
89
- const body = await res.json();
90
- expect(body.error).toBe("Invalid ID");
91
- });
92
-
93
- // Delete todo
94
- it("DELETE /api/todos/:id deletes a todo", async () => {
95
- const createRes = await app.request("/api/todos", {
96
- method: "POST",
97
- headers: { "Content-Type": "application/json" },
98
- body: JSON.stringify({ title: "Delete me" }),
99
- });
100
- const created = await createRes.json();
101
-
102
- const res = await app.request(`/api/todos/${created.id}`, {
103
- method: "DELETE",
104
- });
105
- expect(res.status).toBe(200);
106
- const body = await res.json();
107
- expect(body.success).toBe(true);
108
- });
109
-
110
- // Delete non-existent
111
- it("DELETE /api/todos/999 returns 404", async () => {
112
- const res = await app.request("/api/todos/999", { method: "DELETE" });
113
- expect(res.status).toBe(404);
114
- });
115
-
116
- // Delete bad ID
117
- it("DELETE /api/todos/abc returns 400", async () => {
118
- const res = await app.request("/api/todos/abc", { method: "DELETE" });
119
- expect(res.status).toBe(400);
120
- const body = await res.json();
121
- expect(body.error).toBe("Invalid ID");
122
- });
123
- });
@@ -1,110 +0,0 @@
1
- import { Hono } from "hono";
2
- import { eq, sql } from "drizzle-orm";
3
- import { z } from "zod";
4
- import { db } from "../db/index";
5
- import { todos } from "../db/schema";
6
- import { createTodoSchema, todoIdSchema } from "./schemas";
7
-
8
- export const apiRoutes = new Hono();
9
-
10
- // GET /api/todos — List all todos
11
- apiRoutes.get("/todos", async (c) => {
12
- try {
13
- const allTodos = await db.select().from(todos).all();
14
- return c.json(allTodos);
15
- } catch (error) {
16
- console.error(error);
17
- return c.json({ error: "Internal server error" }, 500);
18
- }
19
- });
20
-
21
- // POST /api/todos — Create a new todo
22
- apiRoutes.post("/todos", async (c) => {
23
- try {
24
- const body = await c.req.json();
25
- const result = createTodoSchema.safeParse(body);
26
-
27
- if (!result.success) {
28
- return c.json(
29
- {
30
- error: "Validation failed",
31
- details: z.flattenError(result.error).fieldErrors,
32
- },
33
- 400,
34
- );
35
- }
36
-
37
- const newTodo = await db
38
- .insert(todos)
39
- .values({ title: result.data.title })
40
- .returning()
41
- .get();
42
- return c.json(newTodo, 201);
43
- } catch (error) {
44
- console.error(error);
45
- return c.json({ error: "Internal server error" }, 500);
46
- }
47
- });
48
-
49
- // PATCH /api/todos/:id — Toggle a todo's completion status
50
- apiRoutes.patch("/todos/:id", async (c) => {
51
- try {
52
- const idResult = todoIdSchema.safeParse(c.req.param("id"));
53
-
54
- if (!idResult.success) {
55
- return c.json({ error: "Invalid ID" }, 400);
56
- }
57
-
58
- const id = idResult.data;
59
- const existing = await db
60
- .select()
61
- .from(todos)
62
- .where(eq(todos.id, id))
63
- .get();
64
-
65
- if (!existing) {
66
- return c.json({ error: "Todo not found" }, 404);
67
- }
68
-
69
- const updated = await db
70
- .update(todos)
71
- .set({
72
- completed: !existing.completed,
73
- updatedAt: sql`(current_timestamp)`,
74
- })
75
- .where(eq(todos.id, id))
76
- .returning()
77
- .get();
78
- return c.json(updated);
79
- } catch (error) {
80
- console.error(error);
81
- return c.json({ error: "Internal server error" }, 500);
82
- }
83
- });
84
-
85
- // DELETE /api/todos/:id — Delete a todo
86
- apiRoutes.delete("/todos/:id", async (c) => {
87
- try {
88
- const idResult = todoIdSchema.safeParse(c.req.param("id"));
89
-
90
- if (!idResult.success) {
91
- return c.json({ error: "Invalid ID" }, 400);
92
- }
93
-
94
- const id = idResult.data;
95
- const deleted = await db
96
- .delete(todos)
97
- .where(eq(todos.id, id))
98
- .returning()
99
- .get();
100
-
101
- if (!deleted) {
102
- return c.json({ error: "Todo not found" }, 404);
103
- }
104
-
105
- return c.json({ success: true });
106
- } catch (error) {
107
- console.error(error);
108
- return c.json({ error: "Internal server error" }, 500);
109
- }
110
- });
@@ -1,7 +0,0 @@
1
- import { z } from "zod";
2
-
3
- export const createTodoSchema = z.object({
4
- title: z.string().min(1).max(500),
5
- });
6
-
7
- export const todoIdSchema = z.coerce.number().int().positive();
@@ -1,9 +0,0 @@
1
- process.env.DATABASE_URL = "file:test.db";
2
-
3
- import { beforeEach } from "vitest";
4
- import { db } from "../server/db/index";
5
- import { todos } from "../server/db/schema";
6
-
7
- beforeEach(async () => {
8
- await db.delete(todos);
9
- });
@@ -1,41 +0,0 @@
1
- import { defineConfig } from "vite";
2
- import react from "@vitejs/plugin-react";
3
- import tailwindcss from "@tailwindcss/vite";
4
- import devServer from "@hono/vite-dev-server";
5
- import { fluidManifestPlugin } from "@fluid-app/portal-sdk/vite";
6
-
7
- export default defineConfig({
8
- plugins: [
9
- react(),
10
- tailwindcss(),
11
- fluidManifestPlugin(),
12
- devServer({
13
- entry: "src/server/index.ts",
14
- // Only send /api/* requests to Hono — let Vite handle everything else
15
- // (static assets, SPA fallback, HMR, etc.)
16
- exclude: [/^\/(?!api\/).*/, /^\/$/],
17
- injectClientScript: false,
18
- }),
19
- ],
20
- build: {
21
- target: "esnext",
22
- outDir: "dist/public",
23
- chunkSizeWarningLimit: 600,
24
- rollupOptions: {
25
- output: {
26
- manualChunks(id) {
27
- if (
28
- id.includes("node_modules/react-dom") ||
29
- id.includes("node_modules/react/") ||
30
- id.includes("node_modules/scheduler")
31
- ) {
32
- return "vendor";
33
- }
34
- if (id.includes("node_modules/@tanstack/react-query")) {
35
- return "query";
36
- }
37
- },
38
- },
39
- },
40
- },
41
- });
@@ -1,9 +0,0 @@
1
- import { defineConfig } from "vitest/config";
2
-
3
- export default defineConfig({
4
- test: {
5
- environment: "node",
6
- include: ["src/**/*.test.ts"],
7
- setupFiles: ["src/test/setup.ts"],
8
- },
9
- });