flingit 0.0.1

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 (161) hide show
  1. package/README.md +84 -0
  2. package/dist/cli/commands/db.d.ts +9 -0
  3. package/dist/cli/commands/db.d.ts.map +1 -0
  4. package/dist/cli/commands/db.js +215 -0
  5. package/dist/cli/commands/db.js.map +1 -0
  6. package/dist/cli/commands/dev.d.ts +6 -0
  7. package/dist/cli/commands/dev.d.ts.map +1 -0
  8. package/dist/cli/commands/dev.js +145 -0
  9. package/dist/cli/commands/dev.js.map +1 -0
  10. package/dist/cli/commands/feedback.d.ts +6 -0
  11. package/dist/cli/commands/feedback.d.ts.map +1 -0
  12. package/dist/cli/commands/feedback.js +116 -0
  13. package/dist/cli/commands/feedback.js.map +1 -0
  14. package/dist/cli/commands/init.d.ts +6 -0
  15. package/dist/cli/commands/init.d.ts.map +1 -0
  16. package/dist/cli/commands/init.js +161 -0
  17. package/dist/cli/commands/init.js.map +1 -0
  18. package/dist/cli/commands/launch.d.ts +12 -0
  19. package/dist/cli/commands/launch.d.ts.map +1 -0
  20. package/dist/cli/commands/launch.js +176 -0
  21. package/dist/cli/commands/launch.js.map +1 -0
  22. package/dist/cli/commands/login.d.ts +6 -0
  23. package/dist/cli/commands/login.d.ts.map +1 -0
  24. package/dist/cli/commands/login.js +90 -0
  25. package/dist/cli/commands/login.js.map +1 -0
  26. package/dist/cli/commands/logout.d.ts +6 -0
  27. package/dist/cli/commands/logout.d.ts.map +1 -0
  28. package/dist/cli/commands/logout.js +32 -0
  29. package/dist/cli/commands/logout.js.map +1 -0
  30. package/dist/cli/commands/logs.d.ts +12 -0
  31. package/dist/cli/commands/logs.d.ts.map +1 -0
  32. package/dist/cli/commands/logs.js +261 -0
  33. package/dist/cli/commands/logs.js.map +1 -0
  34. package/dist/cli/commands/onboard.d.ts +22 -0
  35. package/dist/cli/commands/onboard.d.ts.map +1 -0
  36. package/dist/cli/commands/onboard.js +244 -0
  37. package/dist/cli/commands/onboard.js.map +1 -0
  38. package/dist/cli/commands/project.d.ts +6 -0
  39. package/dist/cli/commands/project.d.ts.map +1 -0
  40. package/dist/cli/commands/project.js +142 -0
  41. package/dist/cli/commands/project.js.map +1 -0
  42. package/dist/cli/commands/push.d.ts +6 -0
  43. package/dist/cli/commands/push.d.ts.map +1 -0
  44. package/dist/cli/commands/push.js +351 -0
  45. package/dist/cli/commands/push.js.map +1 -0
  46. package/dist/cli/commands/register.d.ts +6 -0
  47. package/dist/cli/commands/register.d.ts.map +1 -0
  48. package/dist/cli/commands/register.js +115 -0
  49. package/dist/cli/commands/register.js.map +1 -0
  50. package/dist/cli/commands/secret.d.ts +11 -0
  51. package/dist/cli/commands/secret.d.ts.map +1 -0
  52. package/dist/cli/commands/secret.js +124 -0
  53. package/dist/cli/commands/secret.js.map +1 -0
  54. package/dist/cli/commands/whoami.d.ts +6 -0
  55. package/dist/cli/commands/whoami.d.ts.map +1 -0
  56. package/dist/cli/commands/whoami.js +54 -0
  57. package/dist/cli/commands/whoami.js.map +1 -0
  58. package/dist/cli/deploy/assets.d.ts +73 -0
  59. package/dist/cli/deploy/assets.d.ts.map +1 -0
  60. package/dist/cli/deploy/assets.js +167 -0
  61. package/dist/cli/deploy/assets.js.map +1 -0
  62. package/dist/cli/deploy/base64-stream.d.ts +16 -0
  63. package/dist/cli/deploy/base64-stream.d.ts.map +1 -0
  64. package/dist/cli/deploy/base64-stream.js +44 -0
  65. package/dist/cli/deploy/base64-stream.js.map +1 -0
  66. package/dist/cli/deploy/bucket-stream.d.ts +18 -0
  67. package/dist/cli/deploy/bucket-stream.d.ts.map +1 -0
  68. package/dist/cli/deploy/bucket-stream.js +63 -0
  69. package/dist/cli/deploy/bucket-stream.js.map +1 -0
  70. package/dist/cli/deploy/bundler.d.ts +22 -0
  71. package/dist/cli/deploy/bundler.d.ts.map +1 -0
  72. package/dist/cli/deploy/bundler.js +131 -0
  73. package/dist/cli/deploy/bundler.js.map +1 -0
  74. package/dist/cli/deploy/worker-entry.d.ts +14 -0
  75. package/dist/cli/deploy/worker-entry.d.ts.map +1 -0
  76. package/dist/cli/deploy/worker-entry.js +60 -0
  77. package/dist/cli/deploy/worker-entry.js.map +1 -0
  78. package/dist/cli/deploy/wrangler-config.d.ts +20 -0
  79. package/dist/cli/deploy/wrangler-config.d.ts.map +1 -0
  80. package/dist/cli/deploy/wrangler-config.js +54 -0
  81. package/dist/cli/deploy/wrangler-config.js.map +1 -0
  82. package/dist/cli/index.d.ts +10 -0
  83. package/dist/cli/index.d.ts.map +1 -0
  84. package/dist/cli/index.js +72 -0
  85. package/dist/cli/index.js.map +1 -0
  86. package/dist/cli/utils/config.d.ts +39 -0
  87. package/dist/cli/utils/config.d.ts.map +1 -0
  88. package/dist/cli/utils/config.js +105 -0
  89. package/dist/cli/utils/config.js.map +1 -0
  90. package/dist/cli/utils/duration.d.ts +21 -0
  91. package/dist/cli/utils/duration.d.ts.map +1 -0
  92. package/dist/cli/utils/duration.js +44 -0
  93. package/dist/cli/utils/duration.js.map +1 -0
  94. package/dist/cli/utils/environment.d.ts +17 -0
  95. package/dist/cli/utils/environment.d.ts.map +1 -0
  96. package/dist/cli/utils/environment.js +27 -0
  97. package/dist/cli/utils/environment.js.map +1 -0
  98. package/dist/cli/utils/project.d.ts +24 -0
  99. package/dist/cli/utils/project.d.ts.map +1 -0
  100. package/dist/cli/utils/project.js +47 -0
  101. package/dist/cli/utils/project.js.map +1 -0
  102. package/dist/cli/utils/registry.d.ts +34 -0
  103. package/dist/cli/utils/registry.d.ts.map +1 -0
  104. package/dist/cli/utils/registry.js +94 -0
  105. package/dist/cli/utils/registry.js.map +1 -0
  106. package/dist/cli/utils/token.d.ts +12 -0
  107. package/dist/cli/utils/token.d.ts.map +1 -0
  108. package/dist/cli/utils/token.js +27 -0
  109. package/dist/cli/utils/token.js.map +1 -0
  110. package/dist/index.d.ts +12 -0
  111. package/dist/index.d.ts.map +1 -0
  112. package/dist/index.js +12 -0
  113. package/dist/index.js.map +1 -0
  114. package/dist/runtime/cron.d.ts +50 -0
  115. package/dist/runtime/cron.d.ts.map +1 -0
  116. package/dist/runtime/cron.js +80 -0
  117. package/dist/runtime/cron.js.map +1 -0
  118. package/dist/runtime/db.d.ts +93 -0
  119. package/dist/runtime/db.d.ts.map +1 -0
  120. package/dist/runtime/db.js +137 -0
  121. package/dist/runtime/db.js.map +1 -0
  122. package/dist/runtime/http.d.ts +33 -0
  123. package/dist/runtime/http.d.ts.map +1 -0
  124. package/dist/runtime/http.js +36 -0
  125. package/dist/runtime/http.js.map +1 -0
  126. package/dist/runtime/log.d.ts +90 -0
  127. package/dist/runtime/log.d.ts.map +1 -0
  128. package/dist/runtime/log.js +211 -0
  129. package/dist/runtime/log.js.map +1 -0
  130. package/dist/runtime/migrate.d.ts +54 -0
  131. package/dist/runtime/migrate.d.ts.map +1 -0
  132. package/dist/runtime/migrate.js +130 -0
  133. package/dist/runtime/migrate.js.map +1 -0
  134. package/dist/runtime/secrets.d.ts +36 -0
  135. package/dist/runtime/secrets.d.ts.map +1 -0
  136. package/dist/runtime/secrets.js +75 -0
  137. package/dist/runtime/secrets.js.map +1 -0
  138. package/dist/worker-runtime/index.d.ts +79 -0
  139. package/dist/worker-runtime/index.d.ts.map +1 -0
  140. package/dist/worker-runtime/index.js +203 -0
  141. package/dist/worker-runtime/index.js.map +1 -0
  142. package/package.json +81 -0
  143. package/templates/default/.nvmrc +1 -0
  144. package/templates/default/CLAUDE.md +197 -0
  145. package/templates/default/index.html +13 -0
  146. package/templates/default/package.json +25 -0
  147. package/templates/default/public/vite.svg +1 -0
  148. package/templates/default/skills/fling/API.md +335 -0
  149. package/templates/default/skills/fling/EXAMPLES.md +204 -0
  150. package/templates/default/skills/fling/FEEDBACK.md +73 -0
  151. package/templates/default/skills/fling/SKILL.md +159 -0
  152. package/templates/default/src/react-app/App.css +34 -0
  153. package/templates/default/src/react-app/App.tsx +27 -0
  154. package/templates/default/src/react-app/index.css +15 -0
  155. package/templates/default/src/react-app/main.tsx +10 -0
  156. package/templates/default/src/react-app/vite-env.d.ts +1 -0
  157. package/templates/default/src/worker/index.ts +27 -0
  158. package/templates/default/tsconfig.app.json +22 -0
  159. package/templates/default/tsconfig.json +7 -0
  160. package/templates/default/tsconfig.worker.json +11 -0
  161. package/templates/default/vite.config.ts +21 -0
@@ -0,0 +1,204 @@
1
+ # Fling Examples
2
+
3
+ Common patterns for building personal tools with Fling.
4
+
5
+ ## Simple API with Authentication
6
+
7
+ Protected endpoints with API key auth.
8
+
9
+ ```typescript
10
+ import { app, db, secrets } from "fling";
11
+
12
+ const API_KEY = secrets.get("API_KEY");
13
+
14
+ // Auth middleware
15
+ function requireAuth(c: any, next: () => Promise<void>) {
16
+ const auth = c.req.header("Authorization");
17
+ if (auth !== `Bearer ${API_KEY}`) {
18
+ return c.json({ error: "Unauthorized" }, 401);
19
+ }
20
+ return next();
21
+ }
22
+
23
+ // Public endpoint
24
+ app.get("/health", (c) => c.json({ status: "ok" }));
25
+
26
+ // Protected endpoints
27
+ app.post("/api/items", requireAuth, async (c) => {
28
+ const { name, value } = await c.req.json();
29
+
30
+ const result = await db.prepare(
31
+ "INSERT INTO items (name, value) VALUES (?, ?)"
32
+ ).bind(name, value).run();
33
+
34
+ console.log("Created item", name, result.meta.last_row_id);
35
+
36
+ return c.json({
37
+ id: result.meta.last_row_id,
38
+ name,
39
+ value
40
+ }, 201);
41
+ });
42
+
43
+ app.get("/api/items", requireAuth, async (c) => {
44
+ const { results } = await db.prepare("SELECT * FROM items").all();
45
+ return c.json(results);
46
+ });
47
+
48
+ app.get("/api/items/:id", requireAuth, async (c) => {
49
+ const id = c.req.param("id");
50
+ const item = await db.prepare(
51
+ "SELECT * FROM items WHERE id = ?"
52
+ ).bind(id).first();
53
+
54
+ if (!item) {
55
+ return c.json({ error: "Not found" }, 404);
56
+ }
57
+
58
+ return c.json(item);
59
+ });
60
+ ```
61
+
62
+ ## Webhook Receiver
63
+
64
+ Process incoming webhooks and store data.
65
+
66
+ ```typescript
67
+ import { app, db, secrets } from "fling";
68
+
69
+ async function initDb() {
70
+ await db.prepare(`
71
+ CREATE TABLE IF NOT EXISTS events (
72
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
73
+ source TEXT NOT NULL,
74
+ type TEXT,
75
+ payload TEXT NOT NULL,
76
+ received_at TEXT DEFAULT (datetime('now'))
77
+ )
78
+ `).run();
79
+ }
80
+
81
+ // Verify GitHub webhook signature using Web Crypto API
82
+ async function verifyGitHubSignature(body: string, signature: string, secret: string): Promise<boolean> {
83
+ const encoder = new TextEncoder();
84
+ const key = await crypto.subtle.importKey(
85
+ "raw",
86
+ encoder.encode(secret),
87
+ { name: "HMAC", hash: "SHA-256" },
88
+ false,
89
+ ["sign"]
90
+ );
91
+ const sig = await crypto.subtle.sign("HMAC", key, encoder.encode(body));
92
+ const expected = "sha256=" + Array.from(new Uint8Array(sig))
93
+ .map(b => b.toString(16).padStart(2, "0")).join("");
94
+ return signature === expected;
95
+ }
96
+
97
+ // GitHub webhook
98
+ app.post("/webhooks/github", async (c) => {
99
+ const signature = c.req.header("X-Hub-Signature-256");
100
+ const event = c.req.header("X-GitHub-Event");
101
+ const body = await c.req.text();
102
+
103
+ // Optional: Verify signature (uncomment to enable)
104
+ // const secret = secrets.get("GITHUB_WEBHOOK_SECRET");
105
+ // if (signature && !await verifyGitHubSignature(body, signature, secret)) {
106
+ // return c.text("Invalid signature", 401);
107
+ // }
108
+
109
+ await db.prepare(
110
+ "INSERT INTO events (source, type, payload) VALUES (?, ?, ?)"
111
+ ).bind("github", event, body).run();
112
+
113
+ console.log("GitHub webhook received", event);
114
+
115
+ return c.json({ ok: true });
116
+ });
117
+
118
+ // Generic webhook
119
+ app.post("/webhooks/:source", async (c) => {
120
+ const source = c.req.param("source");
121
+ const body = await c.req.text();
122
+
123
+ await db.prepare(
124
+ "INSERT INTO events (source, payload) VALUES (?, ?)"
125
+ ).bind(source, body).run();
126
+
127
+ console.log("Webhook received", source);
128
+
129
+ return c.json({ ok: true });
130
+ });
131
+
132
+ // View recent events
133
+ app.get("/events", async (c) => {
134
+ const { results } = await db.prepare(
135
+ "SELECT * FROM events ORDER BY received_at DESC LIMIT 100"
136
+ ).all();
137
+ return c.json(results);
138
+ });
139
+ ```
140
+
141
+ ## Data Sync API
142
+
143
+ Pull data from an external API and store it.
144
+
145
+ ```typescript
146
+ import { app, db, secrets, migrate } from "fling";
147
+
148
+ const API_TOKEN = secrets.get("DATA_API_TOKEN");
149
+
150
+ migrate("001_create_sync_data", async () => {
151
+ await db.prepare(`
152
+ CREATE TABLE IF NOT EXISTS sync_data (
153
+ id TEXT PRIMARY KEY,
154
+ data TEXT NOT NULL,
155
+ synced_at TEXT DEFAULT (datetime('now'))
156
+ )
157
+ `).run();
158
+ });
159
+
160
+ // Sync data from external API
161
+ async function syncData() {
162
+ console.log("Starting data sync");
163
+
164
+ const response = await fetch("https://api.example.com/data", {
165
+ headers: { "Authorization": `Bearer ${API_TOKEN}` }
166
+ });
167
+
168
+ if (!response.ok) {
169
+ throw new Error(`Sync failed: ${response.status}`);
170
+ }
171
+
172
+ const items = await response.json();
173
+ let synced = 0;
174
+
175
+ for (const item of items) {
176
+ await db.prepare(
177
+ "INSERT OR REPLACE INTO sync_data (id, data, synced_at) VALUES (?, ?, datetime('now'))"
178
+ ).bind(item.id, JSON.stringify(item)).run();
179
+ synced++;
180
+ }
181
+
182
+ console.log("Sync completed", synced);
183
+ return synced;
184
+ }
185
+
186
+ // View synced data
187
+ app.get("/api/data", async (c) => {
188
+ const { results } = await db.prepare(
189
+ "SELECT * FROM sync_data ORDER BY synced_at DESC"
190
+ ).all();
191
+
192
+ return c.json(results.map((r: any) => ({
193
+ id: r.id,
194
+ data: JSON.parse(r.data),
195
+ synced_at: r.synced_at
196
+ })));
197
+ });
198
+
199
+ // Trigger sync via API
200
+ app.post("/api/sync", async (c) => {
201
+ const count = await syncData();
202
+ return c.json({ message: "Sync completed", count });
203
+ });
204
+ ```
@@ -0,0 +1,73 @@
1
+ # Feedback Skill
2
+
3
+ This skill helps you collect and submit user feedback about Fling to the team.
4
+
5
+ ## When to Offer Feedback
6
+
7
+ Proactively offer to send feedback in these situations:
8
+
9
+ 1. **User frustration** - When the user expresses frustration with Fling
10
+ 2. **Product limitations** - When Fling can't do something the user needs
11
+ 3. **After difficulties** - When you've helped resolve a confusing issue
12
+ 4. **Feature requests** - When the user wishes Fling could do something differently
13
+
14
+ ## Critical Rule: Always Ask First
15
+
16
+ **NEVER send feedback without explicit user approval.** Always:
17
+
18
+ 1. Ask if they'd like to send feedback
19
+ 2. Show them exactly what will be sent
20
+ 3. Wait for their confirmation before running the command
21
+
22
+ Example flow:
23
+ ```
24
+ User: "This is so frustrating, the logs command never shows what I need!"
25
+
26
+ You: "I understand that's frustrating. Would you like me to send this feedback
27
+ to the Fling team? Here's what I'd send:
28
+
29
+ 'The logs command output doesn't include enough context. When debugging
30
+ deployment issues, users need to see request IDs and timestamps together.'
31
+
32
+ Should I submit this?"
33
+ ```
34
+
35
+ ## Command Usage
36
+
37
+ ```bash
38
+ # Simple feedback (single line)
39
+ npm exec fling feedback "Your feedback message here"
40
+
41
+ # Interactive mode (for longer, multi-line feedback)
42
+ npm exec fling feedback -i
43
+ ```
44
+
45
+ The user must be logged in (`npm exec fling login`) for feedback to work.
46
+
47
+ ## Writing Good Feedback
48
+
49
+ Help users write **actionable** feedback:
50
+
51
+ **Good feedback includes:**
52
+ - What they were trying to do
53
+ - What happened vs. what they expected
54
+ - Specific details (commands, error messages)
55
+
56
+ **Less useful feedback:**
57
+ - Vague complaints ("this is broken")
58
+ - No context about the situation
59
+ - Just emotional venting without specifics
60
+
61
+ ## Example Good Feedback
62
+
63
+ ```
64
+ "When running 'fling push' after adding a new secret, the deployment succeeds
65
+ but the secret isn't available until a second deploy. Expected the secret to
66
+ be available immediately. Workaround: run 'fling push' twice."
67
+ ```
68
+
69
+ ## Feedback Requirements
70
+
71
+ - Minimum 10 characters (to encourage meaningful feedback)
72
+ - Maximum 5000 characters
73
+ - User must be logged in to the platform
@@ -0,0 +1,159 @@
1
+ # Fling Skill
2
+
3
+ You are working on a Fling project - a personal software platform for building and deploying personal tools with a React frontend and Hono API backend.
4
+
5
+ ## Core Concepts
6
+
7
+ Fling provides four primitives that work identically in local development and production:
8
+
9
+ 1. **HTTP** - Expose endpoints via Hono
10
+ 2. **Database** - SQLite locally, D1 in production
11
+ 3. **Secrets** - Secure credential management
12
+ 4. **Migrations** - Version your database schema
13
+
14
+ ## Project Structure
15
+
16
+ ```
17
+ src/
18
+ worker/
19
+ index.ts # Backend API entry point
20
+ react-app/
21
+ main.tsx # React entry point
22
+ App.tsx # Main React component
23
+ App.css # Component styles
24
+ index.css # Global styles
25
+ public/ # Static assets (served by Vite)
26
+ index.html # Vite entry HTML
27
+ vite.config.ts # Vite configuration
28
+ .fling/
29
+ secrets # Local secrets (gitignored)
30
+ data/
31
+ local.db # SQLite database (gitignored)
32
+ ```
33
+
34
+ ## Quick Reference
35
+
36
+ ```typescript
37
+ // Backend (src/worker/index.ts)
38
+ import { app, db, secrets } from "fling";
39
+
40
+ // HTTP routes - use /api prefix for Vite proxy
41
+ app.get("/api/hello", (c) => c.json({ message: "Hello!" }));
42
+ app.post("/api/webhook", async (c) => {
43
+ const body = await c.req.json();
44
+ return c.json({ ok: true });
45
+ });
46
+
47
+ // Database (D1-compatible API)
48
+ const row = await db.prepare("SELECT * FROM items WHERE id = ?").bind(id).first();
49
+ const all = await db.prepare("SELECT * FROM items").all();
50
+ await db.prepare("INSERT INTO items (name) VALUES (?)").bind(name).run();
51
+ await db.prepare("CREATE TABLE IF NOT EXISTS items (id INTEGER PRIMARY KEY, name TEXT)").run();
52
+
53
+ // Secrets (throws if not set)
54
+ const token = secrets.get("API_TOKEN");
55
+ ```
56
+
57
+ ```typescript
58
+ // Frontend (src/react-app/App.tsx)
59
+ import { useState, useEffect } from "react";
60
+
61
+ function App() {
62
+ const [data, setData] = useState(null);
63
+
64
+ useEffect(() => {
65
+ fetch("/api/hello")
66
+ .then(res => res.json())
67
+ .then(setData);
68
+ }, []);
69
+
70
+ return <div>{data?.message}</div>;
71
+ }
72
+ ```
73
+
74
+ ## CLI Commands
75
+
76
+ Always use `npm exec` to run the project's installed Fling (not global):
77
+
78
+ ```bash
79
+ npm exec fling dev # Start local server (API + Vite)
80
+ npm exec fling dev -- --port 8080 # Custom API port
81
+ npm exec fling db sql "SELECT * FROM users" # Query local SQLite
82
+ npm exec fling db reset # Wipe local database
83
+ npm exec fling db sql "SELECT 1" # Query local SQLite
84
+ npm exec fling secret set KEY=value
85
+ npm exec fling secret list
86
+ npm exec fling secret remove KEY
87
+ npm exec fling logs # View local logs
88
+ npm exec fling push # Build and deploy to Cloudflare Workers
89
+ npm exec fling project slug # Show current project slug and URL
90
+ npm exec fling project slug:set <new-slug> # Change project slug (affects URL)
91
+ ```
92
+
93
+ ### Local vs Production (`--prod`)
94
+
95
+ Commands default to local environment. Use `--prod` for production:
96
+
97
+ ```bash
98
+ # Local (default)
99
+ npm exec fling secret list # Local secrets
100
+ npm exec fling logs # Local logs
101
+ npm exec fling db sql "SELECT 1" # Local SQLite
102
+
103
+ # Production (requires login)
104
+ npm exec fling -- --prod secret list # Deployed secrets
105
+ npm exec fling -- --prod logs # Deployed logs
106
+ npm exec fling -- --prod db sql "SELECT 1" # Deployed D1
107
+ ```
108
+
109
+ ## Development
110
+
111
+ Run `npm start` (or `fling dev`) to start development:
112
+ - **Frontend**: http://localhost:5173 (Vite with React HMR)
113
+ - **API**: http://localhost:3210 (Hono backend)
114
+ - Vite proxies `/api/*` requests to the API server
115
+
116
+ ## Deployment
117
+
118
+ When the user's request is complete and working locally, deploy it:
119
+ **Run `npm exec fling push` directly** - you have bash access, don't ask the user to run commands.
120
+
121
+ ### What `fling push` does:
122
+
123
+ 1. **Builds frontend** - Runs `vite build`, outputs to `dist/client/`
124
+ 2. **Uploads static assets** - HTML, JS, CSS, images from `dist/client/`
125
+ 3. **Bundles backend** - Compiles `src/worker/index.ts` with esbuild
126
+ 4. **Deploys to Cloudflare** - Both frontend and backend go live
127
+
128
+ ### Static Asset Limits
129
+
130
+ - **25MB** per file maximum
131
+ - **100MB** total assets maximum
132
+ - Supported: HTML, CSS, JS, images, fonts, video, audio, WebAssembly
133
+ - MIME types are detected automatically from file extensions
134
+
135
+ ### Routing
136
+
137
+ - `/api/*` routes are handled by your backend code
138
+ - All other paths serve static assets from `dist/client/`
139
+ - If no asset matches, `index.html` is served (SPA fallback)
140
+
141
+ ## Important Constraints
142
+
143
+ 1. **Backend code runs in Cloudflare Workers** - This is NOT a Node.js environment. You cannot use Node.js-specific APIs (`fs`, `path`, `child_process`, etc.) or npm packages that depend on them. Only use packages that explicitly support Cloudflare Workers or are pure JavaScript/TypeScript with no Node.js dependencies.
144
+
145
+ 2. **Memory limit (~128MB)** - Workers have limited memory. Cannot process large datasets in memory. Use streaming, pagination, or chunked processing for large data.
146
+
147
+ 3. **Feature scope** - Fling supports frontend, backend, and database. Not yet supported: cron jobs, file/blob storage, websockets. If users need these, encourage feedback via `npm exec fling feedback`.
148
+
149
+ 4. **Database operations cannot be at module top-level** - They must be inside route handlers or functions called by them.
150
+
151
+ 5. **Secrets throw on missing** - No default values. Use `fling secret set` to configure.
152
+
153
+ 6. **Use /api prefix for backend routes** - Required for Vite proxy during development.
154
+
155
+ 7. **Run commands yourself** - You have bash access. Don't ask the user to run `fling push`, `fling dev`, `npm start`, etc. Execute them directly.
156
+
157
+ **If the user's request might hit platform limitations, warn them early and suggest alternatives.**
158
+
159
+ See API.md for detailed API reference and EXAMPLES.md for common patterns.
@@ -0,0 +1,34 @@
1
+ .app {
2
+ max-width: 600px;
3
+ margin: 0 auto;
4
+ padding: 2rem;
5
+ text-align: center;
6
+ }
7
+
8
+ h1 {
9
+ font-size: 2.5rem;
10
+ margin-bottom: 1rem;
11
+ color: #333;
12
+ }
13
+
14
+ .api-message {
15
+ font-size: 1.25rem;
16
+ color: #0066cc;
17
+ background: #f0f8ff;
18
+ padding: 1rem;
19
+ border-radius: 8px;
20
+ margin: 1.5rem 0;
21
+ }
22
+
23
+ .hint {
24
+ color: #666;
25
+ font-size: 0.9rem;
26
+ line-height: 1.6;
27
+ }
28
+
29
+ .hint code {
30
+ background: #f4f4f4;
31
+ padding: 0.2rem 0.4rem;
32
+ border-radius: 4px;
33
+ font-family: monospace;
34
+ }
@@ -0,0 +1,27 @@
1
+ import { useState, useEffect } from "react";
2
+ import "./App.css";
3
+
4
+ function App() {
5
+ const [message, setMessage] = useState<string>("Loading...");
6
+
7
+ useEffect(() => {
8
+ fetch("/api/hello")
9
+ .then((res) => res.json())
10
+ .then((data: { message: string }) => setMessage(data.message))
11
+ .catch(() => setMessage("Failed to connect to API"));
12
+ }, []);
13
+
14
+ return (
15
+ <div className="app">
16
+ <h1>Fling App</h1>
17
+ <p className="api-message">{message}</p>
18
+ <p className="hint">
19
+ Edit <code>src/react-app/App.tsx</code> for the frontend
20
+ <br />
21
+ Edit <code>src/worker/index.ts</code> for the API
22
+ </p>
23
+ </div>
24
+ );
25
+ }
26
+
27
+ export default App;
@@ -0,0 +1,15 @@
1
+ @import "tailwindcss";
2
+
3
+ :root {
4
+ font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
5
+ Oxygen, Ubuntu, Cantarell, "Fira Sans", "Droid Sans", "Helvetica Neue",
6
+ sans-serif;
7
+ color: #213547;
8
+ }
9
+
10
+ body {
11
+ min-height: 100vh;
12
+ display: flex;
13
+ place-items: center;
14
+ justify-content: center;
15
+ }
@@ -0,0 +1,10 @@
1
+ import { StrictMode } from "react";
2
+ import { createRoot } from "react-dom/client";
3
+ import "./index.css";
4
+ import App from "./App.tsx";
5
+
6
+ createRoot(document.getElementById("root")!).render(
7
+ <StrictMode>
8
+ <App />
9
+ </StrictMode>
10
+ );
@@ -0,0 +1 @@
1
+ /// <reference types="vite/client" />
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Fling Backend Worker
3
+ *
4
+ * This is your backend API entry point. Define HTTP routes and database
5
+ * migrations here.
6
+ */
7
+
8
+ import { app, migrate, db } from "fling";
9
+
10
+ // Database migrations - run automatically on startup
11
+ // IMPORTANT: Migrations MUST be idempotent (safe to run multiple times).
12
+ // Due to distributed execution, a migration might run more than once.
13
+ // Use "CREATE TABLE IF NOT EXISTS", "CREATE INDEX IF NOT EXISTS", etc.
14
+ migrate("001_create_example", async () => {
15
+ await db.prepare(`
16
+ CREATE TABLE IF NOT EXISTS example (
17
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
18
+ name TEXT NOT NULL,
19
+ created_at TEXT DEFAULT (datetime('now'))
20
+ )
21
+ `).run();
22
+ });
23
+
24
+ // API endpoint - the React frontend calls this
25
+ app.get("/api/hello", (c) => {
26
+ return c.json({ message: "Hello from Fling API!" });
27
+ });
@@ -0,0 +1,22 @@
1
+ {
2
+ "compilerOptions": {
3
+ "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
4
+ "target": "ES2020",
5
+ "useDefineForClassFields": true,
6
+ "lib": ["ES2020", "DOM", "DOM.Iterable"],
7
+ "module": "ESNext",
8
+ "skipLibCheck": true,
9
+ "moduleResolution": "bundler",
10
+ "allowImportingTsExtensions": true,
11
+ "isolatedModules": true,
12
+ "moduleDetection": "force",
13
+ "noEmit": true,
14
+ "jsx": "react-jsx",
15
+ "strict": true,
16
+ "noUnusedLocals": true,
17
+ "noUnusedParameters": true,
18
+ "noFallthroughCasesInSwitch": true,
19
+ "noUncheckedSideEffectImports": true
20
+ },
21
+ "include": ["src/react-app"]
22
+ }
@@ -0,0 +1,7 @@
1
+ {
2
+ "files": [],
3
+ "references": [
4
+ { "path": "./tsconfig.app.json" },
5
+ { "path": "./tsconfig.worker.json" }
6
+ ]
7
+ }
@@ -0,0 +1,11 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2024",
4
+ "module": "NodeNext",
5
+ "moduleResolution": "NodeNext",
6
+ "strict": true,
7
+ "esModuleInterop": true,
8
+ "skipLibCheck": true
9
+ },
10
+ "include": ["src/worker"]
11
+ }
@@ -0,0 +1,21 @@
1
+ import { defineConfig } from "vite";
2
+ import tailwindcss from "@tailwindcss/vite";
3
+ import react from "@vitejs/plugin-react";
4
+
5
+ export default defineConfig({
6
+ base: process.env["VITE_BASE"] || "/",
7
+ plugins: [tailwindcss(), react()],
8
+ build: {
9
+ outDir: "dist/client",
10
+ emptyOutDir: true,
11
+ },
12
+ server: {
13
+ port: 5173,
14
+ proxy: {
15
+ "/api": {
16
+ target: "http://localhost:3210",
17
+ changeOrigin: true,
18
+ },
19
+ },
20
+ },
21
+ });