create-nextdevtpl 2.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 (425) hide show
  1. package/README.md +29 -0
  2. package/catalog.json +385 -0
  3. package/dist/catalog.d.ts +3 -0
  4. package/dist/catalog.js +112 -0
  5. package/dist/catalog.js.map +1 -0
  6. package/dist/cli.d.ts +3 -0
  7. package/dist/cli.js +115 -0
  8. package/dist/cli.js.map +1 -0
  9. package/dist/filesystem.d.ts +10 -0
  10. package/dist/filesystem.js +86 -0
  11. package/dist/filesystem.js.map +1 -0
  12. package/dist/generate.d.ts +5 -0
  13. package/dist/generate.js +758 -0
  14. package/dist/generate.js.map +1 -0
  15. package/dist/index.d.ts +4 -0
  16. package/dist/index.js +4 -0
  17. package/dist/index.js.map +1 -0
  18. package/dist/selection.d.ts +2 -0
  19. package/dist/selection.js +111 -0
  20. package/dist/selection.js.map +1 -0
  21. package/dist/service-templates.d.ts +4 -0
  22. package/dist/service-templates.js +180 -0
  23. package/dist/service-templates.js.map +1 -0
  24. package/dist/types.d.ts +65 -0
  25. package/dist/types.js +2 -0
  26. package/dist/types.js.map +1 -0
  27. package/package.json +54 -0
  28. package/template/.dockerignore +14 -0
  29. package/template/.env.example +282 -0
  30. package/template/.gitattributes +17 -0
  31. package/template/.node-version +1 -0
  32. package/template/Dockerfile +45 -0
  33. package/template/LICENSE +21 -0
  34. package/template/README.md +267 -0
  35. package/template/biome.json +68 -0
  36. package/template/cloudflare/templates/logger.ts +162 -0
  37. package/template/cloudflare/templates/mail/server.ts +6 -0
  38. package/template/cloudflare/templates/mail/templates.ts +87 -0
  39. package/template/cloudflare/templates/mail/utils.ts +69 -0
  40. package/template/cloudflare/worker.mjs +25 -0
  41. package/template/components.json +22 -0
  42. package/template/compose.yaml +74 -0
  43. package/template/deploy/check-health.mjs +25 -0
  44. package/template/deploy/docker/README.md +14 -0
  45. package/template/deploy/server/README.md +24 -0
  46. package/template/deploy/server/build.sh +24 -0
  47. package/template/deploy/server/nextdevtpl.service +19 -0
  48. package/template/deploy/server/start.sh +19 -0
  49. package/template/deploy/vercel/README.md +16 -0
  50. package/template/deploy/verify-production-health.mjs +86 -0
  51. package/template/drizzle.config.ts +47 -0
  52. package/template/gitignore +62 -0
  53. package/template/manifest.json +35 -0
  54. package/template/messages/en.json +766 -0
  55. package/template/messages/zh.json +762 -0
  56. package/template/next.config.mjs +28 -0
  57. package/template/open-next.config.ts +3 -0
  58. package/template/package.json +125 -0
  59. package/template/pnpm-lock.yaml +15901 -0
  60. package/template/postcss.config.mjs +7 -0
  61. package/template/public/file.svg +1 -0
  62. package/template/public/globe.svg +1 -0
  63. package/template/public/logo.svg +6 -0
  64. package/template/public/next.svg +1 -0
  65. package/template/public/vercel.svg +1 -0
  66. package/template/public/window.svg +1 -0
  67. package/template/sentry.client.config.ts +10 -0
  68. package/template/sentry.edge.config.ts +10 -0
  69. package/template/sentry.server.config.ts +10 -0
  70. package/template/source.config.ts +59 -0
  71. package/template/src/adapters/ai/anthropic.ts +113 -0
  72. package/template/src/adapters/ai/index.ts +6 -0
  73. package/template/src/adapters/ai/openai-compatible.ts +88 -0
  74. package/template/src/adapters/ai/workers-ai.ts +63 -0
  75. package/template/src/adapters/index.ts +10 -0
  76. package/template/src/adapters/jobs/cloudflare-workflows.ts +46 -0
  77. package/template/src/adapters/jobs/index.ts +5 -0
  78. package/template/src/adapters/jobs/inngest/adapter.ts +29 -0
  79. package/template/src/adapters/jobs/inngest/client.ts +8 -0
  80. package/template/src/adapters/jobs/inngest/functions.ts +17 -0
  81. package/template/src/adapters/jobs/inngest/handler.ts +6 -0
  82. package/template/src/adapters/jobs/inngest/index.ts +4 -0
  83. package/template/src/adapters/mail/cloudflare-email.ts +50 -0
  84. package/template/src/adapters/mail/disabled.ts +17 -0
  85. package/template/src/adapters/mail/index.ts +7 -0
  86. package/template/src/adapters/mail/resend.ts +76 -0
  87. package/template/src/adapters/mail/smtp.ts +82 -0
  88. package/template/src/adapters/payment/creem.ts +269 -0
  89. package/template/src/adapters/payment/index.ts +2 -0
  90. package/template/src/adapters/payment/stripe.ts +302 -0
  91. package/template/src/adapters/rate-limit/cloudflare.ts +42 -0
  92. package/template/src/adapters/rate-limit/index.ts +6 -0
  93. package/template/src/adapters/rate-limit/noop.ts +27 -0
  94. package/template/src/adapters/rate-limit/upstash.ts +113 -0
  95. package/template/src/adapters/registry.ts +228 -0
  96. package/template/src/adapters/storage/index.ts +3 -0
  97. package/template/src/adapters/storage/r2-binding.ts +105 -0
  98. package/template/src/adapters/storage/s3-compatible.ts +149 -0
  99. package/template/src/app/[locale]/(admin)/admin/layout.tsx +36 -0
  100. package/template/src/app/[locale]/(admin)/admin/page.tsx +345 -0
  101. package/template/src/app/[locale]/(admin)/admin/tickets/[id]/page.tsx +261 -0
  102. package/template/src/app/[locale]/(admin)/admin/tickets/page.tsx +236 -0
  103. package/template/src/app/[locale]/(admin)/admin/users/page.tsx +557 -0
  104. package/template/src/app/[locale]/(auth)/forgot-password/page.tsx +9 -0
  105. package/template/src/app/[locale]/(auth)/layout.tsx +28 -0
  106. package/template/src/app/[locale]/(auth)/sign-in/page.tsx +9 -0
  107. package/template/src/app/[locale]/(auth)/sign-up/page.tsx +9 -0
  108. package/template/src/app/[locale]/(dashboard)/dashboard/credits/buy/buy-credits-view.tsx +170 -0
  109. package/template/src/app/[locale]/(dashboard)/dashboard/credits/buy/page.tsx +12 -0
  110. package/template/src/app/[locale]/(dashboard)/dashboard/credits/page.tsx +5 -0
  111. package/template/src/app/[locale]/(dashboard)/dashboard/loading.tsx +41 -0
  112. package/template/src/app/[locale]/(dashboard)/dashboard/page.tsx +299 -0
  113. package/template/src/app/[locale]/(dashboard)/dashboard/settings/page.tsx +38 -0
  114. package/template/src/app/[locale]/(dashboard)/dashboard/support/[id]/page.tsx +220 -0
  115. package/template/src/app/[locale]/(dashboard)/dashboard/support/new/page.tsx +183 -0
  116. package/template/src/app/[locale]/(dashboard)/dashboard/support/page.tsx +145 -0
  117. package/template/src/app/[locale]/(dashboard)/layout.tsx +35 -0
  118. package/template/src/app/[locale]/(marketing)/blog/[slug]/page.tsx +178 -0
  119. package/template/src/app/[locale]/(marketing)/blog/blog-post-card.tsx +95 -0
  120. package/template/src/app/[locale]/(marketing)/blog/page.tsx +127 -0
  121. package/template/src/app/[locale]/(marketing)/demo/plan-badges/page.tsx +36 -0
  122. package/template/src/app/[locale]/(marketing)/layout.tsx +15 -0
  123. package/template/src/app/[locale]/(marketing)/legal/[slug]/page.tsx +130 -0
  124. package/template/src/app/[locale]/(marketing)/page.tsx +94 -0
  125. package/template/src/app/[locale]/(marketing)/pseo/[slug]/page.tsx +68 -0
  126. package/template/src/app/[locale]/(marketing)/pseo/page.tsx +137 -0
  127. package/template/src/app/[locale]/docs/[[...slug]]/page.tsx +63 -0
  128. package/template/src/app/[locale]/docs/layout.tsx +39 -0
  129. package/template/src/app/[locale]/layout.tsx +80 -0
  130. package/template/src/app/[locale]/opengraph-image.tsx +20 -0
  131. package/template/src/app/[locale]/twitter-image.tsx +20 -0
  132. package/template/src/app/api/auth/[...all]/route.ts +20 -0
  133. package/template/src/app/api/health/route.ts +45 -0
  134. package/template/src/app/api/inngest/route.ts +12 -0
  135. package/template/src/app/api/jobs/credits/expire/route.ts +102 -0
  136. package/template/src/app/api/search/route.ts +17 -0
  137. package/template/src/app/api/upload/presigned/route.ts +98 -0
  138. package/template/src/app/api/webhooks/payment/route.ts +363 -0
  139. package/template/src/app/globals.css +406 -0
  140. package/template/src/app/icon.svg +6 -0
  141. package/template/src/app/image-proxy/[...path]/route.ts +137 -0
  142. package/template/src/app/layout.tsx +73 -0
  143. package/template/src/app/robots.ts +29 -0
  144. package/template/src/app/sitemap.ts +68 -0
  145. package/template/src/components/motion/reveal.tsx +49 -0
  146. package/template/src/components/seo/json-ld.tsx +87 -0
  147. package/template/src/components/seo/og-image-template.tsx +118 -0
  148. package/template/src/components/ui/accordion.tsx +66 -0
  149. package/template/src/components/ui/alert-dialog.tsx +195 -0
  150. package/template/src/components/ui/avatar.tsx +53 -0
  151. package/template/src/components/ui/badge.tsx +46 -0
  152. package/template/src/components/ui/button.tsx +65 -0
  153. package/template/src/components/ui/card.tsx +92 -0
  154. package/template/src/components/ui/checkbox.tsx +32 -0
  155. package/template/src/components/ui/collapsible.tsx +33 -0
  156. package/template/src/components/ui/dialog.tsx +124 -0
  157. package/template/src/components/ui/dropdown-menu.tsx +257 -0
  158. package/template/src/components/ui/form.tsx +233 -0
  159. package/template/src/components/ui/index.ts +2 -0
  160. package/template/src/components/ui/input.tsx +21 -0
  161. package/template/src/components/ui/label.tsx +24 -0
  162. package/template/src/components/ui/navigation-menu.tsx +167 -0
  163. package/template/src/components/ui/pagination.tsx +125 -0
  164. package/template/src/components/ui/popover.tsx +48 -0
  165. package/template/src/components/ui/progress.tsx +27 -0
  166. package/template/src/components/ui/radio-group.tsx +45 -0
  167. package/template/src/components/ui/select.tsx +190 -0
  168. package/template/src/components/ui/separator.tsx +28 -0
  169. package/template/src/components/ui/sheet.tsx +140 -0
  170. package/template/src/components/ui/switch.tsx +31 -0
  171. package/template/src/components/ui/tabs.tsx +75 -0
  172. package/template/src/components/ui/textarea.tsx +18 -0
  173. package/template/src/config/index.ts +31 -0
  174. package/template/src/config/nav.ts +238 -0
  175. package/template/src/config/payment.ts +282 -0
  176. package/template/src/config/site.ts +53 -0
  177. package/template/src/config/subscription-plan.ts +164 -0
  178. package/template/src/content/blog/en/hello-world.mdx +48 -0
  179. package/template/src/content/blog/zh/hello-world.mdx +48 -0
  180. package/template/src/content/blog/zh/nextdevtpl-linuxdo-intro.mdx +98 -0
  181. package/template/src/content/docs/en/admin-and-support.mdx +65 -0
  182. package/template/src/content/docs/en/authentication.mdx +71 -0
  183. package/template/src/content/docs/en/background-jobs.mdx +53 -0
  184. package/template/src/content/docs/en/configuration.mdx +71 -0
  185. package/template/src/content/docs/en/credits.mdx +69 -0
  186. package/template/src/content/docs/en/customization.mdx +95 -0
  187. package/template/src/content/docs/en/database.mdx +66 -0
  188. package/template/src/content/docs/en/deployment.mdx +62 -0
  189. package/template/src/content/docs/en/email.mdx +48 -0
  190. package/template/src/content/docs/en/index.mdx +48 -0
  191. package/template/src/content/docs/en/internationalization.mdx +78 -0
  192. package/template/src/content/docs/en/meta.json +22 -0
  193. package/template/src/content/docs/en/observability.mdx +69 -0
  194. package/template/src/content/docs/en/payments.mdx +64 -0
  195. package/template/src/content/docs/en/project-structure.mdx +82 -0
  196. package/template/src/content/docs/en/quick-start.mdx +113 -0
  197. package/template/src/content/docs/en/storage.mdx +51 -0
  198. package/template/src/content/docs/zh/admin-and-support.mdx +65 -0
  199. package/template/src/content/docs/zh/authentication.mdx +71 -0
  200. package/template/src/content/docs/zh/background-jobs.mdx +53 -0
  201. package/template/src/content/docs/zh/configuration.mdx +70 -0
  202. package/template/src/content/docs/zh/credits.mdx +69 -0
  203. package/template/src/content/docs/zh/customization.mdx +95 -0
  204. package/template/src/content/docs/zh/database.mdx +66 -0
  205. package/template/src/content/docs/zh/deployment.mdx +61 -0
  206. package/template/src/content/docs/zh/email.mdx +47 -0
  207. package/template/src/content/docs/zh/index.mdx +48 -0
  208. package/template/src/content/docs/zh/internationalization.mdx +78 -0
  209. package/template/src/content/docs/zh/meta.json +22 -0
  210. package/template/src/content/docs/zh/observability.mdx +68 -0
  211. package/template/src/content/docs/zh/payments.mdx +64 -0
  212. package/template/src/content/docs/zh/project-structure.mdx +82 -0
  213. package/template/src/content/docs/zh/quick-start.mdx +110 -0
  214. package/template/src/content/docs/zh/storage.mdx +51 -0
  215. package/template/src/content/legal/en/cookie-policy.mdx +78 -0
  216. package/template/src/content/legal/en/privacy.mdx +131 -0
  217. package/template/src/content/legal/en/terms.mdx +93 -0
  218. package/template/src/content/legal/zh/cookie-policy.mdx +78 -0
  219. package/template/src/content/legal/zh/privacy.mdx +131 -0
  220. package/template/src/content/legal/zh/terms.mdx +92 -0
  221. package/template/src/core/modules/contract.ts +225 -0
  222. package/template/src/core/modules/index.ts +1 -0
  223. package/template/src/core/services/ai.ts +31 -0
  224. package/template/src/core/services/common.ts +112 -0
  225. package/template/src/core/services/index.ts +8 -0
  226. package/template/src/core/services/jobs.ts +24 -0
  227. package/template/src/core/services/mail.ts +35 -0
  228. package/template/src/core/services/manifest.ts +51 -0
  229. package/template/src/core/services/payment.ts +92 -0
  230. package/template/src/core/services/rate-limit.ts +47 -0
  231. package/template/src/core/services/storage.ts +42 -0
  232. package/template/src/db/index.ts +71 -0
  233. package/template/src/db/schema/auth.ts +67 -0
  234. package/template/src/db/schema/credits.ts +94 -0
  235. package/template/src/db/schema/index.ts +5 -0
  236. package/template/src/db/schema/mail.ts +14 -0
  237. package/template/src/db/schema/subscription.ts +21 -0
  238. package/template/src/db/schema/support.ts +56 -0
  239. package/template/src/db/schema.ts +2 -0
  240. package/template/src/features/admin/components/admin-sidebar.tsx +213 -0
  241. package/template/src/features/admin/components/index.ts +2 -0
  242. package/template/src/features/admin/index.ts +3 -0
  243. package/template/src/features/admin/manifest.ts +41 -0
  244. package/template/src/features/analytics/components/analytics.tsx +62 -0
  245. package/template/src/features/analytics/components/index.ts +2 -0
  246. package/template/src/features/analytics/index.ts +3 -0
  247. package/template/src/features/analytics/manifest.ts +13 -0
  248. package/template/src/features/auth/components/auth-brand-panel.tsx +82 -0
  249. package/template/src/features/auth/components/auth-error-alert.tsx +28 -0
  250. package/template/src/features/auth/components/auth-footer.tsx +52 -0
  251. package/template/src/features/auth/components/auth-logo.tsx +30 -0
  252. package/template/src/features/auth/components/forgot-password-form.tsx +146 -0
  253. package/template/src/features/auth/components/index.ts +9 -0
  254. package/template/src/features/auth/components/sign-in-form.tsx +241 -0
  255. package/template/src/features/auth/components/sign-up-form.tsx +307 -0
  256. package/template/src/features/auth/index.ts +3 -0
  257. package/template/src/features/auth/manifest.ts +30 -0
  258. package/template/src/features/blog/components/blog-post-item.tsx +72 -0
  259. package/template/src/features/blog/components/index.ts +1 -0
  260. package/template/src/features/blog/data/mock-posts.ts +62 -0
  261. package/template/src/features/blog/index.ts +3 -0
  262. package/template/src/features/blog/manifest.ts +22 -0
  263. package/template/src/features/credits/account.ts +76 -0
  264. package/template/src/features/credits/actions.ts +427 -0
  265. package/template/src/features/credits/components/credit-balance-badge.tsx +54 -0
  266. package/template/src/features/credits/components/credit-usage-section.tsx +160 -0
  267. package/template/src/features/credits/components/index.ts +7 -0
  268. package/template/src/features/credits/components/transaction-history.tsx +346 -0
  269. package/template/src/features/credits/config.ts +70 -0
  270. package/template/src/features/credits/consume.ts +140 -0
  271. package/template/src/features/credits/core.ts +23 -0
  272. package/template/src/features/credits/errors.ts +26 -0
  273. package/template/src/features/credits/expire.ts +112 -0
  274. package/template/src/features/credits/grant.ts +168 -0
  275. package/template/src/features/credits/index.ts +6 -0
  276. package/template/src/features/credits/manifest.ts +36 -0
  277. package/template/src/features/credits/query.ts +65 -0
  278. package/template/src/features/credits/types.ts +48 -0
  279. package/template/src/features/dashboard/components/index.ts +3 -0
  280. package/template/src/features/dashboard/components/main-wrapper.tsx +100 -0
  281. package/template/src/features/dashboard/components/sidebar.tsx +341 -0
  282. package/template/src/features/dashboard/context/index.ts +2 -0
  283. package/template/src/features/dashboard/context/sidebar-context.tsx +72 -0
  284. package/template/src/features/dashboard/index.ts +4 -0
  285. package/template/src/features/dashboard/manifest.ts +28 -0
  286. package/template/src/features/mail/actions.ts +175 -0
  287. package/template/src/features/mail/index.ts +5 -0
  288. package/template/src/features/mail/manifest.ts +13 -0
  289. package/template/src/features/mail/server.ts +8 -0
  290. package/template/src/features/mail/templates/index.ts +11 -0
  291. package/template/src/features/mail/templates/primary-action-email.tsx +224 -0
  292. package/template/src/features/mail/templates/welcome-email.tsx +121 -0
  293. package/template/src/features/mail/utils.ts +190 -0
  294. package/template/src/features/marketing/components/animated-price.tsx +51 -0
  295. package/template/src/features/marketing/components/cookie-consent.tsx +255 -0
  296. package/template/src/features/marketing/components/cta-section.tsx +50 -0
  297. package/template/src/features/marketing/components/faq-section.tsx +59 -0
  298. package/template/src/features/marketing/components/feature-grid.tsx +80 -0
  299. package/template/src/features/marketing/components/footer.tsx +128 -0
  300. package/template/src/features/marketing/components/header.tsx +255 -0
  301. package/template/src/features/marketing/components/hero-section.tsx +323 -0
  302. package/template/src/features/marketing/components/how-it-works.tsx +115 -0
  303. package/template/src/features/marketing/components/index.ts +14 -0
  304. package/template/src/features/marketing/components/nav-menu.tsx +236 -0
  305. package/template/src/features/marketing/components/pricing-section.tsx +446 -0
  306. package/template/src/features/marketing/components/testimonials.tsx +59 -0
  307. package/template/src/features/marketing/components/use-cases-section.tsx +78 -0
  308. package/template/src/features/marketing/index.ts +3 -0
  309. package/template/src/features/marketing/manifest.ts +35 -0
  310. package/template/src/features/payment/actions.ts +182 -0
  311. package/template/src/features/payment/index.ts +4 -0
  312. package/template/src/features/payment/manifest.ts +18 -0
  313. package/template/src/features/payment/server.ts +7 -0
  314. package/template/src/features/payment/types.ts +202 -0
  315. package/template/src/features/pseo/components/index.ts +6 -0
  316. package/template/src/features/pseo/components/pseo-cta.tsx +51 -0
  317. package/template/src/features/pseo/components/pseo-faq.tsx +40 -0
  318. package/template/src/features/pseo/components/pseo-feature-grid.tsx +74 -0
  319. package/template/src/features/pseo/components/pseo-hero.tsx +106 -0
  320. package/template/src/features/pseo/components/pseo-related.tsx +62 -0
  321. package/template/src/features/pseo/components/pseo-use-cases.tsx +50 -0
  322. package/template/src/features/pseo/data/pseo-pages.json +534 -0
  323. package/template/src/features/pseo/index.ts +4 -0
  324. package/template/src/features/pseo/lib/pseo-data.ts +140 -0
  325. package/template/src/features/pseo/manifest.ts +22 -0
  326. package/template/src/features/settings/actions/delete-account.ts +50 -0
  327. package/template/src/features/settings/actions/index.ts +3 -0
  328. package/template/src/features/settings/actions/update-profile.ts +49 -0
  329. package/template/src/features/settings/components/billing-section.tsx +283 -0
  330. package/template/src/features/settings/components/index.ts +3 -0
  331. package/template/src/features/settings/components/profile-form.tsx +154 -0
  332. package/template/src/features/settings/components/security-section.tsx +158 -0
  333. package/template/src/features/settings/components/settings-profile-view.tsx +506 -0
  334. package/template/src/features/settings/index.ts +5 -0
  335. package/template/src/features/settings/manifest.ts +28 -0
  336. package/template/src/features/settings/schemas/index.ts +2 -0
  337. package/template/src/features/settings/schemas/update-profile.ts +21 -0
  338. package/template/src/features/shared/components/cookie-settings-dialog.tsx +282 -0
  339. package/template/src/features/shared/components/index.ts +5 -0
  340. package/template/src/features/shared/components/language-switcher.tsx +82 -0
  341. package/template/src/features/shared/components/max-width-wrapper.tsx +49 -0
  342. package/template/src/features/shared/components/mode-toggle.tsx +119 -0
  343. package/template/src/features/shared/icons/google-icon.tsx +39 -0
  344. package/template/src/features/shared/icons/index.ts +7 -0
  345. package/template/src/features/shared/index.ts +5 -0
  346. package/template/src/features/shared/manifest.ts +13 -0
  347. package/template/src/features/shared/providers.tsx +59 -0
  348. package/template/src/features/storage/actions.ts +107 -0
  349. package/template/src/features/storage/index.ts +6 -0
  350. package/template/src/features/storage/manifest.ts +22 -0
  351. package/template/src/features/storage/types.ts +143 -0
  352. package/template/src/features/storage/utils.ts +85 -0
  353. package/template/src/features/storage/validation.ts +151 -0
  354. package/template/src/features/subscription/actions/get-user-plan.ts +26 -0
  355. package/template/src/features/subscription/actions/index.ts +5 -0
  356. package/template/src/features/subscription/components/current-plan-badge.tsx +18 -0
  357. package/template/src/features/subscription/components/index.ts +6 -0
  358. package/template/src/features/subscription/components/plan-badge.tsx +98 -0
  359. package/template/src/features/subscription/index.ts +25 -0
  360. package/template/src/features/subscription/manifest.ts +13 -0
  361. package/template/src/features/subscription/services/user-plan.ts +181 -0
  362. package/template/src/features/support/actions/admin-ticket.ts +172 -0
  363. package/template/src/features/support/actions/admin-users.ts +295 -0
  364. package/template/src/features/support/actions/index.ts +22 -0
  365. package/template/src/features/support/actions/ticket.ts +12 -0
  366. package/template/src/features/support/actions/user-ticket.ts +153 -0
  367. package/template/src/features/support/components/admin-ticket-reply-form.tsx +109 -0
  368. package/template/src/features/support/components/admin-ticket-status-select.tsx +119 -0
  369. package/template/src/features/support/components/index.ts +6 -0
  370. package/template/src/features/support/components/ticket-message-form.tsx +99 -0
  371. package/template/src/features/support/components/user-role-select.tsx +107 -0
  372. package/template/src/features/support/index.ts +5 -0
  373. package/template/src/features/support/manifest.ts +53 -0
  374. package/template/src/features/support/schemas/index.ts +15 -0
  375. package/template/src/features/support/schemas/ticket.ts +81 -0
  376. package/template/src/hooks/index.ts +2 -0
  377. package/template/src/i18n/index.ts +12 -0
  378. package/template/src/i18n/request.ts +24 -0
  379. package/template/src/i18n/routing.ts +23 -0
  380. package/template/src/instrumentation.ts +19 -0
  381. package/template/src/lib/ai/index.ts +16 -0
  382. package/template/src/lib/api-logger.ts +24 -0
  383. package/template/src/lib/auth/admin.ts +54 -0
  384. package/template/src/lib/auth/api.ts +17 -0
  385. package/template/src/lib/auth/client.ts +227 -0
  386. package/template/src/lib/auth/edge.ts +84 -0
  387. package/template/src/lib/auth/errors.ts +99 -0
  388. package/template/src/lib/auth/index.ts +159 -0
  389. package/template/src/lib/auth/server.ts +46 -0
  390. package/template/src/lib/auth/session-context.tsx +96 -0
  391. package/template/src/lib/cloudflare/bindings.ts +31 -0
  392. package/template/src/lib/cookie-consent.ts +33 -0
  393. package/template/src/lib/file-utils.ts +45 -0
  394. package/template/src/lib/fingerprint/index.ts +73 -0
  395. package/template/src/lib/health/checks.ts +6 -0
  396. package/template/src/lib/health/core.ts +86 -0
  397. package/template/src/lib/health/index.ts +11 -0
  398. package/template/src/lib/health/types.ts +13 -0
  399. package/template/src/lib/index.ts +2 -0
  400. package/template/src/lib/logger/index.ts +258 -0
  401. package/template/src/lib/monitoring/index.ts +384 -0
  402. package/template/src/lib/rate-limit/anonymous.ts +85 -0
  403. package/template/src/lib/rate-limit/index.ts +111 -0
  404. package/template/src/lib/safe-action.ts +127 -0
  405. package/template/src/lib/seo/json-ld.ts +188 -0
  406. package/template/src/lib/source.ts +122 -0
  407. package/template/src/lib/utils.ts +6 -0
  408. package/template/src/middleware.ts +162 -0
  409. package/template/src/modules/index.ts +10 -0
  410. package/template/src/modules/presets.ts +8 -0
  411. package/template/src/modules/registry.ts +34 -0
  412. package/template/src/modules/selection.ts +45 -0
  413. package/template/src/services/ai.ts +49 -0
  414. package/template/src/services/index.ts +6 -0
  415. package/template/src/services/jobs.ts +3 -0
  416. package/template/src/services/mail.ts +7 -0
  417. package/template/src/services/payment.ts +3 -0
  418. package/template/src/services/rate-limit.ts +12 -0
  419. package/template/src/services/storage.ts +3 -0
  420. package/template/src/types/fumadocs-source.d.ts +14 -0
  421. package/template/src/types/index.ts +2 -0
  422. package/template/tsconfig.json +53 -0
  423. package/template/vercel.json +11 -0
  424. package/template/vitest.config.ts +51 -0
  425. package/template/wrangler.jsonc +30 -0
@@ -0,0 +1,225 @@
1
+ export const MODULE_IDS = [
2
+ "admin",
3
+ "analytics",
4
+ "auth",
5
+ "blog",
6
+ "credits",
7
+ "dashboard",
8
+ "mail",
9
+ "marketing",
10
+ "payment",
11
+ "pseo",
12
+ "settings",
13
+ "shared",
14
+ "storage",
15
+ "subscription",
16
+ "support",
17
+ ] as const;
18
+
19
+ export type ModuleId = (typeof MODULE_IDS)[number];
20
+ export type ModuleKind = "core" | "optional";
21
+ export type ModuleNavigationArea = "dashboard" | "admin";
22
+ export type ModuleNavigationIcon =
23
+ | "coins"
24
+ | "dashboard"
25
+ | "headset"
26
+ | "settings"
27
+ | "ticket"
28
+ | "users";
29
+ export type ModuleSchemaGroup =
30
+ | "auth"
31
+ | "credits"
32
+ | "mail"
33
+ | "subscription"
34
+ | "support";
35
+
36
+ export interface ModuleNavigationItem {
37
+ area: ModuleNavigationArea;
38
+ group: string;
39
+ groupTranslationKey: string;
40
+ translationKey: string;
41
+ href: string;
42
+ icon: ModuleNavigationIcon;
43
+ order: number;
44
+ }
45
+
46
+ export interface ModuleRoute {
47
+ path: string;
48
+ source: string;
49
+ }
50
+
51
+ export interface ModuleManifest {
52
+ id: ModuleId;
53
+ name: string;
54
+ description: string;
55
+ kind: ModuleKind;
56
+ dependencies: readonly ModuleId[];
57
+ routes: readonly ModuleRoute[];
58
+ navigation: readonly ModuleNavigationItem[];
59
+ translations: readonly string[];
60
+ schema: readonly ModuleSchemaGroup[];
61
+ }
62
+
63
+ export interface ModuleRegistry {
64
+ manifests: readonly ModuleManifest[];
65
+ byId: ReadonlyMap<ModuleId, ModuleManifest>;
66
+ }
67
+
68
+ export function defineModule<const T extends ModuleManifest>(manifest: T): T {
69
+ return manifest;
70
+ }
71
+
72
+ export function validateModuleManifests(
73
+ manifests: readonly ModuleManifest[]
74
+ ): string[] {
75
+ const errors: string[] = [];
76
+ const byId = new Map<ModuleId, ModuleManifest>();
77
+ const routeOwners = new Map<string, ModuleId>();
78
+ const routeSourceOwners = new Map<string, ModuleId>();
79
+ const navigationOwners = new Map<string, ModuleId>();
80
+ const schemaOwners = new Map<ModuleSchemaGroup, ModuleId>();
81
+ const translationOwners = new Map<string, ModuleId>();
82
+
83
+ for (const manifest of manifests) {
84
+ if (byId.has(manifest.id)) {
85
+ errors.push(`模块 ID 重复:${manifest.id}`);
86
+ continue;
87
+ }
88
+ byId.set(manifest.id, manifest);
89
+
90
+ for (const route of manifest.routes) {
91
+ const owner = routeOwners.get(route.path);
92
+ if (owner) {
93
+ errors.push(`路由 ${route.path} 同时属于 ${owner} 和 ${manifest.id}`);
94
+ } else {
95
+ routeOwners.set(route.path, manifest.id);
96
+ }
97
+
98
+ const sourceOwner = routeSourceOwners.get(route.source);
99
+ if (sourceOwner) {
100
+ errors.push(
101
+ `路由文件 ${route.source} 同时属于 ${sourceOwner} 和 ${manifest.id}`
102
+ );
103
+ } else {
104
+ routeSourceOwners.set(route.source, manifest.id);
105
+ }
106
+ }
107
+
108
+ for (const item of manifest.navigation) {
109
+ const key = `${item.area}:${item.href}`;
110
+ const owner = navigationOwners.get(key);
111
+ if (owner) {
112
+ errors.push(`菜单 ${key} 同时属于 ${owner} 和 ${manifest.id}`);
113
+ } else {
114
+ navigationOwners.set(key, manifest.id);
115
+ }
116
+ }
117
+
118
+ for (const group of manifest.schema) {
119
+ const owner = schemaOwners.get(group);
120
+ if (owner) {
121
+ errors.push(`Schema ${group} 同时属于 ${owner} 和 ${manifest.id}`);
122
+ } else {
123
+ schemaOwners.set(group, manifest.id);
124
+ }
125
+ }
126
+
127
+ for (const namespace of manifest.translations) {
128
+ const owner = translationOwners.get(namespace);
129
+ if (owner) {
130
+ errors.push(
131
+ `翻译命名空间 ${namespace} 同时属于 ${owner} 和 ${manifest.id}`
132
+ );
133
+ } else {
134
+ translationOwners.set(namespace, manifest.id);
135
+ }
136
+ }
137
+ }
138
+
139
+ for (const manifest of manifests) {
140
+ for (const dependency of manifest.dependencies) {
141
+ if (dependency === manifest.id) {
142
+ errors.push(`模块 ${manifest.id} 不能依赖自身`);
143
+ } else if (!byId.has(dependency)) {
144
+ errors.push(`模块 ${manifest.id} 依赖未注册模块 ${dependency}`);
145
+ } else if (
146
+ manifest.kind === "core" &&
147
+ byId.get(dependency)?.kind === "optional"
148
+ ) {
149
+ errors.push(`核心模块 ${manifest.id} 不能依赖可选模块 ${dependency}`);
150
+ }
151
+ }
152
+ }
153
+
154
+ const visiting = new Set<ModuleId>();
155
+ const visited = new Set<ModuleId>();
156
+
157
+ const visit = (id: ModuleId, path: readonly ModuleId[]) => {
158
+ if (visiting.has(id)) {
159
+ errors.push(`模块依赖存在循环:${[...path, id].join(" -> ")}`);
160
+ return;
161
+ }
162
+ if (visited.has(id)) {
163
+ return;
164
+ }
165
+
166
+ const manifest = byId.get(id);
167
+ if (!manifest) {
168
+ return;
169
+ }
170
+
171
+ visiting.add(id);
172
+ for (const dependency of manifest.dependencies) {
173
+ visit(dependency, [...path, id]);
174
+ }
175
+ visiting.delete(id);
176
+ visited.add(id);
177
+ };
178
+
179
+ for (const id of byId.keys()) {
180
+ visit(id, []);
181
+ }
182
+
183
+ return errors;
184
+ }
185
+
186
+ export function createModuleRegistry(
187
+ manifests: readonly ModuleManifest[]
188
+ ): ModuleRegistry {
189
+ const errors = validateModuleManifests(manifests);
190
+ if (errors.length > 0) {
191
+ throw new Error(`模块注册失败:\n${errors.join("\n")}`);
192
+ }
193
+
194
+ return {
195
+ manifests,
196
+ byId: new Map(manifests.map((manifest) => [manifest.id, manifest])),
197
+ };
198
+ }
199
+
200
+ export function resolveModuleClosure(
201
+ registry: ModuleRegistry,
202
+ selectedIds: readonly ModuleId[]
203
+ ): ModuleId[] {
204
+ const resolved = new Set<ModuleId>();
205
+
206
+ const add = (id: ModuleId) => {
207
+ if (resolved.has(id)) {
208
+ return;
209
+ }
210
+ const manifest = registry.byId.get(id);
211
+ if (!manifest) {
212
+ throw new Error(`模块未注册:${id}`);
213
+ }
214
+ for (const dependency of manifest.dependencies) {
215
+ add(dependency);
216
+ }
217
+ resolved.add(id);
218
+ };
219
+
220
+ for (const id of selectedIds) {
221
+ add(id);
222
+ }
223
+
224
+ return [...resolved];
225
+ }
@@ -0,0 +1 @@
1
+ export * from "./contract";
@@ -0,0 +1,31 @@
1
+ import type { AdapterDescriptor } from "./common";
2
+
3
+ export type AIProviderName = "anthropic" | "openai-compatible" | "workers-ai";
4
+
5
+ export interface AICapabilities {
6
+ jsonOutput: boolean;
7
+ streaming: boolean;
8
+ toolCalling: boolean;
9
+ }
10
+
11
+ export interface AIMessage {
12
+ content: string;
13
+ role: "assistant" | "system" | "user";
14
+ }
15
+
16
+ export interface AICompletionInput {
17
+ jsonMode?: boolean;
18
+ maxTokens?: number;
19
+ messages: readonly AIMessage[];
20
+ temperature?: number;
21
+ }
22
+
23
+ export interface AICompletionResult {
24
+ content: string;
25
+ model: string;
26
+ }
27
+
28
+ export interface AIAdapter
29
+ extends AdapterDescriptor<AIProviderName, AICapabilities> {
30
+ complete(input: AICompletionInput): Promise<AICompletionResult>;
31
+ }
@@ -0,0 +1,112 @@
1
+ export type AdapterErrorCode =
2
+ | "authentication"
3
+ | "configuration"
4
+ | "invalid_request"
5
+ | "not_found"
6
+ | "rate_limited"
7
+ | "remote_failure"
8
+ | "signature_invalid"
9
+ | "unsupported";
10
+
11
+ export interface AdapterDescriptor<
12
+ Provider extends string,
13
+ Capabilities extends object,
14
+ > {
15
+ provider: Provider;
16
+ capabilities: Readonly<Capabilities>;
17
+ }
18
+
19
+ export class AdapterError extends Error {
20
+ readonly code: AdapterErrorCode;
21
+ readonly provider: string;
22
+ readonly retryable: boolean;
23
+
24
+ constructor(params: {
25
+ code: AdapterErrorCode;
26
+ message: string;
27
+ provider: string;
28
+ retryable?: boolean;
29
+ cause?: unknown;
30
+ }) {
31
+ super(
32
+ params.message,
33
+ params.cause === undefined ? undefined : { cause: params.cause }
34
+ );
35
+ this.name = "AdapterError";
36
+ this.code = params.code;
37
+ this.provider = params.provider;
38
+ this.retryable = params.retryable ?? false;
39
+ }
40
+ }
41
+
42
+ export function sanitizeAdapterMessage(
43
+ message: string,
44
+ secrets: readonly (string | undefined)[]
45
+ ): string {
46
+ return secrets.reduce<string>(
47
+ (sanitized, secret) =>
48
+ secret ? sanitized.replaceAll(secret, "[REDACTED]") : sanitized,
49
+ message
50
+ );
51
+ }
52
+
53
+ export function toAdapterError(params: {
54
+ code?: AdapterErrorCode;
55
+ error: unknown;
56
+ fallbackMessage: string;
57
+ provider: string;
58
+ retryable?: boolean;
59
+ secrets?: readonly (string | undefined)[];
60
+ }): AdapterError {
61
+ if (params.error instanceof AdapterError) {
62
+ return params.error;
63
+ }
64
+
65
+ const rawMessage =
66
+ params.error instanceof Error
67
+ ? params.error.message
68
+ : params.fallbackMessage;
69
+ const message = sanitizeAdapterMessage(rawMessage, params.secrets ?? []);
70
+
71
+ const errorParams = {
72
+ code: params.code ?? "remote_failure",
73
+ message: message || params.fallbackMessage,
74
+ provider: params.provider,
75
+ cause: params.error,
76
+ };
77
+
78
+ return new AdapterError(
79
+ params.retryable === undefined
80
+ ? errorParams
81
+ : { ...errorParams, retryable: params.retryable }
82
+ );
83
+ }
84
+
85
+ export async function executeAdapterOperation<T>(params: {
86
+ operation: () => Promise<T>;
87
+ provider: string;
88
+ fallbackMessage: string;
89
+ retryable?: boolean;
90
+ secrets?: readonly (string | undefined)[];
91
+ }): Promise<T> {
92
+ try {
93
+ return await params.operation();
94
+ } catch (error) {
95
+ throw toAdapterError({
96
+ error,
97
+ fallbackMessage: params.fallbackMessage,
98
+ provider: params.provider,
99
+ ...(params.retryable === undefined
100
+ ? {}
101
+ : { retryable: params.retryable }),
102
+ ...(params.secrets ? { secrets: params.secrets } : {}),
103
+ });
104
+ }
105
+ }
106
+
107
+ export type JsonPrimitive = boolean | number | string | null;
108
+ export type JsonValue =
109
+ | JsonPrimitive
110
+ | readonly JsonValue[]
111
+ | { readonly [key: string]: JsonValue };
112
+ export type JsonObject = { readonly [key: string]: JsonValue };
@@ -0,0 +1,8 @@
1
+ export * from "./ai";
2
+ export * from "./common";
3
+ export * from "./jobs";
4
+ export * from "./mail";
5
+ export * from "./manifest";
6
+ export * from "./payment";
7
+ export * from "./rate-limit";
8
+ export * from "./storage";
@@ -0,0 +1,24 @@
1
+ import type { AdapterDescriptor, JsonObject } from "./common";
2
+
3
+ export type JobProvider = "cloudflare-workflows" | "inngest";
4
+
5
+ export interface JobCapabilities {
6
+ durableSteps: boolean;
7
+ events: boolean;
8
+ scheduling: boolean;
9
+ }
10
+
11
+ export interface DispatchJobInput {
12
+ id?: string;
13
+ name: string;
14
+ payload: JsonObject;
15
+ }
16
+
17
+ export interface DispatchJobResult {
18
+ id: string;
19
+ }
20
+
21
+ export interface JobAdapter
22
+ extends AdapterDescriptor<JobProvider, JobCapabilities> {
23
+ dispatch(input: DispatchJobInput): Promise<DispatchJobResult>;
24
+ }
@@ -0,0 +1,35 @@
1
+ import type { AdapterDescriptor } from "./common";
2
+
3
+ export type MailProvider = "cloudflare-email" | "disabled" | "resend" | "smtp";
4
+
5
+ export interface MailCapabilities {
6
+ attachments: boolean;
7
+ batch: boolean;
8
+ transactional: boolean;
9
+ }
10
+
11
+ export interface MailAddress {
12
+ email: string;
13
+ name?: string;
14
+ }
15
+
16
+ export interface MailMessage {
17
+ bcc?: readonly string[];
18
+ cc?: readonly string[];
19
+ from: MailAddress;
20
+ html: string;
21
+ replyTo?: readonly string[];
22
+ subject: string;
23
+ text: string;
24
+ to: readonly string[];
25
+ }
26
+
27
+ export interface MailSendResult {
28
+ id?: string;
29
+ queued: boolean;
30
+ }
31
+
32
+ export interface MailAdapter
33
+ extends AdapterDescriptor<MailProvider, MailCapabilities> {
34
+ send(message: MailMessage): Promise<MailSendResult>;
35
+ }
@@ -0,0 +1,51 @@
1
+ export const SERVICE_KINDS = [
2
+ "ai",
3
+ "jobs",
4
+ "mail",
5
+ "payment",
6
+ "rate-limit",
7
+ "storage",
8
+ ] as const;
9
+
10
+ export type ServiceKind = (typeof SERVICE_KINDS)[number];
11
+ export type AdapterRuntime = "node" | "universal" | "worker";
12
+
13
+ export interface ServiceAdapterManifest {
14
+ bindings: readonly string[];
15
+ env: readonly string[];
16
+ id: string;
17
+ packages: readonly string[];
18
+ runtime: AdapterRuntime;
19
+ service: ServiceKind;
20
+ source: string;
21
+ }
22
+
23
+ export function defineServiceAdapter<const T extends ServiceAdapterManifest>(
24
+ manifest: T
25
+ ): T {
26
+ return manifest;
27
+ }
28
+
29
+ export function validateServiceAdapterManifests(
30
+ manifests: readonly ServiceAdapterManifest[]
31
+ ): string[] {
32
+ const errors: string[] = [];
33
+ const ids = new Set<string>();
34
+ const sources = new Set<string>();
35
+
36
+ for (const manifest of manifests) {
37
+ if (ids.has(manifest.id)) {
38
+ errors.push(`适配器 ID 重复:${manifest.id}`);
39
+ }
40
+ if (sources.has(manifest.source)) {
41
+ errors.push(`适配器源码重复登记:${manifest.source}`);
42
+ }
43
+ if (!manifest.id.startsWith(`${manifest.service}:`)) {
44
+ errors.push(`适配器 ${manifest.id} 必须使用 ${manifest.service}: 前缀`);
45
+ }
46
+ ids.add(manifest.id);
47
+ sources.add(manifest.source);
48
+ }
49
+
50
+ return errors;
51
+ }
@@ -0,0 +1,92 @@
1
+ import type { AdapterDescriptor, JsonObject } from "./common";
2
+
3
+ export type PaymentProvider = "creem" | "stripe";
4
+ export type PaymentStatus =
5
+ | "active"
6
+ | "canceled"
7
+ | "incomplete"
8
+ | "past_due"
9
+ | "paused"
10
+ | "trialing"
11
+ | "unpaid";
12
+ export type PaymentWebhookType =
13
+ | "checkout.completed"
14
+ | "subscription.active"
15
+ | "subscription.canceled"
16
+ | "subscription.expired"
17
+ | "subscription.paid"
18
+ | "subscription.past_due"
19
+ | "subscription.paused"
20
+ | "subscription.renewed";
21
+
22
+ export interface PaymentCapabilities {
23
+ customerPortal: boolean;
24
+ oneTimePayments: boolean;
25
+ subscriptions: boolean;
26
+ webhooks: boolean;
27
+ }
28
+
29
+ export interface PaymentCustomer {
30
+ id: string;
31
+ email?: string;
32
+ name?: string;
33
+ }
34
+
35
+ export interface PaymentSubscription {
36
+ id: string;
37
+ status: PaymentStatus;
38
+ productId: string;
39
+ customerId: string;
40
+ currentPeriodStart: Date | null;
41
+ currentPeriodEnd: Date | null;
42
+ cancelAtPeriodEnd: boolean;
43
+ metadata: Readonly<Record<string, string>>;
44
+ }
45
+
46
+ export interface PaymentCheckout {
47
+ id: string;
48
+ mode: "one-time" | "subscription";
49
+ productId: string;
50
+ customer: PaymentCustomer;
51
+ status: string;
52
+ subscription: PaymentSubscription | null;
53
+ metadata: Readonly<Record<string, string>>;
54
+ }
55
+
56
+ export interface CreateCheckoutInput {
57
+ cancelUrl?: string;
58
+ mode?: "one-time" | "subscription";
59
+ productId: string;
60
+ successUrl: string;
61
+ requestId?: string;
62
+ metadata?: Readonly<Record<string, string>>;
63
+ }
64
+
65
+ export interface CreateCheckoutResult {
66
+ id: string;
67
+ status: string;
68
+ url: string;
69
+ }
70
+
71
+ export interface VerifyPaymentWebhookInput {
72
+ payload: string;
73
+ signature: string;
74
+ secret?: string;
75
+ }
76
+
77
+ export interface PaymentWebhookEvent {
78
+ id: string;
79
+ type: PaymentWebhookType;
80
+ createdAt: Date;
81
+ checkout: PaymentCheckout | null;
82
+ subscription: PaymentSubscription | null;
83
+ rawMetadata: JsonObject;
84
+ }
85
+
86
+ export interface PaymentAdapter
87
+ extends AdapterDescriptor<PaymentProvider, PaymentCapabilities> {
88
+ createCheckout(input: CreateCheckoutInput): Promise<CreateCheckoutResult>;
89
+ getSubscription(id: string): Promise<PaymentSubscription>;
90
+ cancelSubscription(id: string): Promise<PaymentSubscription>;
91
+ verifyWebhook(input: VerifyPaymentWebhookInput): Promise<PaymentWebhookEvent>;
92
+ }
@@ -0,0 +1,47 @@
1
+ import type { AdapterDescriptor } from "./common";
2
+
3
+ export type RateLimitProvider = "cloudflare-rate-limit" | "noop" | "upstash";
4
+
5
+ export interface RateLimitCapabilities {
6
+ dynamicRules: boolean;
7
+ preciseMetadata: boolean;
8
+ }
9
+
10
+ export interface RateLimitInput {
11
+ identifier: string;
12
+ limit: number;
13
+ namespace: string;
14
+ window: `${number}${"d" | "h" | "m" | "s"}`;
15
+ }
16
+
17
+ export interface RateLimitDecision {
18
+ limit: number | null;
19
+ remaining: number | null;
20
+ reset: number | null;
21
+ skipped: boolean;
22
+ success: boolean;
23
+ }
24
+
25
+ export interface RateLimitAdapter
26
+ extends AdapterDescriptor<RateLimitProvider, RateLimitCapabilities> {
27
+ limit(input: RateLimitInput): Promise<RateLimitDecision>;
28
+ }
29
+
30
+ export interface UsageQuotaInput {
31
+ identifier: string;
32
+ limit: number;
33
+ namespace: string;
34
+ windowSeconds: number;
35
+ }
36
+
37
+ export interface UsageQuotaDecision {
38
+ allowed: boolean;
39
+ remaining: number;
40
+ reset: number | null;
41
+ }
42
+
43
+ export interface UsageQuotaAdapter {
44
+ check(input: UsageQuotaInput): Promise<UsageQuotaDecision>;
45
+ increment(input: UsageQuotaInput): Promise<number>;
46
+ remaining(input: UsageQuotaInput): Promise<number>;
47
+ }
@@ -0,0 +1,42 @@
1
+ import type { AdapterDescriptor } from "./common";
2
+
3
+ export type StorageProviderName = "r2-binding" | "s3-compatible";
4
+
5
+ export interface StorageCapabilities {
6
+ directRead: boolean;
7
+ directWrite: boolean;
8
+ signedDownloadUrls: boolean;
9
+ signedUploadUrls: boolean;
10
+ }
11
+
12
+ export interface StorageObject {
13
+ body: Uint8Array;
14
+ contentType?: string;
15
+ etag?: string;
16
+ }
17
+
18
+ export interface StorageWriteInput {
19
+ body: ArrayBuffer | Blob | ReadableStream<Uint8Array> | Uint8Array | string;
20
+ bucket: string;
21
+ contentType?: string;
22
+ key: string;
23
+ }
24
+
25
+ export interface StorageUrlInput {
26
+ bucket: string;
27
+ expiresIn: number;
28
+ key: string;
29
+ }
30
+
31
+ export interface StorageUploadUrlInput extends StorageUrlInput {
32
+ contentType: string;
33
+ }
34
+
35
+ export interface StorageAdapter
36
+ extends AdapterDescriptor<StorageProviderName, StorageCapabilities> {
37
+ createDownloadUrl(input: StorageUrlInput): Promise<string>;
38
+ createUploadUrl(input: StorageUploadUrlInput): Promise<string>;
39
+ deleteObject(bucket: string, key: string): Promise<void>;
40
+ readObject(bucket: string, key: string): Promise<StorageObject | null>;
41
+ writeObject(input: StorageWriteInput): Promise<void>;
42
+ }