kaddidlehopper 0.5.0 → 0.6.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.
Files changed (296) hide show
  1. package/dist/cli.js +104 -208
  2. package/dist/index.js +1 -31
  3. package/dist/types/cli.d.ts +1 -8
  4. package/dist/types/types.d.ts +7 -12
  5. package/package.json +1 -2
  6. package/src/cli.ts +129 -271
  7. package/src/index.ts +1 -47
  8. package/src/types.ts +7 -13
  9. package/CONTEXT.md +0 -139
  10. package/add-ons/ai/README.md +0 -34
  11. package/add-ons/ai/assets/AGENTS.md.append +0 -24
  12. package/add-ons/ai/assets/_dot_env.local.append +0 -13
  13. package/add-ons/ai/assets/src/components/AIAssistant.tsx +0 -149
  14. package/add-ons/ai/assets/src/lib/ai-hook.ts +0 -21
  15. package/add-ons/ai/assets/src/lib/weather-tools.ts +0 -30
  16. package/add-ons/ai/assets/src/routes/api.chat.ts +0 -94
  17. package/add-ons/ai/assets/src/routes/chat.css +0 -175
  18. package/add-ons/ai/assets/src/routes/chat.tsx +0 -141
  19. package/add-ons/ai/info.json +0 -22
  20. package/add-ons/ai/package.json +0 -17
  21. package/add-ons/ai/small-logo.svg +0 -8
  22. package/add-ons/db/assets/AGENTS.md.append +0 -22
  23. package/add-ons/db/assets/DB-SETUP.md +0 -65
  24. package/add-ons/db/assets/_dot_env.local.append +0 -2
  25. package/add-ons/db/assets/drizzle.config.ts +0 -10
  26. package/add-ons/db/assets/src/db/index.ts +0 -8
  27. package/add-ons/db/assets/src/db/schema.ts +0 -8
  28. package/add-ons/db/assets/src/routes/db-example.tsx +0 -118
  29. package/add-ons/db/assets/src/server/guestbook.functions.ts +0 -23
  30. package/add-ons/db/info.json +0 -22
  31. package/add-ons/db/package.json +0 -10
  32. package/add-ons/forms/assets/AGENTS.md.append +0 -13
  33. package/add-ons/forms/assets/public/form-example.html +0 -14
  34. package/add-ons/forms/assets/src/routes/form-example.tsx +0 -76
  35. package/add-ons/forms/info.json +0 -18
  36. package/add-ons/forms/package.json +0 -3
  37. package/examples/ai-chat/README.md +0 -36
  38. package/examples/ai-chat/assets/AGENTS.md.append +0 -24
  39. package/examples/ai-chat/assets/src/lib/ai-hook.ts +0 -21
  40. package/examples/ai-chat/assets/src/lib/weather-tools.ts +0 -30
  41. package/examples/ai-chat/assets/src/routes/__root.tsx +0 -57
  42. package/examples/ai-chat/assets/src/routes/api.chat.ts +0 -94
  43. package/examples/ai-chat/assets/src/routes/index.tsx +0 -141
  44. package/examples/ai-chat/assets/src/styles.css +0 -165
  45. package/examples/ai-chat/info.json +0 -25
  46. package/examples/ai-chat/package.json +0 -14
  47. package/examples/blog/README.md +0 -60
  48. package/examples/blog/assets/AGENTS.md.append +0 -18
  49. package/examples/blog/assets/content/posts/beach.md +0 -12
  50. package/examples/blog/assets/content/posts/jungle.md.ejs +0 -12
  51. package/examples/blog/assets/content/posts/mountains.md.ejs +0 -12
  52. package/examples/blog/assets/content/posts/snorkeling.md.ejs +0 -12
  53. package/examples/blog/assets/content/posts/waterfall.md.ejs +0 -12
  54. package/examples/blog/assets/content-collections.ts +0 -30
  55. package/examples/blog/assets/public/beach.jpg +0 -0
  56. package/examples/blog/assets/public/jungle.jpg +0 -0
  57. package/examples/blog/assets/public/mountains.jpg +0 -0
  58. package/examples/blog/assets/public/snorkeling.jpg +0 -0
  59. package/examples/blog/assets/public/waterfall.jpg +0 -0
  60. package/examples/blog/assets/src/components/Header.tsx +0 -52
  61. package/examples/blog/assets/src/components/VacayAssistant.tsx +0 -205
  62. package/examples/blog/assets/src/components/blog-posts.tsx +0 -78
  63. package/examples/blog/assets/src/components/ui/card.tsx +0 -92
  64. package/examples/blog/assets/src/lib/blog-ai-hook.ts +0 -25
  65. package/examples/blog/assets/src/lib/blog-tools.ts +0 -111
  66. package/examples/blog/assets/src/lib/utils.ts +0 -6
  67. package/examples/blog/assets/src/routes/__root.tsx +0 -57
  68. package/examples/blog/assets/src/routes/api.blog-chat.ts +0 -117
  69. package/examples/blog/assets/src/routes/category.$category.tsx +0 -19
  70. package/examples/blog/assets/src/routes/index.tsx +0 -19
  71. package/examples/blog/assets/src/routes/posts.$slug.tsx +0 -63
  72. package/examples/blog/assets/src/styles.css +0 -138
  73. package/examples/blog/info.json +0 -40
  74. package/examples/blog/package.json +0 -23
  75. package/examples/calculator/README.md +0 -16
  76. package/examples/calculator/assets/AGENTS.md.append +0 -9
  77. package/examples/calculator/assets/src/components/Calculator.tsx +0 -238
  78. package/examples/calculator/assets/src/components/Header.tsx +0 -17
  79. package/examples/calculator/assets/src/routes/__root.tsx +0 -57
  80. package/examples/calculator/assets/src/routes/index.tsx +0 -14
  81. package/examples/calculator/info.json +0 -20
  82. package/examples/calculator/package.json +0 -1
  83. package/examples/dashboard/README.md +0 -17
  84. package/examples/dashboard/assets/AGENTS.md.append +0 -12
  85. package/examples/dashboard/assets/src/components/Header.tsx +0 -17
  86. package/examples/dashboard/assets/src/routes/__root.tsx +0 -57
  87. package/examples/dashboard/assets/src/routes/index.tsx +0 -158
  88. package/examples/dashboard/info.json +0 -19
  89. package/examples/dashboard/package.json +0 -6
  90. package/examples/ecommerce/README.md +0 -48
  91. package/examples/ecommerce/assets/AGENTS.md.append +0 -22
  92. package/examples/ecommerce/assets/public/logo.png +0 -0
  93. package/examples/ecommerce/assets/public/motorcycle-scooter.jpg +0 -0
  94. package/examples/ecommerce/assets/src/components/BuyButton.tsx +0 -35
  95. package/examples/ecommerce/assets/src/components/Header.tsx +0 -36
  96. package/examples/ecommerce/assets/src/components/MotorcycleAIAssistant.tsx +0 -162
  97. package/examples/ecommerce/assets/src/components/MotorcycleRecommendation.tsx +0 -53
  98. package/examples/ecommerce/assets/src/data/motorcycles.ts +0 -27
  99. package/examples/ecommerce/assets/src/lib/motorcycle-ai-hook.ts +0 -24
  100. package/examples/ecommerce/assets/src/lib/motorcycle-tools.ts +0 -42
  101. package/examples/ecommerce/assets/src/lib/stripe.server.ts +0 -39
  102. package/examples/ecommerce/assets/src/routes/__root.tsx +0 -57
  103. package/examples/ecommerce/assets/src/routes/api.motorcycle-chat.ts +0 -78
  104. package/examples/ecommerce/assets/src/routes/checkout/cancel.tsx +0 -25
  105. package/examples/ecommerce/assets/src/routes/checkout/success.tsx +0 -25
  106. package/examples/ecommerce/assets/src/routes/index.tsx +0 -76
  107. package/examples/ecommerce/assets/src/routes/motorcycles/$motorcycleId.tsx +0 -55
  108. package/examples/ecommerce/assets/src/store/motorcycle-assistant.ts +0 -3
  109. package/examples/ecommerce/assets/src/styles.css +0 -212
  110. package/examples/ecommerce/info.json +0 -38
  111. package/examples/ecommerce/package.json +0 -13
  112. package/examples/events/README.md +0 -110
  113. package/examples/events/assets/AGENTS.md.append +0 -21
  114. package/examples/events/assets/content/speakers/andre-costa.md +0 -22
  115. package/examples/events/assets/content/speakers/hans-mueller.md.ejs +0 -22
  116. package/examples/events/assets/content/speakers/isabella-martinez.md.ejs +0 -22
  117. package/examples/events/assets/content/speakers/kenji-nakamura.md.ejs +0 -22
  118. package/examples/events/assets/content/speakers/marie-dubois.md.ejs +0 -20
  119. package/examples/events/assets/content/speakers/priya-sharma.md.ejs +0 -22
  120. package/examples/events/assets/content/talks/croissant-lamination-secrets.md +0 -39
  121. package/examples/events/assets/content/talks/french-macaron-mastery.md.ejs +0 -39
  122. package/examples/events/assets/content/talks/neapolitan-pizza-tradition-meets-innovation.md.ejs +0 -39
  123. package/examples/events/assets/content/talks/savory-breads-of-the-mediterranean.md.ejs +0 -39
  124. package/examples/events/assets/content/talks/sourdough-from-starter-to-masterpiece.md.ejs +0 -36
  125. package/examples/events/assets/content/talks/the-art-of-the-perfect-tart.md.ejs +0 -32
  126. package/examples/events/assets/content/talks/the-science-of-sugar.md.ejs +0 -39
  127. package/examples/events/assets/content/talks/umami-in-pastry-east-meets-west.md.ejs +0 -39
  128. package/examples/events/assets/content-collections.ts +0 -56
  129. package/examples/events/assets/public/background-1.jpg +0 -0
  130. package/examples/events/assets/public/background-2.jpg +0 -0
  131. package/examples/events/assets/public/background-3.jpg +0 -0
  132. package/examples/events/assets/public/background-4.jpg +0 -0
  133. package/examples/events/assets/public/conference-logo.png +0 -0
  134. package/examples/events/assets/public/favicon.ico +0 -0
  135. package/examples/events/assets/public/speakers/andre-costa.jpg +0 -0
  136. package/examples/events/assets/public/speakers/hans-mueller.jpg +0 -0
  137. package/examples/events/assets/public/speakers/isabella-martinez.jpg +0 -0
  138. package/examples/events/assets/public/speakers/kenji-nakamura.jpg +0 -0
  139. package/examples/events/assets/public/speakers/marie-dubois.jpg +0 -0
  140. package/examples/events/assets/public/speakers/priya-sharma.jpg +0 -0
  141. package/examples/events/assets/public/talks/croissant-lamination-secrets.jpg +0 -0
  142. package/examples/events/assets/public/talks/french-macaron-mastery.jpg +0 -0
  143. package/examples/events/assets/public/talks/neapolitan-pizza-tradition-meets-innovation.jpg +0 -0
  144. package/examples/events/assets/public/talks/savory-breads-of-the-mediterranean.jpg +0 -0
  145. package/examples/events/assets/public/talks/sourdough-from-starter-to-masterpiece.jpg +0 -0
  146. package/examples/events/assets/public/talks/the-art-of-the-perfect-tart.jpg +0 -0
  147. package/examples/events/assets/public/talks/the-science-of-sugar.jpg +0 -0
  148. package/examples/events/assets/public/talks/umami-in-pastry-east-meets-west.jpg +0 -0
  149. package/examples/events/assets/public/tanstack-circle-logo.png +0 -0
  150. package/examples/events/assets/public/tanstack-word-logo-white.svg +0 -1
  151. package/examples/events/assets/src/components/Header.tsx +0 -59
  152. package/examples/events/assets/src/components/HeaderNav.tsx +0 -67
  153. package/examples/events/assets/src/components/HeroCarousel.tsx +0 -61
  154. package/examples/events/assets/src/components/RemyAssistant.tsx +0 -207
  155. package/examples/events/assets/src/components/SpeakerCard.tsx +0 -67
  156. package/examples/events/assets/src/components/TalkCard.tsx +0 -77
  157. package/examples/events/assets/src/components/ui/card.tsx +0 -92
  158. package/examples/events/assets/src/lib/conference-ai-hook.ts +0 -26
  159. package/examples/events/assets/src/lib/conference-tools.ts +0 -210
  160. package/examples/events/assets/src/lib/model-selection.ts +0 -1
  161. package/examples/events/assets/src/lib/utils.ts +0 -6
  162. package/examples/events/assets/src/routes/__root.tsx +0 -70
  163. package/examples/events/assets/src/routes/api.remy-chat.ts +0 -119
  164. package/examples/events/assets/src/routes/index.tsx +0 -192
  165. package/examples/events/assets/src/routes/schedule.index.tsx +0 -274
  166. package/examples/events/assets/src/routes/speakers.$slug.tsx +0 -122
  167. package/examples/events/assets/src/routes/speakers.index.tsx +0 -40
  168. package/examples/events/assets/src/routes/talks.$slug.tsx +0 -116
  169. package/examples/events/assets/src/routes/talks.index.tsx +0 -40
  170. package/examples/events/assets/src/styles.css +0 -182
  171. package/examples/events/info.json +0 -61
  172. package/examples/events/package.json +0 -23
  173. package/examples/marketing/README.md +0 -60
  174. package/examples/marketing/assets/AGENTS.md.append +0 -15
  175. package/examples/marketing/assets/public/logo.png +0 -0
  176. package/examples/marketing/assets/public/motorcycle-adventure.jpg +0 -0
  177. package/examples/marketing/assets/public/motorcycle-cruiser.jpg +0 -0
  178. package/examples/marketing/assets/public/motorcycle-scooter.jpg +0 -0
  179. package/examples/marketing/assets/public/motorcycle-sport.jpg +0 -0
  180. package/examples/marketing/assets/public/motorcycle-supersport.jpg +0 -0
  181. package/examples/marketing/assets/src/components/Header.tsx +0 -36
  182. package/examples/marketing/assets/src/components/MotorcycleAIAssistant.tsx +0 -162
  183. package/examples/marketing/assets/src/components/MotorcycleRecommendation.tsx +0 -53
  184. package/examples/marketing/assets/src/data/motorcycles.ts.ejs +0 -77
  185. package/examples/marketing/assets/src/lib/motorcycle-ai-hook.ts +0 -24
  186. package/examples/marketing/assets/src/lib/motorcycle-tools.ts +0 -42
  187. package/examples/marketing/assets/src/routes/__root.tsx +0 -57
  188. package/examples/marketing/assets/src/routes/api.motorcycle-chat.ts +0 -78
  189. package/examples/marketing/assets/src/routes/index.tsx +0 -72
  190. package/examples/marketing/assets/src/routes/motorcycles/$motorcycleId.tsx +0 -56
  191. package/examples/marketing/assets/src/store/motorcycle-assistant.ts +0 -3
  192. package/examples/marketing/assets/src/styles.css +0 -212
  193. package/examples/marketing/info.json +0 -33
  194. package/examples/marketing/package.json +0 -14
  195. package/examples/portfolio/README.md +0 -49
  196. package/examples/portfolio/assets/AGENTS.md.append +0 -21
  197. package/examples/portfolio/assets/content/blog/getting-started-with-tanstack.md +0 -53
  198. package/examples/portfolio/assets/content/blog/react-19-features.md +0 -78
  199. package/examples/portfolio/assets/content/blog/tailwind-css-v4-guide.md +0 -60
  200. package/examples/portfolio/assets/content/education/code-school.md +0 -17
  201. package/examples/portfolio/assets/content/jobs/initech-junior.md +0 -20
  202. package/examples/portfolio/assets/content/projects/portfolio-site.md +0 -15
  203. package/examples/portfolio/assets/content/projects/task-manager.md +0 -15
  204. package/examples/portfolio/assets/content-collections.ts +0 -65
  205. package/examples/portfolio/assets/public/contact.html +0 -6
  206. package/examples/portfolio/assets/public/headshot-on-white.jpg +0 -0
  207. package/examples/portfolio/assets/src/components/Header.tsx +0 -33
  208. package/examples/portfolio/assets/src/components/ResumeAssistant.tsx +0 -193
  209. package/examples/portfolio/assets/src/components/ui/badge.tsx +0 -46
  210. package/examples/portfolio/assets/src/components/ui/card.tsx +0 -92
  211. package/examples/portfolio/assets/src/components/ui/checkbox.tsx +0 -30
  212. package/examples/portfolio/assets/src/components/ui/hover-card.tsx +0 -44
  213. package/examples/portfolio/assets/src/components/ui/separator.tsx +0 -26
  214. package/examples/portfolio/assets/src/lib/resume-ai-hook.ts +0 -21
  215. package/examples/portfolio/assets/src/lib/resume-tools.ts +0 -165
  216. package/examples/portfolio/assets/src/lib/utils.ts +0 -6
  217. package/examples/portfolio/assets/src/routes/__root.tsx +0 -57
  218. package/examples/portfolio/assets/src/routes/api.resume-chat.ts +0 -116
  219. package/examples/portfolio/assets/src/routes/blog/$slug.tsx +0 -73
  220. package/examples/portfolio/assets/src/routes/contact.tsx +0 -121
  221. package/examples/portfolio/assets/src/routes/index.tsx +0 -55
  222. package/examples/portfolio/assets/src/routes/projects.tsx +0 -62
  223. package/examples/portfolio/assets/src/routes/resume.tsx +0 -220
  224. package/examples/portfolio/assets/src/styles.css +0 -138
  225. package/examples/portfolio/info.json +0 -50
  226. package/examples/portfolio/package.json +0 -26
  227. package/examples/resume/README.md +0 -82
  228. package/examples/resume/assets/AGENTS.md.append +0 -19
  229. package/examples/resume/assets/content/education/code-school.md +0 -17
  230. package/examples/resume/assets/content/jobs/freelance.md.ejs +0 -13
  231. package/examples/resume/assets/content/jobs/initech-junior.md +0 -20
  232. package/examples/resume/assets/content/jobs/initech-lead.md.ejs +0 -29
  233. package/examples/resume/assets/content/jobs/initrode-senior.md.ejs +0 -28
  234. package/examples/resume/assets/content-collections.ts +0 -36
  235. package/examples/resume/assets/public/headshot-on-white.jpg +0 -0
  236. package/examples/resume/assets/src/components/Header.tsx +0 -33
  237. package/examples/resume/assets/src/components/ResumeAssistant.tsx +0 -193
  238. package/examples/resume/assets/src/components/ui/badge.tsx +0 -46
  239. package/examples/resume/assets/src/components/ui/card.tsx +0 -92
  240. package/examples/resume/assets/src/components/ui/checkbox.tsx +0 -30
  241. package/examples/resume/assets/src/components/ui/hover-card.tsx +0 -44
  242. package/examples/resume/assets/src/components/ui/separator.tsx +0 -26
  243. package/examples/resume/assets/src/lib/resume-ai-hook.ts +0 -21
  244. package/examples/resume/assets/src/lib/resume-tools.ts +0 -165
  245. package/examples/resume/assets/src/lib/utils.ts +0 -6
  246. package/examples/resume/assets/src/routes/api.resume-chat.ts +0 -110
  247. package/examples/resume/assets/src/routes/index.tsx +0 -220
  248. package/examples/resume/assets/src/styles.css +0 -138
  249. package/examples/resume/info.json +0 -35
  250. package/examples/resume/package.json +0 -26
  251. package/examples/saas/README.md +0 -16
  252. package/examples/saas/assets/AGENTS.md.append +0 -9
  253. package/examples/saas/assets/src/components/Header.tsx +0 -17
  254. package/examples/saas/assets/src/routes/__root.tsx +0 -57
  255. package/examples/saas/assets/src/routes/faq.tsx +0 -94
  256. package/examples/saas/assets/src/routes/index.tsx +0 -197
  257. package/examples/saas/info.json +0 -23
  258. package/examples/saas/package.json +0 -1
  259. package/examples/survey/README.md +0 -20
  260. package/examples/survey/assets/AGENTS.md.append +0 -9
  261. package/examples/survey/assets/public/form-survey.html +0 -15
  262. package/examples/survey/assets/src/components/SurveyForm.tsx +0 -128
  263. package/examples/survey/assets/src/routes/index.tsx +0 -14
  264. package/examples/survey/info.json +0 -19
  265. package/examples/survey/package.json +0 -1
  266. package/project/base/AGENTS.md +0 -86
  267. package/project/base/_dot_claude/skills/content-collections/SKILL.md +0 -505
  268. package/project/base/_dot_claude/skills/netlify-blobs/SKILL.md +0 -410
  269. package/project/base/_dot_claude/skills/netlify-db/SKILL.md +0 -424
  270. package/project/base/_dot_claude/skills/netlify-debugging/SKILL.md +0 -419
  271. package/project/base/_dot_claude/skills/netlify-forms/SKILL.md +0 -243
  272. package/project/base/_dot_claude/skills/netlify-functions/SKILL.md +0 -372
  273. package/project/base/_dot_claude/skills/tanstack-start-api-routes/SKILL.md +0 -421
  274. package/project/base/_dot_claude/skills/tanstack-start-loaders/SKILL.md +0 -426
  275. package/project/base/_dot_claude/skills/tanstack-start-project-setup/SKILL.md +0 -493
  276. package/project/base/_dot_claude/skills/tanstack-start-routes/SKILL.md +0 -430
  277. package/project/base/_dot_claude/skills/tanstack-start-server-functions/SKILL.md +0 -445
  278. package/project/base/_dot_claude/skills/tanstack-start-typesafe-routing/SKILL.md +0 -494
  279. package/project/base/_dot_gitignore +0 -8
  280. package/project/base/netlify.toml +0 -7
  281. package/project/base/package.json +0 -33
  282. package/project/base/public/favicon.ico +0 -0
  283. package/project/base/public/tanstack-circle-logo.png +0 -0
  284. package/project/base/public/tanstack-word-logo-white.svg +0 -1
  285. package/project/base/src/components/Header.tsx +0 -17
  286. package/project/base/src/components/HeaderNav.tsx.ejs +0 -179
  287. package/project/base/src/router.tsx +0 -15
  288. package/project/base/src/routes/__root.tsx +0 -57
  289. package/project/base/src/routes/index.tsx +0 -48
  290. package/project/base/src/styles.css +0 -15
  291. package/project/base/tsconfig.json +0 -28
  292. package/project/base/vite.config.ts.ejs +0 -25
  293. package/project/info.json +0 -17
  294. package/project/packages.json +0 -22
  295. package/scripts/check-outdated-packages.js +0 -421
  296. package/src/agents-md.ts +0 -139
@@ -1,410 +0,0 @@
1
- ---
2
- name: netlify-blobs
3
- description: Store and retrieve unstructured data using Netlify Blobs key-value storage. Use for file uploads, caching, user-generated content, session storage, or any binary/JSON data persistence on Netlify.
4
- license: Apache-2.0
5
- metadata:
6
- author: netlify
7
- version: "1.0"
8
- ---
9
-
10
- # Netlify Blobs
11
-
12
- Netlify Blobs is a built-in key-value store for unstructured data. It's ideal for storing files, JSON, or any binary data without setting up external storage.
13
-
14
- ## When to Use
15
-
16
- - Storing user uploads (images, files)
17
- - Caching API responses
18
- - Session or state storage
19
- - Persisting background function results
20
- - Deploy-specific data storage
21
-
22
- ## Installation
23
-
24
- ```bash
25
- npm install @netlify/blobs
26
- ```
27
-
28
- ## Basic Usage
29
-
30
- ### Writing Data
31
-
32
- ```typescript
33
- import { getStore } from "@netlify/blobs";
34
-
35
- // Get a store reference
36
- const store = getStore("my-store");
37
-
38
- // Store a string
39
- await store.set("greeting", "Hello, World!");
40
-
41
- // Store JSON (automatically serialized)
42
- await store.setJSON("user", {
43
- id: 1,
44
- name: "Alice",
45
- email: "alice@example.com"
46
- });
47
-
48
- // Store binary data (Buffer, ArrayBuffer, Blob, ReadableStream)
49
- await store.set("image", imageBuffer);
50
- ```
51
-
52
- ### Reading Data
53
-
54
- ```typescript
55
- import { getStore } from "@netlify/blobs";
56
-
57
- const store = getStore("my-store");
58
-
59
- // Get as string
60
- const greeting = await store.get("greeting");
61
- // → "Hello, World!"
62
-
63
- // Get as JSON (automatically parsed)
64
- const user = await store.get("user", { type: "json" });
65
- // → { id: 1, name: "Alice", email: "alice@example.com" }
66
-
67
- // Get as ArrayBuffer
68
- const imageData = await store.get("image", { type: "arrayBuffer" });
69
-
70
- // Get as Blob
71
- const blob = await store.get("image", { type: "blob" });
72
-
73
- // Get as Stream
74
- const stream = await store.get("image", { type: "stream" });
75
-
76
- // Returns null if key doesn't exist
77
- const missing = await store.get("nonexistent");
78
- // → null
79
- ```
80
-
81
- ### Deleting Data
82
-
83
- ```typescript
84
- const store = getStore("my-store");
85
-
86
- // Delete a single key
87
- await store.delete("old-data");
88
-
89
- // Check if key exists before deleting
90
- const exists = await store.get("maybe-exists");
91
- if (exists) {
92
- await store.delete("maybe-exists");
93
- }
94
- ```
95
-
96
- ### Listing Keys
97
-
98
- ```typescript
99
- const store = getStore("my-store");
100
-
101
- // List all keys
102
- const { blobs } = await store.list();
103
- for (const blob of blobs) {
104
- console.log(blob.key);
105
- }
106
-
107
- // List with prefix filter
108
- const { blobs: userBlobs } = await store.list({ prefix: "users/" });
109
-
110
- // Paginate through results
111
- let cursor: string | undefined;
112
- do {
113
- const result = await store.list({ cursor });
114
- for (const blob of result.blobs) {
115
- console.log(blob.key);
116
- }
117
- cursor = result.cursor;
118
- } while (cursor);
119
- ```
120
-
121
- ## Storing Files with Metadata
122
-
123
- ```typescript
124
- const store = getStore("uploads");
125
-
126
- // Store file with metadata
127
- await store.set("profile-123.jpg", imageBuffer, {
128
- metadata: {
129
- contentType: "image/jpeg",
130
- uploadedBy: "user-123",
131
- originalName: "my-photo.jpg",
132
- },
133
- });
134
-
135
- // Retrieve with metadata
136
- const { data, metadata } = await store.getWithMetadata("profile-123.jpg", {
137
- type: "arrayBuffer",
138
- });
139
-
140
- console.log(metadata.contentType); // "image/jpeg"
141
- ```
142
-
143
- ## Consistency Modes
144
-
145
- ### Eventual Consistency (Default)
146
-
147
- Data is cached at the edge for fast reads. Updates propagate within 60 seconds.
148
-
149
- ```typescript
150
- const store = getStore("my-store");
151
- // Uses eventual consistency by default
152
- ```
153
-
154
- ### Strong Consistency
155
-
156
- For when you need immediate read-after-write consistency:
157
-
158
- ```typescript
159
- // Store-level strong consistency
160
- const store = getStore({
161
- name: "my-store",
162
- consistency: "strong",
163
- });
164
-
165
- // Or per-operation
166
- const store = getStore("my-store");
167
- const data = await store.get("key", { consistency: "strong" });
168
- ```
169
-
170
- Use strong consistency when:
171
- - Reading immediately after writing
172
- - Handling transactions or counters
173
- - Data correctness is critical
174
-
175
- ## Deploy-Scoped Stores
176
-
177
- Data tied to a specific deploy (cleaned up with deploy):
178
-
179
- ```typescript
180
- import { getDeployStore } from "@netlify/blobs";
181
-
182
- // This store is scoped to the current deploy
183
- const store = getDeployStore("build-cache");
184
-
185
- // Data is automatically cleaned up when deploy is deleted
186
- await store.set("compiled-assets", compiledData);
187
- ```
188
-
189
- ## Using in Functions
190
-
191
- ### Serverless Function Example
192
-
193
- ```typescript
194
- // netlify/functions/upload.ts
195
- import { getStore } from "@netlify/blobs";
196
- import type { Context } from "@netlify/functions";
197
-
198
- export default async (request: Request, context: Context) => {
199
- const store = getStore("uploads");
200
-
201
- if (request.method === "POST") {
202
- const formData = await request.formData();
203
- const file = formData.get("file") as File;
204
-
205
- if (!file) {
206
- return new Response("No file provided", { status: 400 });
207
- }
208
-
209
- const key = `${Date.now()}-${file.name}`;
210
- const buffer = await file.arrayBuffer();
211
-
212
- await store.set(key, buffer, {
213
- metadata: {
214
- contentType: file.type,
215
- originalName: file.name,
216
- size: file.size.toString(),
217
- },
218
- });
219
-
220
- return Response.json({ key, message: "Upload successful" });
221
- }
222
-
223
- if (request.method === "GET") {
224
- const url = new URL(request.url);
225
- const key = url.searchParams.get("key");
226
-
227
- if (!key) {
228
- return new Response("Key required", { status: 400 });
229
- }
230
-
231
- const { data, metadata } = await store.getWithMetadata(key, {
232
- type: "arrayBuffer",
233
- });
234
-
235
- if (!data) {
236
- return new Response("Not found", { status: 404 });
237
- }
238
-
239
- return new Response(data, {
240
- headers: {
241
- "Content-Type": metadata?.contentType || "application/octet-stream",
242
- },
243
- });
244
- }
245
-
246
- return new Response("Method not allowed", { status: 405 });
247
- };
248
- ```
249
-
250
- ### Edge Function Example
251
-
252
- ```typescript
253
- // netlify/edge-functions/cache.ts
254
- import { getStore } from "@netlify/blobs";
255
-
256
- export default async (request: Request) => {
257
- const url = new URL(request.url);
258
- const cacheKey = `page-cache:${url.pathname}`;
259
-
260
- const store = getStore("page-cache");
261
-
262
- // Try to get from cache
263
- const cached = await store.get(cacheKey, { type: "json" });
264
-
265
- if (cached && cached.expires > Date.now()) {
266
- return new Response(cached.html, {
267
- headers: {
268
- "Content-Type": "text/html",
269
- "X-Cache": "HIT",
270
- },
271
- });
272
- }
273
-
274
- // Generate fresh content
275
- const html = await generatePage(url.pathname);
276
-
277
- // Cache for 5 minutes
278
- await store.setJSON(cacheKey, {
279
- html,
280
- expires: Date.now() + 5 * 60 * 1000,
281
- });
282
-
283
- return new Response(html, {
284
- headers: {
285
- "Content-Type": "text/html",
286
- "X-Cache": "MISS",
287
- },
288
- });
289
- };
290
- ```
291
-
292
- ## File-Based Uploads (Build Time)
293
-
294
- Place files in `.netlify/blobs/deploy` during build:
295
-
296
- ```
297
- project/
298
- ├── .netlify/
299
- │ └── blobs/
300
- │ └── deploy/
301
- │ ├── assets/
302
- │ │ └── logo.png
303
- │ ├── $assets/logo.png.json # Metadata file
304
- │ └── config.json
305
- ```
306
-
307
- Metadata file example (`$assets/logo.png.json`):
308
- ```json
309
- {
310
- "contentType": "image/png",
311
- "uploadedAt": "2024-01-15T10:00:00Z"
312
- }
313
- ```
314
-
315
- ## Common Patterns
316
-
317
- ### Rate Limiting
318
-
319
- ```typescript
320
- const store = getStore({ name: "rate-limits", consistency: "strong" });
321
-
322
- async function checkRateLimit(ip: string, limit: number, windowMs: number) {
323
- const key = `rate:${ip}`;
324
- const now = Date.now();
325
-
326
- const data = await store.get(key, { type: "json" }) as {
327
- count: number;
328
- resetAt: number;
329
- } | null;
330
-
331
- if (!data || data.resetAt < now) {
332
- await store.setJSON(key, { count: 1, resetAt: now + windowMs });
333
- return { allowed: true, remaining: limit - 1 };
334
- }
335
-
336
- if (data.count >= limit) {
337
- return { allowed: false, remaining: 0, resetAt: data.resetAt };
338
- }
339
-
340
- await store.setJSON(key, { count: data.count + 1, resetAt: data.resetAt });
341
- return { allowed: true, remaining: limit - data.count - 1 };
342
- }
343
- ```
344
-
345
- ### Session Storage
346
-
347
- ```typescript
348
- const sessions = getStore({ name: "sessions", consistency: "strong" });
349
-
350
- async function createSession(userId: string) {
351
- const sessionId = crypto.randomUUID();
352
- await sessions.setJSON(`session:${sessionId}`, {
353
- userId,
354
- createdAt: Date.now(),
355
- expiresAt: Date.now() + 24 * 60 * 60 * 1000, // 24 hours
356
- });
357
- return sessionId;
358
- }
359
-
360
- async function getSession(sessionId: string) {
361
- const session = await sessions.get(`session:${sessionId}`, { type: "json" });
362
- if (!session || session.expiresAt < Date.now()) {
363
- return null;
364
- }
365
- return session;
366
- }
367
- ```
368
-
369
- ### Background Job Results
370
-
371
- ```typescript
372
- // Background function writes result
373
- // netlify/functions/process-background.ts
374
- import { getStore } from "@netlify/blobs";
375
-
376
- export default async (request: Request) => {
377
- const { jobId, data } = await request.json();
378
- const store = getStore("job-results");
379
-
380
- await store.setJSON(`job:${jobId}:status`, { status: "processing" });
381
-
382
- // Do long-running work...
383
- const result = await processData(data);
384
-
385
- await store.setJSON(`job:${jobId}:status`, {
386
- status: "complete",
387
- result,
388
- });
389
- };
390
-
391
- // Regular function checks status
392
- // netlify/functions/job-status.ts
393
- export default async (request: Request) => {
394
- const url = new URL(request.url);
395
- const jobId = url.searchParams.get("id");
396
-
397
- const store = getStore("job-results");
398
- const status = await store.get(`job:${jobId}:status`, { type: "json" });
399
-
400
- return Response.json(status || { status: "not-found" });
401
- };
402
- ```
403
-
404
- ## Limits
405
-
406
- - **Key length**: 600 bytes max
407
- - **Value size**: 5GB max per blob
408
- - **Metadata**: 64KB max per blob
409
- - **Store names**: Must be alphanumeric with hyphens
410
- - **Consistency**: Eventual by default (60s propagation), strong available