kaddidlehopper 0.1.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/CONTEXT.md +139 -0
- package/README.md +47 -0
- package/add-ons/ai/README.md +34 -0
- package/add-ons/ai/assets/_dot_env.local.append +13 -0
- package/add-ons/ai/assets/src/components/AIAssistant.tsx +149 -0
- package/add-ons/ai/assets/src/lib/ai-hook.ts +21 -0
- package/add-ons/ai/assets/src/lib/weather-tools.ts +30 -0
- package/add-ons/ai/assets/src/routes/api.chat.ts +94 -0
- package/add-ons/ai/assets/src/routes/chat.css +175 -0
- package/add-ons/ai/assets/src/routes/chat.tsx +141 -0
- package/add-ons/ai/info.json +27 -0
- package/add-ons/ai/package.json +17 -0
- package/add-ons/ai/small-logo.svg +8 -0
- package/dist/cli.js +251 -0
- package/dist/index.js +33 -0
- package/dist/types/cli.d.ts +8 -0
- package/dist/types/index.d.ts +2 -0
- package/dist/types/types.d.ts +14 -0
- package/dist/types.js +1 -0
- package/examples/blog/README.md +60 -0
- package/examples/blog/assets/content/posts/beach.md +12 -0
- package/examples/blog/assets/content/posts/jungle.md.ejs +12 -0
- package/examples/blog/assets/content/posts/mountains.md.ejs +12 -0
- package/examples/blog/assets/content/posts/snorkeling.md.ejs +12 -0
- package/examples/blog/assets/content/posts/waterfall.md.ejs +12 -0
- package/examples/blog/assets/content-collections.ts +30 -0
- package/examples/blog/assets/public/beach.jpg +0 -0
- package/examples/blog/assets/public/jungle.jpg +0 -0
- package/examples/blog/assets/public/mountains.jpg +0 -0
- package/examples/blog/assets/public/snorkeling.jpg +0 -0
- package/examples/blog/assets/public/waterfall.jpg +0 -0
- package/examples/blog/assets/src/components/Header.tsx +52 -0
- package/examples/blog/assets/src/components/VacayAssistant.tsx +205 -0
- package/examples/blog/assets/src/components/blog-posts.tsx +78 -0
- package/examples/blog/assets/src/components/ui/card.tsx +92 -0
- package/examples/blog/assets/src/lib/blog-ai-hook.ts +25 -0
- package/examples/blog/assets/src/lib/blog-tools.ts +111 -0
- package/examples/blog/assets/src/lib/utils.ts +6 -0
- package/examples/blog/assets/src/routes/__root.tsx +57 -0
- package/examples/blog/assets/src/routes/api.blog-chat.ts +117 -0
- package/examples/blog/assets/src/routes/category.$category.tsx +19 -0
- package/examples/blog/assets/src/routes/index.tsx +19 -0
- package/examples/blog/assets/src/routes/posts.$slug.tsx +63 -0
- package/examples/blog/assets/src/styles.css +138 -0
- package/examples/blog/info.json +43 -0
- package/examples/blog/package.json +23 -0
- package/examples/events/README.md +110 -0
- package/examples/events/assets/content/speakers/andre-costa.md +22 -0
- package/examples/events/assets/content/speakers/hans-mueller.md.ejs +22 -0
- package/examples/events/assets/content/speakers/isabella-martinez.md.ejs +22 -0
- package/examples/events/assets/content/speakers/kenji-nakamura.md.ejs +22 -0
- package/examples/events/assets/content/speakers/marie-dubois.md.ejs +20 -0
- package/examples/events/assets/content/speakers/priya-sharma.md.ejs +22 -0
- package/examples/events/assets/content/talks/croissant-lamination-secrets.md +39 -0
- package/examples/events/assets/content/talks/french-macaron-mastery.md.ejs +39 -0
- package/examples/events/assets/content/talks/neapolitan-pizza-tradition-meets-innovation.md.ejs +39 -0
- package/examples/events/assets/content/talks/savory-breads-of-the-mediterranean.md.ejs +39 -0
- package/examples/events/assets/content/talks/sourdough-from-starter-to-masterpiece.md.ejs +36 -0
- package/examples/events/assets/content/talks/the-art-of-the-perfect-tart.md.ejs +32 -0
- package/examples/events/assets/content/talks/the-science-of-sugar.md.ejs +39 -0
- package/examples/events/assets/content/talks/umami-in-pastry-east-meets-west.md.ejs +39 -0
- package/examples/events/assets/content-collections.ts +56 -0
- package/examples/events/assets/public/background-1.jpg +0 -0
- package/examples/events/assets/public/background-2.jpg +0 -0
- package/examples/events/assets/public/background-3.jpg +0 -0
- package/examples/events/assets/public/background-4.jpg +0 -0
- package/examples/events/assets/public/conference-logo.png +0 -0
- package/examples/events/assets/public/favicon.ico +0 -0
- package/examples/events/assets/public/speakers/andre-costa.jpg +0 -0
- package/examples/events/assets/public/speakers/hans-mueller.jpg +0 -0
- package/examples/events/assets/public/speakers/isabella-martinez.jpg +0 -0
- package/examples/events/assets/public/speakers/kenji-nakamura.jpg +0 -0
- package/examples/events/assets/public/speakers/marie-dubois.jpg +0 -0
- package/examples/events/assets/public/speakers/priya-sharma.jpg +0 -0
- package/examples/events/assets/public/talks/croissant-lamination-secrets.jpg +0 -0
- package/examples/events/assets/public/talks/french-macaron-mastery.jpg +0 -0
- package/examples/events/assets/public/talks/neapolitan-pizza-tradition-meets-innovation.jpg +0 -0
- package/examples/events/assets/public/talks/savory-breads-of-the-mediterranean.jpg +0 -0
- package/examples/events/assets/public/talks/sourdough-from-starter-to-masterpiece.jpg +0 -0
- package/examples/events/assets/public/talks/the-art-of-the-perfect-tart.jpg +0 -0
- package/examples/events/assets/public/talks/the-science-of-sugar.jpg +0 -0
- package/examples/events/assets/public/talks/umami-in-pastry-east-meets-west.jpg +0 -0
- package/examples/events/assets/public/tanstack-circle-logo.png +0 -0
- package/examples/events/assets/public/tanstack-word-logo-white.svg +1 -0
- package/examples/events/assets/src/components/Header.tsx +59 -0
- package/examples/events/assets/src/components/HeaderNav.tsx +67 -0
- package/examples/events/assets/src/components/HeroCarousel.tsx +61 -0
- package/examples/events/assets/src/components/RemyAssistant.tsx +207 -0
- package/examples/events/assets/src/components/SpeakerCard.tsx +67 -0
- package/examples/events/assets/src/components/TalkCard.tsx +77 -0
- package/examples/events/assets/src/components/ui/card.tsx +92 -0
- package/examples/events/assets/src/lib/conference-ai-hook.ts +26 -0
- package/examples/events/assets/src/lib/conference-tools.ts +210 -0
- package/examples/events/assets/src/lib/model-selection.ts +1 -0
- package/examples/events/assets/src/lib/utils.ts +6 -0
- package/examples/events/assets/src/routes/__root.tsx +70 -0
- package/examples/events/assets/src/routes/api.remy-chat.ts +119 -0
- package/examples/events/assets/src/routes/index.tsx +192 -0
- package/examples/events/assets/src/routes/schedule.index.tsx +274 -0
- package/examples/events/assets/src/routes/speakers.$slug.tsx +122 -0
- package/examples/events/assets/src/routes/speakers.index.tsx +40 -0
- package/examples/events/assets/src/routes/talks.$slug.tsx +116 -0
- package/examples/events/assets/src/routes/talks.index.tsx +40 -0
- package/examples/events/assets/src/styles.css +182 -0
- package/examples/events/info.json +74 -0
- package/examples/events/package.json +23 -0
- package/examples/marketing/README.md +60 -0
- package/examples/marketing/assets/public/logo.png +0 -0
- package/examples/marketing/assets/public/motorcycle-adventure.jpg +0 -0
- package/examples/marketing/assets/public/motorcycle-cruiser.jpg +0 -0
- package/examples/marketing/assets/public/motorcycle-scooter.jpg +0 -0
- package/examples/marketing/assets/public/motorcycle-sport.jpg +0 -0
- package/examples/marketing/assets/public/motorcycle-supersport.jpg +0 -0
- package/examples/marketing/assets/src/components/Header.tsx +36 -0
- package/examples/marketing/assets/src/components/MotorcycleAIAssistant.tsx +162 -0
- package/examples/marketing/assets/src/components/MotorcycleRecommendation.tsx +53 -0
- package/examples/marketing/assets/src/data/motorcycles.ts.ejs +77 -0
- package/examples/marketing/assets/src/lib/motorcycle-ai-hook.ts +24 -0
- package/examples/marketing/assets/src/lib/motorcycle-tools.ts +42 -0
- package/examples/marketing/assets/src/routes/__root.tsx +57 -0
- package/examples/marketing/assets/src/routes/api.motorcycle-chat.ts +78 -0
- package/examples/marketing/assets/src/routes/index.tsx +72 -0
- package/examples/marketing/assets/src/routes/motorcycles/$motorcycleId.tsx +56 -0
- package/examples/marketing/assets/src/store/motorcycle-assistant.ts +3 -0
- package/examples/marketing/assets/src/styles.css +212 -0
- package/examples/marketing/info.json +38 -0
- package/examples/marketing/package.json +14 -0
- package/examples/resume/README.md +82 -0
- package/examples/resume/assets/content/education/code-school.md +17 -0
- package/examples/resume/assets/content/jobs/freelance.md.ejs +13 -0
- package/examples/resume/assets/content/jobs/initech-junior.md +20 -0
- package/examples/resume/assets/content/jobs/initech-lead.md.ejs +29 -0
- package/examples/resume/assets/content/jobs/initrode-senior.md.ejs +28 -0
- package/examples/resume/assets/content-collections.ts +36 -0
- package/examples/resume/assets/public/headshot-on-white.jpg +0 -0
- package/examples/resume/assets/src/components/Header.tsx +33 -0
- package/examples/resume/assets/src/components/ResumeAssistant.tsx +193 -0
- package/examples/resume/assets/src/components/ui/badge.tsx +46 -0
- package/examples/resume/assets/src/components/ui/card.tsx +92 -0
- package/examples/resume/assets/src/components/ui/checkbox.tsx +30 -0
- package/examples/resume/assets/src/components/ui/hover-card.tsx +44 -0
- package/examples/resume/assets/src/components/ui/separator.tsx +26 -0
- package/examples/resume/assets/src/lib/resume-ai-hook.ts +21 -0
- package/examples/resume/assets/src/lib/resume-tools.ts +165 -0
- package/examples/resume/assets/src/lib/utils.ts +6 -0
- package/examples/resume/assets/src/routes/api.resume-chat.ts +110 -0
- package/examples/resume/assets/src/routes/index.tsx +220 -0
- package/examples/resume/assets/src/styles.css +138 -0
- package/examples/resume/info.json +25 -0
- package/examples/resume/package.json +26 -0
- package/package.json +39 -0
- package/project/base/_dot_claude/skills/content-collections/SKILL.md +505 -0
- package/project/base/_dot_claude/skills/netlify-blobs/SKILL.md +410 -0
- package/project/base/_dot_claude/skills/netlify-db/SKILL.md +424 -0
- package/project/base/_dot_claude/skills/netlify-debugging/SKILL.md +419 -0
- package/project/base/_dot_claude/skills/netlify-forms/SKILL.md +243 -0
- package/project/base/_dot_claude/skills/netlify-functions/SKILL.md +372 -0
- package/project/base/_dot_claude/skills/tanstack-start-api-routes/SKILL.md +421 -0
- package/project/base/_dot_claude/skills/tanstack-start-loaders/SKILL.md +426 -0
- package/project/base/_dot_claude/skills/tanstack-start-project-setup/SKILL.md +493 -0
- package/project/base/_dot_claude/skills/tanstack-start-routes/SKILL.md +430 -0
- package/project/base/_dot_claude/skills/tanstack-start-server-functions/SKILL.md +445 -0
- package/project/base/_dot_claude/skills/tanstack-start-typesafe-routing/SKILL.md +494 -0
- package/project/base/_dot_gitignore +8 -0
- package/project/base/netlify.toml +7 -0
- package/project/base/package.json +33 -0
- package/project/base/public/favicon.ico +0 -0
- package/project/base/public/tanstack-circle-logo.png +0 -0
- package/project/base/public/tanstack-word-logo-white.svg +1 -0
- package/project/base/src/components/Header.tsx +17 -0
- package/project/base/src/components/HeaderNav.tsx.ejs +179 -0
- package/project/base/src/router.tsx +15 -0
- package/project/base/src/routes/__root.tsx +57 -0
- package/project/base/src/routes/index.tsx +48 -0
- package/project/base/src/styles.css +15 -0
- package/project/base/tsconfig.json +28 -0
- package/project/base/vite.config.ts.ejs +25 -0
- package/project/packages.json +22 -0
- package/scripts/check-outdated-packages.js +421 -0
- package/src/cli.ts +343 -0
- package/src/index.ts +49 -0
- package/src/types.ts +15 -0
- package/tsconfig.json +17 -0
|
@@ -0,0 +1,419 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: netlify-debugging
|
|
3
|
+
description: Debug Netlify deployments, functions, and builds. Use when troubleshooting build failures, function errors, deployment issues, or monitoring logs on Netlify.
|
|
4
|
+
license: Apache-2.0
|
|
5
|
+
metadata:
|
|
6
|
+
author: netlify
|
|
7
|
+
version: "1.0"
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# Netlify Debugging
|
|
11
|
+
|
|
12
|
+
Guide to debugging builds, functions, deployments, and runtime issues on Netlify.
|
|
13
|
+
|
|
14
|
+
## When to Use
|
|
15
|
+
|
|
16
|
+
- Build failures or timeouts
|
|
17
|
+
- Function errors or timeouts
|
|
18
|
+
- Deployment not working as expected
|
|
19
|
+
- Environment variable issues
|
|
20
|
+
- Redirect/rewrite problems
|
|
21
|
+
- Performance troubleshooting
|
|
22
|
+
|
|
23
|
+
## Function Logs
|
|
24
|
+
|
|
25
|
+
### Viewing in Netlify UI
|
|
26
|
+
|
|
27
|
+
1. Go to your site dashboard
|
|
28
|
+
2. Navigate to **Logs** → **Functions**
|
|
29
|
+
3. Select a function to view its logs
|
|
30
|
+
4. Filter by time range or search for specific errors
|
|
31
|
+
|
|
32
|
+
### Streaming Logs with CLI
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
# Stream all function logs
|
|
36
|
+
netlify functions:log
|
|
37
|
+
|
|
38
|
+
# Stream logs for a specific function
|
|
39
|
+
netlify functions:log --name=my-function
|
|
40
|
+
|
|
41
|
+
# Stream with tail (follow mode)
|
|
42
|
+
netlify functions:log --tail
|
|
43
|
+
|
|
44
|
+
# Filter by level
|
|
45
|
+
netlify functions:log --level=error
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### Adding Logs to Functions
|
|
49
|
+
|
|
50
|
+
```typescript
|
|
51
|
+
// netlify/functions/api.ts
|
|
52
|
+
export default async (request: Request, context: Context) => {
|
|
53
|
+
console.log("Request received:", {
|
|
54
|
+
method: request.method,
|
|
55
|
+
url: request.url,
|
|
56
|
+
headers: Object.fromEntries(request.headers),
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
try {
|
|
60
|
+
const result = await processRequest(request);
|
|
61
|
+
console.log("Request processed successfully:", result);
|
|
62
|
+
return Response.json(result);
|
|
63
|
+
} catch (error) {
|
|
64
|
+
console.error("Error processing request:", error);
|
|
65
|
+
return new Response("Internal error", { status: 500 });
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### Structured Logging
|
|
71
|
+
|
|
72
|
+
```typescript
|
|
73
|
+
function log(level: "info" | "warn" | "error", message: string, data?: object) {
|
|
74
|
+
const entry = {
|
|
75
|
+
timestamp: new Date().toISOString(),
|
|
76
|
+
level,
|
|
77
|
+
message,
|
|
78
|
+
...data,
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
if (level === "error") {
|
|
82
|
+
console.error(JSON.stringify(entry));
|
|
83
|
+
} else {
|
|
84
|
+
console.log(JSON.stringify(entry));
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Usage
|
|
89
|
+
log("info", "User logged in", { userId: "123" });
|
|
90
|
+
log("error", "Database connection failed", { error: err.message });
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
## Build Logs
|
|
94
|
+
|
|
95
|
+
### Viewing Build Logs
|
|
96
|
+
|
|
97
|
+
1. Go to **Deploys** in site dashboard
|
|
98
|
+
2. Click on a deploy
|
|
99
|
+
3. View **Deploy log** for full build output
|
|
100
|
+
|
|
101
|
+
### Local Build Testing
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
# Run the build command locally
|
|
105
|
+
npm run build
|
|
106
|
+
|
|
107
|
+
# Test with Netlify CLI (simulates Netlify environment)
|
|
108
|
+
netlify build
|
|
109
|
+
|
|
110
|
+
# Build with specific context
|
|
111
|
+
netlify build --context=deploy-preview
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### Common Build Issues
|
|
115
|
+
|
|
116
|
+
#### Out of Memory
|
|
117
|
+
|
|
118
|
+
```toml
|
|
119
|
+
# netlify.toml - Increase Node.js memory
|
|
120
|
+
[build.environment]
|
|
121
|
+
NODE_OPTIONS = "--max_old_space_size=4096"
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
#### Missing Dependencies
|
|
125
|
+
|
|
126
|
+
```bash
|
|
127
|
+
# Ensure all dependencies are in package.json
|
|
128
|
+
npm install missing-package --save
|
|
129
|
+
|
|
130
|
+
# Clear cache if issues persist
|
|
131
|
+
# In Netlify UI: Deploys → Trigger deploy → Clear cache and deploy site
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
#### Build Timeout
|
|
135
|
+
|
|
136
|
+
```toml
|
|
137
|
+
# netlify.toml - Check your build command
|
|
138
|
+
[build]
|
|
139
|
+
command = "npm run build"
|
|
140
|
+
publish = "dist"
|
|
141
|
+
|
|
142
|
+
# Consider:
|
|
143
|
+
# - Splitting build into smaller chunks
|
|
144
|
+
# - Using build caching
|
|
145
|
+
# - Optimizing slow operations
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
## Environment Variables
|
|
149
|
+
|
|
150
|
+
### Debugging Environment Variables
|
|
151
|
+
|
|
152
|
+
```typescript
|
|
153
|
+
// Log available environment variables (be careful with secrets!)
|
|
154
|
+
console.log("NODE_ENV:", process.env.NODE_ENV);
|
|
155
|
+
console.log("CONTEXT:", process.env.CONTEXT);
|
|
156
|
+
console.log("DEPLOY_URL:", process.env.DEPLOY_URL);
|
|
157
|
+
|
|
158
|
+
// Check if variable exists
|
|
159
|
+
if (!process.env.API_KEY) {
|
|
160
|
+
console.error("API_KEY is not set!");
|
|
161
|
+
}
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
### Netlify-Provided Variables
|
|
165
|
+
|
|
166
|
+
| Variable | Description |
|
|
167
|
+
|----------|-------------|
|
|
168
|
+
| `CONTEXT` | Build context: `production`, `deploy-preview`, `branch-deploy` |
|
|
169
|
+
| `DEPLOY_URL` | URL of the current deploy |
|
|
170
|
+
| `DEPLOY_PRIME_URL` | Primary URL for the deploy |
|
|
171
|
+
| `URL` | Main site URL |
|
|
172
|
+
| `SITE_ID` | Netlify site ID |
|
|
173
|
+
| `SITE_NAME` | Site name |
|
|
174
|
+
| `BUILD_ID` | Unique build ID |
|
|
175
|
+
| `COMMIT_REF` | Git commit SHA |
|
|
176
|
+
| `BRANCH` | Git branch name |
|
|
177
|
+
|
|
178
|
+
### Testing Different Environments
|
|
179
|
+
|
|
180
|
+
```bash
|
|
181
|
+
# Run locally with production environment
|
|
182
|
+
netlify dev --context production
|
|
183
|
+
|
|
184
|
+
# Check what variables are available
|
|
185
|
+
netlify env:list
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
## Debugging Redirects
|
|
189
|
+
|
|
190
|
+
### Testing Redirects Locally
|
|
191
|
+
|
|
192
|
+
```bash
|
|
193
|
+
# netlify dev respects _redirects and netlify.toml
|
|
194
|
+
netlify dev
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
### Common Redirect Issues
|
|
198
|
+
|
|
199
|
+
```toml
|
|
200
|
+
# netlify.toml
|
|
201
|
+
|
|
202
|
+
# Order matters - first match wins
|
|
203
|
+
[[redirects]]
|
|
204
|
+
from = "/api/*"
|
|
205
|
+
to = "/.netlify/functions/api/:splat"
|
|
206
|
+
status = 200
|
|
207
|
+
|
|
208
|
+
# SPA fallback (should be LAST)
|
|
209
|
+
[[redirects]]
|
|
210
|
+
from = "/*"
|
|
211
|
+
to = "/index.html"
|
|
212
|
+
status = 200
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
### Debugging with Headers
|
|
216
|
+
|
|
217
|
+
```toml
|
|
218
|
+
# Add debug headers to see which rule matched
|
|
219
|
+
[[headers]]
|
|
220
|
+
for = "/*"
|
|
221
|
+
[headers.values]
|
|
222
|
+
X-Debug = "true"
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
## Function Debugging
|
|
226
|
+
|
|
227
|
+
### Local Function Development
|
|
228
|
+
|
|
229
|
+
```bash
|
|
230
|
+
# Run functions locally with hot reload
|
|
231
|
+
netlify dev
|
|
232
|
+
|
|
233
|
+
# Functions available at:
|
|
234
|
+
# http://localhost:8888/.netlify/functions/function-name
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
### Debugging Timeouts
|
|
238
|
+
|
|
239
|
+
```typescript
|
|
240
|
+
// Check how long operations take
|
|
241
|
+
export default async (request: Request, context: Context) => {
|
|
242
|
+
const start = Date.now();
|
|
243
|
+
|
|
244
|
+
try {
|
|
245
|
+
const result = await someOperation();
|
|
246
|
+
console.log(`Operation took ${Date.now() - start}ms`);
|
|
247
|
+
return Response.json(result);
|
|
248
|
+
} catch (error) {
|
|
249
|
+
console.error(`Failed after ${Date.now() - start}ms:`, error);
|
|
250
|
+
throw error;
|
|
251
|
+
}
|
|
252
|
+
};
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
### Memory Issues
|
|
256
|
+
|
|
257
|
+
```typescript
|
|
258
|
+
// Monitor memory usage
|
|
259
|
+
export default async (request: Request) => {
|
|
260
|
+
const memBefore = process.memoryUsage();
|
|
261
|
+
console.log("Memory before:", {
|
|
262
|
+
heapUsed: Math.round(memBefore.heapUsed / 1024 / 1024) + "MB",
|
|
263
|
+
heapTotal: Math.round(memBefore.heapTotal / 1024 / 1024) + "MB",
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
// ... your code ...
|
|
267
|
+
|
|
268
|
+
const memAfter = process.memoryUsage();
|
|
269
|
+
console.log("Memory after:", {
|
|
270
|
+
heapUsed: Math.round(memAfter.heapUsed / 1024 / 1024) + "MB",
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
return new Response("OK");
|
|
274
|
+
};
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
## Edge Function Debugging
|
|
278
|
+
|
|
279
|
+
### Edge Function Logs
|
|
280
|
+
|
|
281
|
+
```bash
|
|
282
|
+
# View edge function logs
|
|
283
|
+
netlify logs:function --edge
|
|
284
|
+
|
|
285
|
+
# Or in the UI: Logs → Edge Functions
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
### Local Edge Function Testing
|
|
289
|
+
|
|
290
|
+
```bash
|
|
291
|
+
# Edge functions run locally with netlify dev
|
|
292
|
+
netlify dev
|
|
293
|
+
|
|
294
|
+
# Test specific paths configured in netlify.toml
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
## Deploy Previews
|
|
298
|
+
|
|
299
|
+
### Debugging Deploy Previews
|
|
300
|
+
|
|
301
|
+
1. Each PR gets a unique deploy preview URL
|
|
302
|
+
2. Check the deploy log for the specific PR
|
|
303
|
+
3. Test the preview URL to reproduce issues
|
|
304
|
+
|
|
305
|
+
```bash
|
|
306
|
+
# Get deploy preview URL
|
|
307
|
+
netlify deploy --build
|
|
308
|
+
# Outputs: Draft URL: https://abc123--sitename.netlify.app
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
## CLI Debugging Commands
|
|
312
|
+
|
|
313
|
+
```bash
|
|
314
|
+
# Check Netlify CLI status
|
|
315
|
+
netlify status
|
|
316
|
+
|
|
317
|
+
# View site info
|
|
318
|
+
netlify sites:list
|
|
319
|
+
|
|
320
|
+
# Check linked site
|
|
321
|
+
netlify link --status
|
|
322
|
+
|
|
323
|
+
# View current environment
|
|
324
|
+
netlify env:list
|
|
325
|
+
|
|
326
|
+
# Test function locally
|
|
327
|
+
netlify functions:invoke function-name --payload '{"key": "value"}'
|
|
328
|
+
|
|
329
|
+
# Open site dashboard
|
|
330
|
+
netlify open
|
|
331
|
+
|
|
332
|
+
# Open function logs
|
|
333
|
+
netlify open:logs
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
## Troubleshooting Checklist
|
|
337
|
+
|
|
338
|
+
### Build Failures
|
|
339
|
+
|
|
340
|
+
- [ ] Check build logs for error messages
|
|
341
|
+
- [ ] Verify `build.command` in `netlify.toml`
|
|
342
|
+
- [ ] Ensure all dependencies are in `package.json`
|
|
343
|
+
- [ ] Check Node.js version compatibility
|
|
344
|
+
- [ ] Try clearing cache and redeploying
|
|
345
|
+
- [ ] Test build locally with `netlify build`
|
|
346
|
+
|
|
347
|
+
### Function Issues
|
|
348
|
+
|
|
349
|
+
- [ ] Check function logs for errors
|
|
350
|
+
- [ ] Verify function file is in correct directory
|
|
351
|
+
- [ ] Check function naming (no spaces, correct extension)
|
|
352
|
+
- [ ] Verify environment variables are set
|
|
353
|
+
- [ ] Test locally with `netlify dev`
|
|
354
|
+
- [ ] Check for timeout issues (default 10s)
|
|
355
|
+
|
|
356
|
+
### Deployment Issues
|
|
357
|
+
|
|
358
|
+
- [ ] Verify publish directory is correct
|
|
359
|
+
- [ ] Check that build outputs files to publish directory
|
|
360
|
+
- [ ] Verify no `.gitignore` issues with built files
|
|
361
|
+
- [ ] Check deploy summary for warnings
|
|
362
|
+
|
|
363
|
+
### Environment Variable Issues
|
|
364
|
+
|
|
365
|
+
- [ ] Variables set in Netlify UI are available
|
|
366
|
+
- [ ] Check variable scopes (build vs. functions)
|
|
367
|
+
- [ ] Verify variable names match exactly (case-sensitive)
|
|
368
|
+
- [ ] Redeploy after adding new variables
|
|
369
|
+
|
|
370
|
+
## Log Drains (Advanced)
|
|
371
|
+
|
|
372
|
+
Send logs to external services:
|
|
373
|
+
|
|
374
|
+
```bash
|
|
375
|
+
# Configure log drain to external service
|
|
376
|
+
netlify logs:drain add --type http --destination https://logs.example.com/ingest
|
|
377
|
+
|
|
378
|
+
# List configured drains
|
|
379
|
+
netlify logs:drain list
|
|
380
|
+
```
|
|
381
|
+
|
|
382
|
+
## Performance Debugging
|
|
383
|
+
|
|
384
|
+
### Function Cold Starts
|
|
385
|
+
|
|
386
|
+
```typescript
|
|
387
|
+
// Measure cold start impact
|
|
388
|
+
let isWarm = false;
|
|
389
|
+
|
|
390
|
+
export default async (request: Request) => {
|
|
391
|
+
if (!isWarm) {
|
|
392
|
+
console.log("Cold start detected");
|
|
393
|
+
isWarm = true;
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
// ... function logic
|
|
397
|
+
};
|
|
398
|
+
```
|
|
399
|
+
|
|
400
|
+
### Response Time Monitoring
|
|
401
|
+
|
|
402
|
+
```typescript
|
|
403
|
+
export default async (request: Request, context: Context) => {
|
|
404
|
+
const startTime = performance.now();
|
|
405
|
+
|
|
406
|
+
const response = await handleRequest(request);
|
|
407
|
+
|
|
408
|
+
const duration = performance.now() - startTime;
|
|
409
|
+
console.log(`Request processed in ${duration.toFixed(2)}ms`);
|
|
410
|
+
|
|
411
|
+
return new Response(response.body, {
|
|
412
|
+
...response,
|
|
413
|
+
headers: {
|
|
414
|
+
...response.headers,
|
|
415
|
+
"X-Response-Time": `${duration.toFixed(2)}ms`,
|
|
416
|
+
},
|
|
417
|
+
});
|
|
418
|
+
};
|
|
419
|
+
```
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: netlify-forms
|
|
3
|
+
description: Handle Netlify Forms including HTML form setup, spam filtering with honeypot fields, AJAX submissions, and form notifications. Use when implementing contact forms, signup forms, or any form submission handling on Netlify-hosted sites.
|
|
4
|
+
license: Apache-2.0
|
|
5
|
+
metadata:
|
|
6
|
+
author: netlify
|
|
7
|
+
version: "1.0"
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# Netlify Forms
|
|
11
|
+
|
|
12
|
+
Netlify Forms automatically handles form submissions without requiring server-side code. Forms are detected at build time and submissions are stored in the Netlify dashboard.
|
|
13
|
+
|
|
14
|
+
## When to Use
|
|
15
|
+
|
|
16
|
+
- Adding contact forms to static sites
|
|
17
|
+
- Capturing user signups or feedback
|
|
18
|
+
- Form submissions without a backend
|
|
19
|
+
- Spam filtering without external services
|
|
20
|
+
|
|
21
|
+
## Basic HTML Form
|
|
22
|
+
|
|
23
|
+
Add `data-netlify="true"` to any HTML form:
|
|
24
|
+
|
|
25
|
+
```html
|
|
26
|
+
<form name="contact" method="POST" data-netlify="true">
|
|
27
|
+
<input type="hidden" name="form-name" value="contact" />
|
|
28
|
+
<p>
|
|
29
|
+
<label>Name: <input type="text" name="name" required /></label>
|
|
30
|
+
</p>
|
|
31
|
+
<p>
|
|
32
|
+
<label>Email: <input type="email" name="email" required /></label>
|
|
33
|
+
</p>
|
|
34
|
+
<p>
|
|
35
|
+
<label>Message: <textarea name="message" required></textarea></label>
|
|
36
|
+
</p>
|
|
37
|
+
<p>
|
|
38
|
+
<button type="submit">Send</button>
|
|
39
|
+
</p>
|
|
40
|
+
</form>
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
**Critical**: The hidden `form-name` input MUST match the form's `name` attribute.
|
|
44
|
+
|
|
45
|
+
## Spam Filtering with Honeypot
|
|
46
|
+
|
|
47
|
+
Add a honeypot field that bots will fill but humans won't see:
|
|
48
|
+
|
|
49
|
+
```html
|
|
50
|
+
<form name="contact" method="POST" data-netlify="true" netlify-honeypot="bot-field">
|
|
51
|
+
<input type="hidden" name="form-name" value="contact" />
|
|
52
|
+
|
|
53
|
+
<!-- Honeypot field - hidden from humans -->
|
|
54
|
+
<p class="hidden" style="display:none;">
|
|
55
|
+
<label>Don't fill this out: <input name="bot-field" /></label>
|
|
56
|
+
</p>
|
|
57
|
+
|
|
58
|
+
<p>
|
|
59
|
+
<label>Name: <input type="text" name="name" required /></label>
|
|
60
|
+
</p>
|
|
61
|
+
<p>
|
|
62
|
+
<label>Email: <input type="email" name="email" required /></label>
|
|
63
|
+
</p>
|
|
64
|
+
<p>
|
|
65
|
+
<button type="submit">Send</button>
|
|
66
|
+
</p>
|
|
67
|
+
</form>
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## AJAX/JavaScript Submission
|
|
71
|
+
|
|
72
|
+
For SPAs or enhanced UX, submit forms via JavaScript:
|
|
73
|
+
|
|
74
|
+
```typescript
|
|
75
|
+
async function handleSubmit(event: React.FormEvent<HTMLFormElement>) {
|
|
76
|
+
event.preventDefault();
|
|
77
|
+
|
|
78
|
+
const form = event.currentTarget;
|
|
79
|
+
const formData = new FormData(form);
|
|
80
|
+
|
|
81
|
+
try {
|
|
82
|
+
const response = await fetch('/', {
|
|
83
|
+
method: 'POST',
|
|
84
|
+
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
|
85
|
+
body: new URLSearchParams(formData as any).toString(),
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
if (response.ok) {
|
|
89
|
+
// Success - show thank you message or redirect
|
|
90
|
+
console.log('Form submitted successfully');
|
|
91
|
+
} else {
|
|
92
|
+
throw new Error('Form submission failed');
|
|
93
|
+
}
|
|
94
|
+
} catch (error) {
|
|
95
|
+
console.error('Error:', error);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
**Important**: Even with AJAX submission, you need the form HTML present in your built output for Netlify to detect it.
|
|
101
|
+
|
|
102
|
+
## React Component Example
|
|
103
|
+
|
|
104
|
+
```tsx
|
|
105
|
+
export function ContactForm() {
|
|
106
|
+
const [status, setStatus] = useState<'idle' | 'submitting' | 'success' | 'error'>('idle');
|
|
107
|
+
|
|
108
|
+
async function handleSubmit(e: React.FormEvent<HTMLFormElement>) {
|
|
109
|
+
e.preventDefault();
|
|
110
|
+
setStatus('submitting');
|
|
111
|
+
|
|
112
|
+
const form = e.currentTarget;
|
|
113
|
+
const formData = new FormData(form);
|
|
114
|
+
|
|
115
|
+
try {
|
|
116
|
+
const response = await fetch('/', {
|
|
117
|
+
method: 'POST',
|
|
118
|
+
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
|
119
|
+
body: new URLSearchParams(formData as any).toString(),
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
if (response.ok) {
|
|
123
|
+
setStatus('success');
|
|
124
|
+
form.reset();
|
|
125
|
+
} else {
|
|
126
|
+
setStatus('error');
|
|
127
|
+
}
|
|
128
|
+
} catch {
|
|
129
|
+
setStatus('error');
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
if (status === 'success') {
|
|
134
|
+
return <p>Thank you for your message!</p>;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
return (
|
|
138
|
+
<form
|
|
139
|
+
name="contact"
|
|
140
|
+
method="POST"
|
|
141
|
+
data-netlify="true"
|
|
142
|
+
netlify-honeypot="bot-field"
|
|
143
|
+
onSubmit={handleSubmit}
|
|
144
|
+
>
|
|
145
|
+
<input type="hidden" name="form-name" value="contact" />
|
|
146
|
+
<p style={{ display: 'none' }}>
|
|
147
|
+
<label>Don't fill this out: <input name="bot-field" /></label>
|
|
148
|
+
</p>
|
|
149
|
+
|
|
150
|
+
<label>
|
|
151
|
+
Name:
|
|
152
|
+
<input type="text" name="name" required />
|
|
153
|
+
</label>
|
|
154
|
+
|
|
155
|
+
<label>
|
|
156
|
+
Email:
|
|
157
|
+
<input type="email" name="email" required />
|
|
158
|
+
</label>
|
|
159
|
+
|
|
160
|
+
<label>
|
|
161
|
+
Message:
|
|
162
|
+
<textarea name="message" required />
|
|
163
|
+
</label>
|
|
164
|
+
|
|
165
|
+
<button type="submit" disabled={status === 'submitting'}>
|
|
166
|
+
{status === 'submitting' ? 'Sending...' : 'Send'}
|
|
167
|
+
</button>
|
|
168
|
+
|
|
169
|
+
{status === 'error' && <p>Something went wrong. Please try again.</p>}
|
|
170
|
+
</form>
|
|
171
|
+
);
|
|
172
|
+
}
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
## SPA/Framework Considerations
|
|
176
|
+
|
|
177
|
+
For React, Vue, or other SPAs where forms are rendered client-side:
|
|
178
|
+
|
|
179
|
+
1. **Option A**: Include a hidden static HTML form in your `index.html` or a static file:
|
|
180
|
+
|
|
181
|
+
```html
|
|
182
|
+
<!-- In public/index.html or a static HTML file -->
|
|
183
|
+
<form name="contact" netlify netlify-honeypot="bot-field" hidden>
|
|
184
|
+
<input type="text" name="name" />
|
|
185
|
+
<input type="email" name="email" />
|
|
186
|
+
<textarea name="message"></textarea>
|
|
187
|
+
</form>
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
2. **Option B**: Use a prerendered/SSR page that includes the form markup at build time.
|
|
191
|
+
|
|
192
|
+
## File Uploads
|
|
193
|
+
|
|
194
|
+
Forms can accept file uploads:
|
|
195
|
+
|
|
196
|
+
```html
|
|
197
|
+
<form name="upload" method="POST" data-netlify="true" enctype="multipart/form-data">
|
|
198
|
+
<input type="hidden" name="form-name" value="upload" />
|
|
199
|
+
<input type="file" name="attachment" />
|
|
200
|
+
<button type="submit">Upload</button>
|
|
201
|
+
</form>
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
**Limits**:
|
|
205
|
+
- Max 10MB per file
|
|
206
|
+
- Max 10MB total per submission
|
|
207
|
+
|
|
208
|
+
## Custom Success Page
|
|
209
|
+
|
|
210
|
+
Redirect to a thank-you page after submission:
|
|
211
|
+
|
|
212
|
+
```html
|
|
213
|
+
<form name="contact" method="POST" data-netlify="true" action="/thank-you">
|
|
214
|
+
<!-- form fields -->
|
|
215
|
+
</form>
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
## Form Notifications
|
|
219
|
+
|
|
220
|
+
Configure email notifications in Netlify UI:
|
|
221
|
+
1. Go to Site settings → Forms → Form notifications
|
|
222
|
+
2. Add email notification for form submissions
|
|
223
|
+
3. Optionally integrate with Slack, webhooks, or Zapier
|
|
224
|
+
|
|
225
|
+
## Common Issues
|
|
226
|
+
|
|
227
|
+
### Form not detected
|
|
228
|
+
- Ensure `data-netlify="true"` is present in static HTML at build time
|
|
229
|
+
- Check that `name` attribute matches the hidden `form-name` value
|
|
230
|
+
- For SPAs, include a hidden static form or use SSR
|
|
231
|
+
|
|
232
|
+
### Submissions not appearing
|
|
233
|
+
- Check the Forms tab in Netlify dashboard
|
|
234
|
+
- Verify the form name matches exactly
|
|
235
|
+
- Check spam folder in Forms dashboard
|
|
236
|
+
|
|
237
|
+
### 404 on submission
|
|
238
|
+
- Ensure you're posting to `/` or the page URL where the form exists
|
|
239
|
+
- Include `Content-Type: application/x-www-form-urlencoded` header for AJAX
|
|
240
|
+
|
|
241
|
+
## Environment Variables
|
|
242
|
+
|
|
243
|
+
No environment variables required - Netlify Forms work automatically on Netlify-hosted sites.
|