@oyolincc/create 0.4.0 → 0.5.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 (391) hide show
  1. package/package.json +2 -2
  2. package/templates/hono-fullstack/.codegraph/.gitignore +5 -0
  3. package/templates/hono-fullstack/README.md +22 -1
  4. package/templates/hono-fullstack/_gitignore +1 -0
  5. package/templates/hono-fullstack/apps/server/package.json +7 -2
  6. package/templates/hono-fullstack/apps/server/src/adapters/cloudflare/entry.ts +4 -30
  7. package/templates/hono-fullstack/apps/server/src/adapters/nodejs/bootstrap.ts +20 -12
  8. package/templates/hono-fullstack/apps/server/src/adapters/nodejs/entry.ts +7 -57
  9. package/templates/hono-fullstack/apps/server/src/app.ts +37 -21
  10. package/templates/hono-fullstack/apps/server/src/config.ts +1 -1
  11. package/templates/hono-fullstack/apps/server/src/di-container.ts +51 -0
  12. package/templates/hono-fullstack/apps/server/src/infra/logger/getter.ts +1 -1
  13. package/templates/hono-fullstack/apps/server/src/infra/logger/pino.ts +21 -34
  14. package/templates/hono-fullstack/apps/server/src/infra/work-runner/errors.ts +22 -0
  15. package/templates/hono-fullstack/apps/server/src/infra/work-runner/pass-through/runner.ts +28 -22
  16. package/templates/hono-fullstack/apps/server/src/infra/work-runner/piscina/runner.ts +11 -34
  17. package/templates/hono-fullstack/apps/server/src/infra/work-runner/{example → piscina}/worker-entry.ts +6 -2
  18. package/templates/hono-fullstack/apps/server/src/infra/work-runner/types.ts +2 -16
  19. package/templates/hono-fullstack/apps/server/src/infra/work-runner/utils/business-error.ts +10 -5
  20. package/templates/hono-fullstack/apps/server/src/infra/work-runner/utils/message.ts +5 -17
  21. package/templates/hono-fullstack/apps/server/src/middlewares/cors.ts +16 -22
  22. package/templates/hono-fullstack/apps/server/src/middlewares/logger.ts +34 -0
  23. package/templates/hono-fullstack/apps/server/src/modules/example/module.ts +8 -0
  24. package/templates/hono-fullstack/apps/server/src/modules/example/queries.ts +8 -0
  25. package/templates/hono-fullstack/apps/server/src/modules/example/routes.ts +74 -68
  26. package/templates/hono-fullstack/apps/server/src/modules/example/service.test.ts +53 -0
  27. package/templates/hono-fullstack/apps/server/src/modules/example/service.ts +54 -0
  28. package/templates/hono-fullstack/apps/server/src/modules/example/types.ts +12 -0
  29. package/templates/hono-fullstack/apps/server/src/modules/example/work-task/tasks.test.ts +9 -35
  30. package/templates/hono-fullstack/apps/server/src/modules/example/work-task/tasks.ts +15 -46
  31. package/templates/hono-fullstack/apps/server/src/shared/errors/defs/index.ts +6 -1
  32. package/templates/hono-fullstack/apps/server/src/shared/errors/handler.ts +1 -1
  33. package/templates/hono-fullstack/apps/server/src/shared/network/openapi/index.ts +1 -1
  34. package/templates/hono-fullstack/apps/server/src/shared/types/app.ts +51 -0
  35. package/templates/hono-fullstack/apps/server/src/shared/types/db.ts +126 -0
  36. package/templates/hono-fullstack/apps/server/src/shared/utils/data-access/check.ts +19 -0
  37. package/templates/hono-fullstack/apps/server/src/shared/utils/data-access/model.ts +47 -0
  38. package/templates/hono-fullstack/apps/server/src/shared/utils/data-access/params.ts +105 -0
  39. package/templates/hono-fullstack/apps/server/src/shared/utils/data-access/query.test.ts +139 -0
  40. package/templates/hono-fullstack/apps/server/src/shared/utils/data-access/query.ts +236 -0
  41. package/templates/hono-fullstack/apps/server/vite.config.ts +2 -2
  42. package/templates/hono-fullstack/apps/server/wrangler.jsonc +1 -1
  43. package/templates/hono-fullstack/apps/stack/frontend/assets/styles.css +6 -0
  44. package/templates/hono-fullstack/apps/stack/frontend/components/general/ErrorMessage.tsx +3 -1
  45. package/templates/hono-fullstack/apps/stack/frontend/components/layouts/AppLayout.tsx +2 -1
  46. package/templates/hono-fullstack/apps/stack/frontend/components/layouts/Header.tsx +14 -2
  47. package/templates/hono-fullstack/apps/stack/frontend/components/layouts/Page.tsx +2 -1
  48. package/templates/hono-fullstack/apps/stack/frontend/components/layouts/PageContainer.tsx +4 -1
  49. package/templates/hono-fullstack/apps/stack/frontend/modules/example/api.ts +16 -9
  50. package/templates/hono-fullstack/apps/stack/frontend/modules/example/components/ExampleForm.tsx +25 -12
  51. package/templates/hono-fullstack/apps/stack/frontend/modules/example/components/ExampleList.tsx +13 -4
  52. package/templates/hono-fullstack/apps/stack/frontend/modules/example/hooks/api-hooks.ts +3 -3
  53. package/templates/hono-fullstack/apps/stack/frontend/routes/__root.tsx +1 -1
  54. package/templates/hono-fullstack/apps/stack/frontend/routes/index.tsx +16 -4
  55. package/templates/hono-fullstack/apps/stack/package.json +9 -4
  56. package/templates/hono-fullstack/apps/stack/panda.config.ts +31 -0
  57. package/templates/hono-fullstack/apps/stack/tsconfig.json +1 -0
  58. package/templates/hono-fullstack/apps/stack/turbo.json +20 -0
  59. package/templates/hono-fullstack/apps/stack/vite.config.ts +48 -12
  60. package/templates/hono-fullstack/apps/stack/wrangler.jsonc +1 -1
  61. package/templates/hono-fullstack/envs/.env.example.sqlite-d1 +1 -0
  62. package/templates/hono-fullstack/libs/db/drizzle-kit/sqlite-d1.config.ts +29 -10
  63. package/templates/hono-fullstack/libs/db/package.json +1 -0
  64. package/templates/hono-fullstack/libs/db/src/dialects/sqlite/schema/columns.ts +1 -1
  65. package/templates/hono-fullstack/libs/db/src/dialects/sqlite/schema/modules/examples/examples.ts +6 -3
  66. package/templates/hono-fullstack/libs/db/src/dialects/sqlite/schema/vo.ts +7 -1
  67. package/templates/hono-fullstack/libs/definition/README.md +81 -0
  68. package/templates/hono-fullstack/libs/definition/package.json +3 -1
  69. package/templates/hono-fullstack/libs/definition/src/constants/index.ts +1 -0
  70. package/templates/hono-fullstack/libs/definition/src/constants/pagination.ts +3 -0
  71. package/templates/hono-fullstack/libs/definition/src/modules/common-contracts.ts +24 -0
  72. package/templates/hono-fullstack/libs/definition/src/modules/example/contracts.ts +34 -0
  73. package/templates/hono-fullstack/libs/definition/src/modules/example/index.ts +1 -16
  74. package/templates/hono-fullstack/libs/definition/src/modules/index.ts +2 -0
  75. package/templates/hono-fullstack/libs/definition/src/modules/vo-generated-types.ts +12 -0
  76. package/templates/hono-fullstack/libs/ui/.storybook/main.ts +32 -0
  77. package/templates/hono-fullstack/libs/ui/.storybook/panda.config.ts +22 -0
  78. package/templates/hono-fullstack/libs/ui/.storybook/preview.tsx +79 -0
  79. package/templates/hono-fullstack/libs/ui/.storybook/styles.css +1 -0
  80. package/templates/hono-fullstack/libs/ui/.storybook/vite.config.ts +37 -0
  81. package/templates/hono-fullstack/libs/ui/components.json +15 -0
  82. package/templates/hono-fullstack/libs/ui/package.json +114 -0
  83. package/templates/hono-fullstack/libs/ui/panda.config.ts +41 -0
  84. package/templates/hono-fullstack/libs/ui/src/Tooltip.tsx +47 -0
  85. package/templates/hono-fullstack/libs/ui/src/components/atoms/buttons/Button.tsx +80 -0
  86. package/templates/hono-fullstack/libs/ui/src/components/atoms/buttons/CloseButton.tsx +15 -0
  87. package/templates/hono-fullstack/libs/ui/src/components/atoms/buttons/IconButton.tsx +10 -0
  88. package/templates/hono-fullstack/libs/ui/src/components/atoms/collections/Combobox.tsx +91 -0
  89. package/templates/hono-fullstack/libs/ui/src/components/atoms/collections/Select.tsx +88 -0
  90. package/templates/hono-fullstack/libs/ui/src/components/atoms/collections/TreeView.tsx +142 -0
  91. package/templates/hono-fullstack/libs/ui/src/components/atoms/data-display/Avatar.tsx +64 -0
  92. package/templates/hono-fullstack/libs/ui/src/components/atoms/data-display/Badge.tsx +7 -0
  93. package/templates/hono-fullstack/libs/ui/src/components/atoms/data-display/Card.tsx +23 -0
  94. package/templates/hono-fullstack/libs/ui/src/components/atoms/data-display/Clipboard.tsx +77 -0
  95. package/templates/hono-fullstack/libs/ui/src/components/atoms/data-display/Icon.tsx +9 -0
  96. package/templates/hono-fullstack/libs/ui/src/components/atoms/data-display/Table.tsx +51 -0
  97. package/templates/hono-fullstack/libs/ui/src/components/atoms/data-display/Tag.tsx +23 -0
  98. package/templates/hono-fullstack/libs/ui/src/components/atoms/data-display/Timeline.tsx +33 -0
  99. package/templates/hono-fullstack/libs/ui/src/components/atoms/date-and-time/DatePicker.tsx +255 -0
  100. package/templates/hono-fullstack/libs/ui/src/components/atoms/disclosure/Accordion.tsx +48 -0
  101. package/templates/hono-fullstack/libs/ui/src/components/atoms/disclosure/Breadcrumb.tsx +50 -0
  102. package/templates/hono-fullstack/libs/ui/src/components/atoms/disclosure/Collapsible.tsx +34 -0
  103. package/templates/hono-fullstack/libs/ui/src/components/atoms/disclosure/Pagination.tsx +74 -0
  104. package/templates/hono-fullstack/libs/ui/src/components/atoms/disclosure/Steps.tsx +91 -0
  105. package/templates/hono-fullstack/libs/ui/src/components/atoms/disclosure/Tabs.tsx +37 -0
  106. package/templates/hono-fullstack/libs/ui/src/components/atoms/feedback/Alert.tsx +52 -0
  107. package/templates/hono-fullstack/libs/ui/src/components/atoms/feedback/Progress.tsx +41 -0
  108. package/templates/hono-fullstack/libs/ui/src/components/atoms/feedback/ProgressCircle.tsx +40 -0
  109. package/templates/hono-fullstack/libs/ui/src/components/atoms/feedback/Skeleton.tsx +37 -0
  110. package/templates/hono-fullstack/libs/ui/src/components/atoms/feedback/Spinner.tsx +7 -0
  111. package/templates/hono-fullstack/libs/ui/src/components/atoms/feedback/Toast.tsx +99 -0
  112. package/templates/hono-fullstack/libs/ui/src/components/atoms/forms/Checkbox.tsx +95 -0
  113. package/templates/hono-fullstack/libs/ui/src/components/atoms/forms/ColorPicker.tsx +148 -0
  114. package/templates/hono-fullstack/libs/ui/src/components/atoms/forms/Editable.tsx +44 -0
  115. package/templates/hono-fullstack/libs/ui/src/components/atoms/forms/Field.tsx +36 -0
  116. package/templates/hono-fullstack/libs/ui/src/components/atoms/forms/Fieldset.tsx +34 -0
  117. package/templates/hono-fullstack/libs/ui/src/components/atoms/forms/FileUpload.tsx +143 -0
  118. package/templates/hono-fullstack/libs/ui/src/components/atoms/forms/Input.tsx +7 -0
  119. package/templates/hono-fullstack/libs/ui/src/components/atoms/forms/InputAddon.tsx +7 -0
  120. package/templates/hono-fullstack/libs/ui/src/components/atoms/forms/InputGroup.tsx +61 -0
  121. package/templates/hono-fullstack/libs/ui/src/components/atoms/forms/NativeSelect.tsx +81 -0
  122. package/templates/hono-fullstack/libs/ui/src/components/atoms/forms/NumberInput.tsx +63 -0
  123. package/templates/hono-fullstack/libs/ui/src/components/atoms/forms/PasswordInput.tsx +76 -0
  124. package/templates/hono-fullstack/libs/ui/src/components/atoms/forms/PinInput.tsx +36 -0
  125. package/templates/hono-fullstack/libs/ui/src/components/atoms/forms/RadioCard.tsx +96 -0
  126. package/templates/hono-fullstack/libs/ui/src/components/atoms/forms/RadioGroup.tsx +61 -0
  127. package/templates/hono-fullstack/libs/ui/src/components/atoms/forms/RatingGroup.tsx +106 -0
  128. package/templates/hono-fullstack/libs/ui/src/components/atoms/forms/SegmentGroup.tsx +68 -0
  129. package/templates/hono-fullstack/libs/ui/src/components/atoms/forms/Slider.tsx +87 -0
  130. package/templates/hono-fullstack/libs/ui/src/components/atoms/forms/Switch.tsx +74 -0
  131. package/templates/hono-fullstack/libs/ui/src/components/atoms/forms/TagsInput.tsx +77 -0
  132. package/templates/hono-fullstack/libs/ui/src/components/atoms/forms/Textarea.tsx +7 -0
  133. package/templates/hono-fullstack/libs/ui/src/components/atoms/index.ts +98 -0
  134. package/templates/hono-fullstack/libs/ui/src/components/atoms/internal/AbsoluteCenter.tsx +7 -0
  135. package/templates/hono-fullstack/libs/ui/src/components/atoms/internal/Loader.tsx +69 -0
  136. package/templates/hono-fullstack/libs/ui/src/components/atoms/internal/Span.tsx +5 -0
  137. package/templates/hono-fullstack/libs/ui/src/components/atoms/internal/create-layout.ts +18 -0
  138. package/templates/hono-fullstack/libs/ui/src/components/atoms/layout/Float.tsx +7 -0
  139. package/templates/hono-fullstack/libs/ui/src/components/atoms/layout/Grid.tsx +17 -0
  140. package/templates/hono-fullstack/libs/ui/src/components/atoms/layout/Group.tsx +7 -0
  141. package/templates/hono-fullstack/libs/ui/src/components/atoms/layout/ScrollArea.tsx +46 -0
  142. package/templates/hono-fullstack/libs/ui/src/components/atoms/layout/Separator.tsx +26 -0
  143. package/templates/hono-fullstack/libs/ui/src/components/atoms/layout/Stack.tsx +54 -0
  144. package/templates/hono-fullstack/libs/ui/src/components/atoms/overlays/Dialog.tsx +69 -0
  145. package/templates/hono-fullstack/libs/ui/src/components/atoms/overlays/Drawer.tsx +49 -0
  146. package/templates/hono-fullstack/libs/ui/src/components/atoms/overlays/HoverCard.tsx +37 -0
  147. package/templates/hono-fullstack/libs/ui/src/components/atoms/overlays/Menu.tsx +74 -0
  148. package/templates/hono-fullstack/libs/ui/src/components/atoms/overlays/OverlayManager.test.ts +124 -0
  149. package/templates/hono-fullstack/libs/ui/src/components/atoms/overlays/OverlayManager.tsx +189 -0
  150. package/templates/hono-fullstack/libs/ui/src/components/atoms/overlays/Popover.tsx +57 -0
  151. package/templates/hono-fullstack/libs/ui/src/components/atoms/overlays/Tooltip.tsx +37 -0
  152. package/templates/hono-fullstack/libs/ui/src/components/atoms/typography/Heading.tsx +6 -0
  153. package/templates/hono-fullstack/libs/ui/src/components/atoms/typography/Link.tsx +7 -0
  154. package/templates/hono-fullstack/libs/ui/src/components/atoms/typography/LinkOverlay.tsx +21 -0
  155. package/templates/hono-fullstack/libs/ui/src/components/atoms/typography/Text.tsx +6 -0
  156. package/templates/hono-fullstack/libs/ui/src/components/atoms/utilities/VisuallyHidden.tsx +13 -0
  157. package/templates/hono-fullstack/libs/ui/src/hooks.ts +242 -0
  158. package/templates/hono-fullstack/libs/ui/src/index.ts +19 -0
  159. package/templates/hono-fullstack/libs/ui/src/theme/animation-styles.ts +50 -0
  160. package/templates/hono-fullstack/libs/ui/src/theme/breakpoints.ts +7 -0
  161. package/templates/hono-fullstack/libs/ui/src/theme/conditions.ts +12 -0
  162. package/templates/hono-fullstack/libs/ui/src/theme/containers.ts +16 -0
  163. package/templates/hono-fullstack/libs/ui/src/theme/global-css.ts +12 -0
  164. package/templates/hono-fullstack/libs/ui/src/theme/keyframes.ts +157 -0
  165. package/templates/hono-fullstack/libs/ui/src/theme/layer-styles.ts +11 -0
  166. package/templates/hono-fullstack/libs/ui/src/theme/patterns/grid.ts +51 -0
  167. package/templates/hono-fullstack/libs/ui/src/theme/patterns/index.ts +60 -0
  168. package/templates/hono-fullstack/libs/ui/src/theme/patterns/stack.ts +46 -0
  169. package/templates/hono-fullstack/libs/ui/src/theme/recipes/absolute-center.ts +38 -0
  170. package/templates/hono-fullstack/libs/ui/src/theme/recipes/accordion.ts +68 -0
  171. package/templates/hono-fullstack/libs/ui/src/theme/recipes/alert.ts +83 -0
  172. package/templates/hono-fullstack/libs/ui/src/theme/recipes/avatar.ts +155 -0
  173. package/templates/hono-fullstack/libs/ui/src/theme/recipes/badge.ts +67 -0
  174. package/templates/hono-fullstack/libs/ui/src/theme/recipes/breadcrumb.ts +69 -0
  175. package/templates/hono-fullstack/libs/ui/src/theme/recipes/button.ts +149 -0
  176. package/templates/hono-fullstack/libs/ui/src/theme/recipes/card.ts +36 -0
  177. package/templates/hono-fullstack/libs/ui/src/theme/recipes/checkbox.ts +120 -0
  178. package/templates/hono-fullstack/libs/ui/src/theme/recipes/clipboard.ts +22 -0
  179. package/templates/hono-fullstack/libs/ui/src/theme/recipes/collapsible.ts +26 -0
  180. package/templates/hono-fullstack/libs/ui/src/theme/recipes/color-picker.ts +299 -0
  181. package/templates/hono-fullstack/libs/ui/src/theme/recipes/combobox.ts +141 -0
  182. package/templates/hono-fullstack/libs/ui/src/theme/recipes/date-picker.ts +302 -0
  183. package/templates/hono-fullstack/libs/ui/src/theme/recipes/dialog.ts +191 -0
  184. package/templates/hono-fullstack/libs/ui/src/theme/recipes/drawer.ts +170 -0
  185. package/templates/hono-fullstack/libs/ui/src/theme/recipes/editable.ts +64 -0
  186. package/templates/hono-fullstack/libs/ui/src/theme/recipes/field.ts +41 -0
  187. package/templates/hono-fullstack/libs/ui/src/theme/recipes/fieldset.ts +47 -0
  188. package/templates/hono-fullstack/libs/ui/src/theme/recipes/file-upload.ts +86 -0
  189. package/templates/hono-fullstack/libs/ui/src/theme/recipes/group.ts +78 -0
  190. package/templates/hono-fullstack/libs/ui/src/theme/recipes/heading.ts +24 -0
  191. package/templates/hono-fullstack/libs/ui/src/theme/recipes/hover-card.ts +47 -0
  192. package/templates/hono-fullstack/libs/ui/src/theme/recipes/icon.ts +25 -0
  193. package/templates/hono-fullstack/libs/ui/src/theme/recipes/index.ts +120 -0
  194. package/templates/hono-fullstack/libs/ui/src/theme/recipes/input-addon.ts +51 -0
  195. package/templates/hono-fullstack/libs/ui/src/theme/recipes/input-group.ts +77 -0
  196. package/templates/hono-fullstack/libs/ui/src/theme/recipes/input.ts +99 -0
  197. package/templates/hono-fullstack/libs/ui/src/theme/recipes/link.ts +36 -0
  198. package/templates/hono-fullstack/libs/ui/src/theme/recipes/menu.ts +114 -0
  199. package/templates/hono-fullstack/libs/ui/src/theme/recipes/native-select.ts +92 -0
  200. package/templates/hono-fullstack/libs/ui/src/theme/recipes/number-input.ts +91 -0
  201. package/templates/hono-fullstack/libs/ui/src/theme/recipes/pagination.ts +9 -0
  202. package/templates/hono-fullstack/libs/ui/src/theme/recipes/pin-input.ts +33 -0
  203. package/templates/hono-fullstack/libs/ui/src/theme/recipes/popover.ts +78 -0
  204. package/templates/hono-fullstack/libs/ui/src/theme/recipes/progress-circle.ts +50 -0
  205. package/templates/hono-fullstack/libs/ui/src/theme/recipes/progress.ts +112 -0
  206. package/templates/hono-fullstack/libs/ui/src/theme/recipes/radio-card.ts +223 -0
  207. package/templates/hono-fullstack/libs/ui/src/theme/recipes/radio-group.ts +78 -0
  208. package/templates/hono-fullstack/libs/ui/src/theme/recipes/rating-group.ts +98 -0
  209. package/templates/hono-fullstack/libs/ui/src/theme/recipes/scroll-area.ts +119 -0
  210. package/templates/hono-fullstack/libs/ui/src/theme/recipes/segment-group.ts +162 -0
  211. package/templates/hono-fullstack/libs/ui/src/theme/recipes/select.ts +157 -0
  212. package/templates/hono-fullstack/libs/ui/src/theme/recipes/separator.ts +25 -0
  213. package/templates/hono-fullstack/libs/ui/src/theme/recipes/skeleton.ts +65 -0
  214. package/templates/hono-fullstack/libs/ui/src/theme/recipes/slider.ts +164 -0
  215. package/templates/hono-fullstack/libs/ui/src/theme/recipes/spinner.ts +34 -0
  216. package/templates/hono-fullstack/libs/ui/src/theme/recipes/steps.ts +162 -0
  217. package/templates/hono-fullstack/libs/ui/src/theme/recipes/switch.ts +150 -0
  218. package/templates/hono-fullstack/libs/ui/src/theme/recipes/table.ts +150 -0
  219. package/templates/hono-fullstack/libs/ui/src/theme/recipes/tabs.ts +128 -0
  220. package/templates/hono-fullstack/libs/ui/src/theme/recipes/tag.ts +114 -0
  221. package/templates/hono-fullstack/libs/ui/src/theme/recipes/tags-input.ts +201 -0
  222. package/templates/hono-fullstack/libs/ui/src/theme/recipes/text.ts +7 -0
  223. package/templates/hono-fullstack/libs/ui/src/theme/recipes/textarea.ts +90 -0
  224. package/templates/hono-fullstack/libs/ui/src/theme/recipes/timeline.ts +137 -0
  225. package/templates/hono-fullstack/libs/ui/src/theme/recipes/toast.ts +52 -0
  226. package/templates/hono-fullstack/libs/ui/src/theme/recipes/tooltip.ts +44 -0
  227. package/templates/hono-fullstack/libs/ui/src/theme/recipes/tree-view.ts +200 -0
  228. package/templates/hono-fullstack/libs/ui/src/theme/semanticTokens/colors/amber.ts +91 -0
  229. package/templates/hono-fullstack/libs/ui/src/theme/semanticTokens/colors/gray.ts +103 -0
  230. package/templates/hono-fullstack/libs/ui/src/theme/semanticTokens/colors/green.ts +88 -0
  231. package/templates/hono-fullstack/libs/ui/src/theme/semanticTokens/colors/index.ts +14 -0
  232. package/templates/hono-fullstack/libs/ui/src/theme/semanticTokens/colors/lime.ts +91 -0
  233. package/templates/hono-fullstack/libs/ui/src/theme/semanticTokens/colors/red.ts +88 -0
  234. package/templates/hono-fullstack/libs/ui/src/theme/semanticTokens/index.ts +8 -0
  235. package/templates/hono-fullstack/libs/ui/src/theme/semanticTokens/shadows.ts +46 -0
  236. package/templates/hono-fullstack/libs/ui/src/theme/text-styles.ts +82 -0
  237. package/templates/hono-fullstack/libs/ui/src/theme/tokens/animations.ts +8 -0
  238. package/templates/hono-fullstack/libs/ui/src/theme/tokens/aspect-ratio.ts +6 -0
  239. package/templates/hono-fullstack/libs/ui/src/theme/tokens/borders.ts +5 -0
  240. package/templates/hono-fullstack/libs/ui/src/theme/tokens/colors.ts +35 -0
  241. package/templates/hono-fullstack/libs/ui/src/theme/tokens/durations.ts +11 -0
  242. package/templates/hono-fullstack/libs/ui/src/theme/tokens/easings.ts +9 -0
  243. package/templates/hono-fullstack/libs/ui/src/theme/tokens/index.ts +42 -0
  244. package/templates/hono-fullstack/libs/ui/src/theme/tokens/radii.ts +13 -0
  245. package/templates/hono-fullstack/libs/ui/src/theme/tokens/sizes.ts +27 -0
  246. package/templates/hono-fullstack/libs/ui/src/theme/tokens/spacing.ts +41 -0
  247. package/templates/hono-fullstack/libs/ui/src/theme/tokens/typography.ts +86 -0
  248. package/templates/hono-fullstack/libs/ui/src/theme/tokens/z-index.ts +43 -0
  249. package/templates/hono-fullstack/libs/ui/stories/README.md +107 -0
  250. package/templates/hono-fullstack/libs/ui/stories/actions/Button.stories.tsx +260 -0
  251. package/templates/hono-fullstack/libs/ui/stories/actions/Clipboard.stories.tsx +87 -0
  252. package/templates/hono-fullstack/libs/ui/stories/actions/IconButton.stories.tsx +33 -0
  253. package/templates/hono-fullstack/libs/ui/stories/collections/Combobox.stories.tsx +334 -0
  254. package/templates/hono-fullstack/libs/ui/stories/collections/Select.stories.tsx +342 -0
  255. package/templates/hono-fullstack/libs/ui/stories/collections/TreeView.stories.tsx +252 -0
  256. package/templates/hono-fullstack/libs/ui/stories/color/ColorPicker.stories.tsx +217 -0
  257. package/templates/hono-fullstack/libs/ui/stories/date/DatePicker.stories.tsx +302 -0
  258. package/templates/hono-fullstack/libs/ui/stories/disclosure/Accordion.stories.tsx +105 -0
  259. package/templates/hono-fullstack/libs/ui/stories/disclosure/Collapsible.stories.tsx +55 -0
  260. package/templates/hono-fullstack/libs/ui/stories/disclosure/Steps.stories.tsx +208 -0
  261. package/templates/hono-fullstack/libs/ui/stories/disclosure/Tabs.stories.tsx +176 -0
  262. package/templates/hono-fullstack/libs/ui/stories/display/Avatar.stories.tsx +108 -0
  263. package/templates/hono-fullstack/libs/ui/stories/display/Badge.stories.tsx +90 -0
  264. package/templates/hono-fullstack/libs/ui/stories/display/Card.stories.tsx +121 -0
  265. package/templates/hono-fullstack/libs/ui/stories/display/Heading.stories.tsx +62 -0
  266. package/templates/hono-fullstack/libs/ui/stories/display/Icon.stories.tsx +51 -0
  267. package/templates/hono-fullstack/libs/ui/stories/display/Table.stories.tsx +222 -0
  268. package/templates/hono-fullstack/libs/ui/stories/display/Tag.stories.tsx +168 -0
  269. package/templates/hono-fullstack/libs/ui/stories/display/Text.stories.tsx +32 -0
  270. package/templates/hono-fullstack/libs/ui/stories/display/Timeline.stories.tsx +241 -0
  271. package/templates/hono-fullstack/libs/ui/stories/feedback/Alert.stories.tsx +122 -0
  272. package/templates/hono-fullstack/libs/ui/stories/feedback/Progress.stories.tsx +163 -0
  273. package/templates/hono-fullstack/libs/ui/stories/feedback/ProgressCircle.stories.tsx +176 -0
  274. package/templates/hono-fullstack/libs/ui/stories/feedback/Skeleton.stories.tsx +75 -0
  275. package/templates/hono-fullstack/libs/ui/stories/feedback/Spinner.stories.tsx +49 -0
  276. package/templates/hono-fullstack/libs/ui/stories/feedback/Toast.stories.tsx +145 -0
  277. package/templates/hono-fullstack/libs/ui/stories/forms/Checkbox.stories.tsx +281 -0
  278. package/templates/hono-fullstack/libs/ui/stories/forms/Editable.stories.tsx +94 -0
  279. package/templates/hono-fullstack/libs/ui/stories/forms/Field.stories.tsx +72 -0
  280. package/templates/hono-fullstack/libs/ui/stories/forms/Fieldset.stories.tsx +109 -0
  281. package/templates/hono-fullstack/libs/ui/stories/forms/FileUpload.stories.tsx +115 -0
  282. package/templates/hono-fullstack/libs/ui/stories/forms/Form.stories.tsx +120 -0
  283. package/templates/hono-fullstack/libs/ui/stories/forms/Input.stories.tsx +155 -0
  284. package/templates/hono-fullstack/libs/ui/stories/forms/InputGroup.stories.tsx +79 -0
  285. package/templates/hono-fullstack/libs/ui/stories/forms/NativeSelect.stories.tsx +160 -0
  286. package/templates/hono-fullstack/libs/ui/stories/forms/NumberInput.stories.tsx +149 -0
  287. package/templates/hono-fullstack/libs/ui/stories/forms/PasswordInput.stories.tsx +141 -0
  288. package/templates/hono-fullstack/libs/ui/stories/forms/PinInput.stories.tsx +155 -0
  289. package/templates/hono-fullstack/libs/ui/stories/forms/RadioCard.stories.tsx +265 -0
  290. package/templates/hono-fullstack/libs/ui/stories/forms/RadioGroup.stories.tsx +109 -0
  291. package/templates/hono-fullstack/libs/ui/stories/forms/RatingGroup.stories.tsx +123 -0
  292. package/templates/hono-fullstack/libs/ui/stories/forms/SegmentGroup.stories.tsx +130 -0
  293. package/templates/hono-fullstack/libs/ui/stories/forms/Slider.stories.tsx +190 -0
  294. package/templates/hono-fullstack/libs/ui/stories/forms/Switch.stories.tsx +124 -0
  295. package/templates/hono-fullstack/libs/ui/stories/forms/TagsInput.stories.tsx +154 -0
  296. package/templates/hono-fullstack/libs/ui/stories/forms/Textarea.stories.tsx +148 -0
  297. package/templates/hono-fullstack/libs/ui/stories/layout/Grid.stories.tsx +60 -0
  298. package/templates/hono-fullstack/libs/ui/stories/layout/Positioning.stories.tsx +90 -0
  299. package/templates/hono-fullstack/libs/ui/stories/layout/ScrollArea.stories.tsx +68 -0
  300. package/templates/hono-fullstack/libs/ui/stories/layout/Stack.stories.tsx +87 -0
  301. package/templates/hono-fullstack/libs/ui/stories/navigation/Breadcrumb.stories.tsx +104 -0
  302. package/templates/hono-fullstack/libs/ui/stories/navigation/Link.stories.tsx +62 -0
  303. package/templates/hono-fullstack/libs/ui/stories/navigation/LinkBox.stories.tsx +45 -0
  304. package/templates/hono-fullstack/libs/ui/stories/navigation/Pagination.stories.tsx +74 -0
  305. package/templates/hono-fullstack/libs/ui/stories/overlays/Dialog.stories.tsx +255 -0
  306. package/templates/hono-fullstack/libs/ui/stories/overlays/Drawer.stories.tsx +114 -0
  307. package/templates/hono-fullstack/libs/ui/stories/overlays/HoverCard.stories.tsx +47 -0
  308. package/templates/hono-fullstack/libs/ui/stories/overlays/Menu.stories.tsx +133 -0
  309. package/templates/hono-fullstack/libs/ui/stories/overlays/OverlayManager.stories.tsx +206 -0
  310. package/templates/hono-fullstack/libs/ui/stories/overlays/Popover.stories.tsx +73 -0
  311. package/templates/hono-fullstack/libs/ui/stories/overlays/Tooltip.stories.tsx +114 -0
  312. package/templates/hono-fullstack/libs/ui/stories/tsconfig.json +14 -0
  313. package/templates/hono-fullstack/libs/ui/tsconfig.build.json +5 -0
  314. package/templates/hono-fullstack/libs/ui/tsconfig.json +13 -0
  315. package/templates/hono-fullstack/libs/ui/turbo.json +28 -0
  316. package/templates/hono-fullstack/libs/ui/vite.config.ts +33 -0
  317. package/templates/hono-fullstack/oyo.config.json +30 -4
  318. package/templates/hono-fullstack/package.json +1 -0
  319. package/templates/hono-fullstack/pnpm-workspace.yaml +20 -10
  320. package/templates/hono-fullstack/turbo.json +10 -0
  321. package/templates/hono-server/.codegraph/.gitignore +5 -0
  322. package/templates/hono-server/README.md +9 -1
  323. package/templates/hono-server/_gitignore +1 -0
  324. package/templates/hono-server/apps/server/package.json +7 -2
  325. package/templates/hono-server/apps/server/src/adapters/cloudflare/entry.ts +4 -30
  326. package/templates/hono-server/apps/server/src/adapters/nodejs/bootstrap.ts +20 -12
  327. package/templates/hono-server/apps/server/src/adapters/nodejs/entry.ts +7 -57
  328. package/templates/hono-server/apps/server/src/app.ts +37 -21
  329. package/templates/hono-server/apps/server/src/config.ts +1 -1
  330. package/templates/hono-server/apps/server/src/di-container.ts +51 -0
  331. package/templates/hono-server/apps/server/src/infra/logger/getter.ts +1 -1
  332. package/templates/hono-server/apps/server/src/infra/logger/pino.ts +21 -34
  333. package/templates/hono-server/apps/server/src/infra/work-runner/errors.ts +22 -0
  334. package/templates/hono-server/apps/server/src/infra/work-runner/pass-through/runner.ts +28 -22
  335. package/templates/hono-server/apps/server/src/infra/work-runner/piscina/runner.ts +11 -34
  336. package/templates/hono-server/apps/server/src/infra/work-runner/{example → piscina}/worker-entry.ts +6 -2
  337. package/templates/hono-server/apps/server/src/infra/work-runner/types.ts +2 -16
  338. package/templates/hono-server/apps/server/src/infra/work-runner/utils/business-error.ts +10 -5
  339. package/templates/hono-server/apps/server/src/infra/work-runner/utils/message.ts +5 -17
  340. package/templates/hono-server/apps/server/src/middlewares/cors.ts +16 -22
  341. package/templates/hono-server/apps/server/src/middlewares/logger.ts +34 -0
  342. package/templates/hono-server/apps/server/src/modules/example/module.ts +8 -0
  343. package/templates/hono-server/apps/server/src/modules/example/queries.ts +8 -0
  344. package/templates/hono-server/apps/server/src/modules/example/routes.ts +74 -68
  345. package/templates/hono-server/apps/server/src/modules/example/service.test.ts +53 -0
  346. package/templates/hono-server/apps/server/src/modules/example/service.ts +54 -0
  347. package/templates/hono-server/apps/server/src/modules/example/types.ts +12 -0
  348. package/templates/hono-server/apps/server/src/modules/example/work-task/tasks.test.ts +9 -35
  349. package/templates/hono-server/apps/server/src/modules/example/work-task/tasks.ts +15 -46
  350. package/templates/hono-server/apps/server/src/shared/errors/defs/index.ts +6 -1
  351. package/templates/hono-server/apps/server/src/shared/errors/handler.ts +1 -1
  352. package/templates/hono-server/apps/server/src/shared/network/openapi/index.ts +1 -1
  353. package/templates/hono-server/apps/server/src/shared/types/app.ts +51 -0
  354. package/templates/hono-server/apps/server/src/shared/types/db.ts +126 -0
  355. package/templates/hono-server/apps/server/src/shared/utils/data-access/check.ts +19 -0
  356. package/templates/hono-server/apps/server/src/shared/utils/data-access/model.ts +47 -0
  357. package/templates/hono-server/apps/server/src/shared/utils/data-access/params.ts +105 -0
  358. package/templates/hono-server/apps/server/src/shared/utils/data-access/query.test.ts +139 -0
  359. package/templates/hono-server/apps/server/src/shared/utils/data-access/query.ts +236 -0
  360. package/templates/hono-server/apps/server/vite.config.ts +2 -2
  361. package/templates/hono-server/apps/server/wrangler.jsonc +1 -1
  362. package/templates/hono-server/docker/README.md +1 -1
  363. package/templates/hono-server/envs/.env.example.sqlite-d1 +1 -0
  364. package/templates/hono-server/libs/db/drizzle-kit/sqlite-d1.config.ts +29 -10
  365. package/templates/hono-server/libs/db/package.json +1 -0
  366. package/templates/hono-server/libs/db/src/dialects/sqlite/schema/columns.ts +1 -1
  367. package/templates/hono-server/libs/db/src/dialects/sqlite/schema/modules/examples/examples.ts +6 -3
  368. package/templates/hono-server/libs/db/src/dialects/sqlite/schema/vo.ts +7 -1
  369. package/templates/hono-server/libs/definition/README.md +81 -0
  370. package/templates/hono-server/libs/definition/package.json +3 -1
  371. package/templates/hono-server/libs/definition/src/constants/index.ts +1 -0
  372. package/templates/hono-server/libs/definition/src/constants/pagination.ts +3 -0
  373. package/templates/hono-server/libs/definition/src/modules/common-contracts.ts +24 -0
  374. package/templates/hono-server/libs/definition/src/modules/example/contracts.ts +34 -0
  375. package/templates/hono-server/libs/definition/src/modules/example/index.ts +1 -16
  376. package/templates/hono-server/libs/definition/src/modules/index.ts +2 -0
  377. package/templates/hono-server/libs/definition/src/modules/vo-generated-types.ts +12 -0
  378. package/templates/hono-server/oyo.config.json +30 -4
  379. package/templates/hono-server/package.json +1 -0
  380. package/templates/hono-server/pnpm-workspace.yaml +9 -7
  381. package/templates/hono-server/turbo.json +10 -0
  382. package/templates/monorepo/.codegraph/.gitignore +5 -0
  383. package/templates/monorepo/_gitignore +1 -0
  384. package/templates/monorepo/oyo.config.json +6 -4
  385. package/templates/monorepo/pnpm-workspace.yaml +4 -4
  386. package/templates/monorepo/turbo.json +10 -0
  387. package/templates/hono-fullstack/apps/server/src/modules/example/contracts.ts +0 -9
  388. package/templates/hono-fullstack/apps/server/src/shared/app/types.ts +0 -44
  389. package/templates/hono-fullstack/apps/stack/frontend/styles.css +0 -23
  390. package/templates/hono-server/apps/server/src/modules/example/contracts.ts +0 -9
  391. package/templates/hono-server/apps/server/src/shared/app/types.ts +0 -44
@@ -1,58 +1,27 @@
1
- import type { ProcessExampleInput, ProcessExampleOutput } from '@repo/definition/backend'
2
- import { type WorkSubject } from '@/infra/work-runner/constants'
3
- import type {
4
- DefineRunnerTaskModel,
5
- RunnerTaskResult,
6
- WorkerMessagePort,
7
- } from '@/infra/work-runner/types'
8
- import {
9
- isWorkerTaskBusinessError,
10
- WorkerTaskBusinessError,
11
- } from '@/infra/work-runner/utils/business-error'
12
- import { workerLog, workerProgress } from '@/infra/work-runner/utils/message'
1
+ import type { IExampleProcess, IExampleProcessResult } from '@repo/definition/backend'
2
+ import type { WorkSubject } from '@/infra/work-runner/constants'
3
+ import type { DefineRunnerTaskModel, RunnerTaskResult } from '@/infra/work-runner/types'
4
+ import { WorkerTaskBusinessError } from '@/infra/work-runner/utils/business-error'
13
5
 
14
6
  export type ExampleTaskErrorCode = 'EXAMPLE_PROCESS_FAILED'
15
7
 
16
8
  export type ExampleRunnerTaskModel = DefineRunnerTaskModel<
17
9
  typeof WorkSubject.PROCESS_EXAMPLE,
18
- ProcessExampleInput,
19
- ProcessExampleOutput,
10
+ IExampleProcess,
11
+ IExampleProcessResult,
20
12
  ExampleTaskErrorCode
21
13
  >
22
14
 
23
15
  export async function executeExampleTask(
24
16
  input: ExampleRunnerTaskModel['input'],
25
- messagePort: WorkerMessagePort,
26
- ): Promise<RunnerTaskResult<ProcessExampleOutput, ExampleTaskErrorCode>> {
27
- try {
28
- workerLog(messagePort, 'debug', 'Example task started')
29
- workerProgress(messagePort, 0, 'Validating input')
30
-
31
- const original = input.message
32
- const normalized = original.trim().replace(/\s+/g, ' ')
33
- if (!normalized) {
34
- throw new WorkerTaskBusinessError('Message must not be empty', { field: 'message' })
35
- }
36
-
37
- workerProgress(messagePort, 100, 'Example task completed')
38
- const result = {
39
- ok: true,
40
- data: { original, normalized, length: normalized.length },
41
- } as const
42
- messagePort.postMessage({ type: 'result', data: result.data })
43
- return result
44
- } catch (error) {
45
- if (!isWorkerTaskBusinessError(error)) throw error
46
-
47
- const result: RunnerTaskResult<ProcessExampleOutput, ExampleTaskErrorCode> = {
48
- ok: false,
49
- error: {
50
- code: 'EXAMPLE_PROCESS_FAILED',
51
- message: error.message,
52
- ...(error.details ? { details: error.details } : {}),
53
- },
54
- }
55
- messagePort.postMessage({ type: 'error', error: result.error })
56
- return result
17
+ ): Promise<RunnerTaskResult<IExampleProcessResult, ExampleTaskErrorCode>> {
18
+ const original = input.message
19
+ const normalized = original.trim().replace(/\s+/g, ' ')
20
+ if (!normalized) {
21
+ throw new WorkerTaskBusinessError('Message must not be empty', {
22
+ code: 'EXAMPLE_PROCESS_FAILED',
23
+ field: 'message',
24
+ })
57
25
  }
26
+ return { ok: true, data: { original, normalized, length: normalized.length } }
58
27
  }
@@ -12,4 +12,9 @@ const normalErrorCategories = {
12
12
  NOT_FOUND: { status: ErrorHttpStatus.NOT_FOUND, message: 'Resource not found' },
13
13
  } satisfies Record<string, ErrorCategoryItem>
14
14
 
15
- export const E = defineErrorCategories({ NORMAL: normalErrorCategories })
15
+ export const E = defineErrorCategories({
16
+ NORMAL: normalErrorCategories,
17
+ EXAMPLES: {
18
+ NOT_FOUND: { status: ErrorHttpStatus.NOT_FOUND, message: 'Example not found' },
19
+ },
20
+ })
@@ -2,7 +2,7 @@ import { problemDetailsHandler } from 'hono-problem-details'
2
2
  import { E } from './defs'
3
3
  import { logLevelFor } from './log'
4
4
  import type { ErrorHandler } from 'hono'
5
- import type { AppEnv } from '@/shared/app/types'
5
+ import type { AppEnv } from '@/shared/types/app'
6
6
  import { getLogger } from '@/infra/logger/getter'
7
7
 
8
8
  const problemHandler = problemDetailsHandler({
@@ -1,7 +1,7 @@
1
1
  import type { Hono } from 'hono'
2
2
  import { swaggerUI } from '@hono/swagger-ui'
3
3
  import { generateSpecs } from 'hono-openapi'
4
- import type { AppEnv } from '@/shared/app/types'
4
+ import type { AppEnv } from '@/shared/types/app'
5
5
  import { stringify } from 'yaml'
6
6
 
7
7
  export * from './res-defs'
@@ -0,0 +1,51 @@
1
+ import type { Hono } from 'hono'
2
+ import type { Logger } from 'pino'
3
+ import type { InferdiHonoEnv } from '@inferdi/hono'
4
+ import type {
5
+ CORSConfig,
6
+ LogBaseConfig,
7
+ LogPrettyRawConfig,
8
+ LogRollRawConfig,
9
+ LogRollSchemaConfig,
10
+ RuntimeConfig,
11
+ WorkerConfig,
12
+ } from '@repo/definition/backend'
13
+ import type { RequestIdVariables } from 'hono/request-id'
14
+ import type { DIContainer } from '@/di-container'
15
+
16
+ export type AppLogger = Logger
17
+
18
+ export type LogConfig = {
19
+ base: LogBaseConfig
20
+ roll?: LogRollSchemaConfig & LogRollRawConfig
21
+ pretty?: LogPrettyRawConfig
22
+ }
23
+
24
+ export type AppConfig = {
25
+ runtime: RuntimeConfig & { isLocal: boolean }
26
+ log: LogConfig
27
+ worker: WorkerConfig
28
+ middleware: { cors: CORSConfig }
29
+ }
30
+
31
+ // export type AppDependencies = {
32
+ // appConfig: AppConfig
33
+ // loggerHandler: MiddlewareHandler<{ Variables: { logger: AppLogger } }>
34
+ // db: unknown
35
+ // }
36
+
37
+ export type AppEnv = {
38
+ Variables: InferdiHonoEnv<DIContainer>['Variables'] & RequestIdVariables & { logger?: AppLogger }
39
+ }
40
+
41
+ export type CreateAppOptions = {
42
+ serverBasePath?: string
43
+ env: Record<string, unknown>
44
+ // makeDependencies: (c: Context<AppEnv>) => Promise<AppDependencies> | AppDependencies
45
+ }
46
+
47
+ export type AppInstance = {
48
+ app: Hono<AppEnv>
49
+ logger: Logger
50
+ dispose: () => Promise<void>
51
+ }
@@ -0,0 +1,126 @@
1
+ import type {
2
+ InsertModel,
3
+ SelectVOResults,
4
+ SqliteColType,
5
+ SqliteTableType,
6
+ TableColsMap,
7
+ TableField,
8
+ VO,
9
+ relations,
10
+ } from '@repo/db/sqlite/schema'
11
+ import type { DBQueryConfigOrderBy, InferSelectModel, TableFilter } from 'drizzle-orm'
12
+ import type { SQLiteAsyncDatabase } from 'drizzle-orm/sqlite-core/async'
13
+ import type { createD1Client } from '@repo/db/sqlite/d1-client'
14
+ import type { SqliteDatabase } from '@repo/db/sqlite/sqlite'
15
+ import type { Model } from '@/shared/utils/data-access/model'
16
+
17
+ /** 应用持有的数据库资源,按运行环境使用本地 SQLite 或 Cloudflare D1。 */
18
+ export type Database = {
19
+ db: SqliteDatabase | ReturnType<typeof createD1Client>['db']
20
+ [Symbol.dispose]?: () => void
21
+ }
22
+
23
+ /** Drizzle 的驱动执行方式:本地 better-sqlite3 同步返回结果,Cloudflare D1 返回 Promise。 */
24
+ export type QueryResultKind = 'sync' | 'async'
25
+
26
+ // 仅要求查询能力,SQLite、D1 的 db 或 tx 均可传入;查询层不持有事务能力。
27
+ export type QueryDB<K extends QueryResultKind = QueryResultKind> = Pick<
28
+ SQLiteAsyncDatabase<K, unknown, typeof relations>,
29
+ 'query' | 'select' | 'insert' | 'update' | 'delete'
30
+ > & {
31
+ /** Drizzle 自带的运行时标记;公共查询据此保持驱动原有的同步或异步行为。 */
32
+ readonly _: { readonly resultKind: K }
33
+ }
34
+
35
+ export type ObjectWhereCondition<T extends SqliteTableType> = TableFilter<T>
36
+ export type VOsMap = Record<string, VO<SqliteColType>>
37
+ export type ModelType = Pick<Model<SqliteTableType, VOsMap>, 'table' | 'voCollections'>
38
+
39
+ /** 通用查询参数;voType 由调用方选择,未指定时返回整行。 */
40
+ export type QueryOptions<T extends SqliteTableType, VT extends string = string> = {
41
+ where?: ObjectWhereCondition<T>
42
+ orderBy?: DBQueryConfigOrderBy<T, TableColsMap<T>>
43
+ voType?: VT
44
+ page?: number
45
+ limit?: number
46
+ withTotal?: boolean
47
+ fuzzyWhere?: TableField<T>[]
48
+ }
49
+
50
+ export type QueryOneOptions<T extends SqliteTableType, VT extends string = string> = Omit<
51
+ QueryOptions<T, VT>,
52
+ 'page' | 'limit' | 'withTotal'
53
+ >
54
+
55
+ export type QueryPageOptions<T extends SqliteTableType, VT extends string = string> = Omit<
56
+ QueryOptions<T, VT>,
57
+ 'withTotal'
58
+ > & { limit: number }
59
+
60
+ export type MetaBase = {
61
+ offset: number
62
+ // 未提供 limit 时不分页,不引入隐含的数量上限。
63
+ limit?: number
64
+ }
65
+
66
+ export type GetModelVO<
67
+ M extends ModelType,
68
+ VT extends keyof M['voCollections'] & string,
69
+ > = M['voCollections'][VT]
70
+
71
+ export type SelectModelVOResults<
72
+ M extends ModelType,
73
+ VT extends keyof M['voCollections'] & string,
74
+ > = SelectVOResults<GetModelVO<M, VT>>
75
+
76
+ /** 按调用方的 voType 推断单行字段;可选 voType 同时保留整行结果的可能性。 */
77
+ export type QueryRow<
78
+ M extends ModelType,
79
+ O extends { voType?: keyof M['voCollections'] & string },
80
+ > = 'voType' extends keyof O
81
+ ?
82
+ | (undefined extends O['voType'] ? InferSelectModel<M['table']> : never)
83
+ | {
84
+ [VT in Extract<O['voType'], keyof M['voCollections'] & string>]: SelectModelVOResults<
85
+ M,
86
+ VT
87
+ >
88
+ }[Extract<O['voType'], keyof M['voCollections'] & string>]
89
+ : InferSelectModel<M['table']>
90
+
91
+ export type QueryResults<
92
+ M extends ModelType,
93
+ Q extends QueryOptions<M['table'], keyof M['voCollections'] & string>,
94
+ > = {
95
+ results: QueryRow<M, Q>[]
96
+ meta: MetaBase &
97
+ (Q extends { limit: number } ? { limit: number } : {}) &
98
+ (Q extends { withTotal: true } ? { total: number } : { total?: number })
99
+ }
100
+
101
+ export type InsertOptions<T extends SqliteTableType, VT extends string = string> = {
102
+ values: InsertModel<T> | InsertModel<T>[]
103
+ voType?: VT
104
+ }
105
+
106
+ export type InsertResults<
107
+ M extends ModelType,
108
+ O extends InsertOptions<M['table'], keyof M['voCollections'] & string>,
109
+ > = {
110
+ result: QueryRow<M, O>[]
111
+ meta: { count: number; affected: number }
112
+ }
113
+
114
+ export type UpdateOptions<T extends SqliteTableType> = {
115
+ value: Partial<InsertModel<T>>
116
+ where: ObjectWhereCondition<T>
117
+ fuzzyWhere?: TableField<T>[]
118
+ }
119
+
120
+ export type DeleteOptions<T extends SqliteTableType> = {
121
+ where: ObjectWhereCondition<T>
122
+ fuzzyWhere?: TableField<T>[]
123
+ }
124
+
125
+ export type UpdateResults = { updated: number }
126
+ export type DeleteResults = { deleted: number }
@@ -0,0 +1,19 @@
1
+ /**
2
+ * 检查多组条件是否存在冲突记录,任一命中就抛出调用方提供的领域错误。
3
+ * findOne 由领域绑定 db 或 tx,支持同步或异步查询;统一使用 id VO,避免读取整行。
4
+ * 多组条件并行检查,按传入顺序报告首个冲突;空数组不执行查询,不创建事务。
5
+ * 这里只做写入前的业务检查,并发写入仍由数据库唯一约束最终保证。
6
+ */
7
+ export async function checkExistedConflict<Where>(
8
+ findOne: (options: {
9
+ where: Where
10
+ voType: 'id'
11
+ }) => object | null | undefined | PromiseLike<object | null | undefined>,
12
+ conditions: readonly Where[],
13
+ createError: (condition: Where) => Error,
14
+ ): Promise<void> {
15
+ const results = await Promise.all(conditions.map((where) => findOne({ where, voType: 'id' })))
16
+ for (const [index, condition] of conditions.entries()) {
17
+ if (results[index]) throw createError(condition)
18
+ }
19
+ }
@@ -0,0 +1,47 @@
1
+ import type { SqliteColType, SqliteTableType, TableField } from '@repo/db/sqlite/schema'
2
+ import { getTableName } from 'drizzle-orm'
3
+ import { getTableConfig } from 'drizzle-orm/sqlite-core'
4
+ import type { VOsMap } from '@/shared/types/db'
5
+
6
+ /** 绑定表与 VO,保存主键列和默认模糊查询字段,不执行数据库操作。 */
7
+ export class Model<TTable extends SqliteTableType, VOs extends VOsMap> {
8
+ protected fuzzyWhereFields: TableField<TTable>[] = []
9
+ private readonly pkCols: SqliteColType[]
10
+
11
+ constructor(
12
+ public readonly table: TTable,
13
+ public readonly voCollections: VOs,
14
+ ) {
15
+ const { columns, primaryKeys } = getTableConfig(table)
16
+ // 列级主键与表级复合主键都需要保留;复合主键沿用声明时的列顺序。
17
+ this.pkCols = primaryKeys.length
18
+ ? primaryKeys.flatMap((primaryKey) => primaryKey.columns)
19
+ : columns.filter((column) => column.primary)
20
+ }
21
+
22
+ /** 读取数据库中的实际表名。 */
23
+ public get tableName() {
24
+ return getTableName(this.table)
25
+ }
26
+
27
+ /** 返回声明的主键列,支持复合主键,不约定主键名为 id。 */
28
+ public getPkCols() {
29
+ return this.pkCols
30
+ }
31
+
32
+ /** 取得指定 VO 的列映射;未知的 VO 名称抛出异常。 */
33
+ public getVO<VT extends keyof VOs & string>(type: VT): VOs[VT] {
34
+ if (!Object.hasOwn(this.voCollections, type)) throw new Error(`invalid vo type: ${type}`)
35
+ return this.voCollections[type]
36
+ }
37
+
38
+ /** 设置读取时默认将字符串等值条件转为 LIKE 的字段。 */
39
+ public setFuzzyWhereFields(fields: TableField<TTable>[]) {
40
+ this.fuzzyWhereFields = fields
41
+ }
42
+
43
+ /** 读取默认模糊查询字段;调用方可传 fuzzyWhere 覆盖,空数组表示禁用。 */
44
+ public getFuzzyWhereFields() {
45
+ return this.fuzzyWhereFields
46
+ }
47
+ }
@@ -0,0 +1,105 @@
1
+ import type { SqliteTableType, TableField, VO, VOColsOption } from '@repo/db/sqlite/schema'
2
+ import { relationsFilterToSQL, sql, type AnyTableFilter } from 'drizzle-orm'
3
+ import type {
4
+ DeleteOptions,
5
+ MetaBase,
6
+ ObjectWhereCondition,
7
+ QueryOptions,
8
+ UpdateOptions,
9
+ VOsMap,
10
+ } from '@/shared/types/db'
11
+ import type { Model } from './model'
12
+
13
+ /** 校验分页参数并计算偏移量;不传 limit 则不分页,page 从 1 开始。 */
14
+ export function calcLimit(page?: number, limit?: number): MetaBase {
15
+ if (limit === undefined) {
16
+ if (page !== undefined) throw new Error('limit is required when page is provided')
17
+ return { offset: 0 }
18
+ }
19
+ if (!Number.isSafeInteger(limit) || limit <= 0) {
20
+ throw new Error('limit must be a positive safe integer')
21
+ }
22
+ const currentPage = page === undefined ? 1 : page
23
+ if (!Number.isSafeInteger(currentPage) || currentPage <= 0) {
24
+ throw new Error('page must be a positive safe integer')
25
+ }
26
+ const offset = (currentPage - 1) * limit
27
+ if (!Number.isSafeInteger(offset)) throw new Error('page offset exceeds the safe integer range')
28
+ return { offset, limit }
29
+ }
30
+
31
+ /** 仅将指定顶层字段的字符串等值条件转为 LIKE;保留其他条件且不修改原对象。 */
32
+ export function makeFuzzyWhere<T extends SqliteTableType>(
33
+ where: ObjectWhereCondition<T> | undefined,
34
+ fields: TableField<T>[],
35
+ ): ObjectWhereCondition<T> | undefined {
36
+ if (!where) return undefined
37
+ const fuzzyFields = new Set<string>(fields)
38
+ return Object.fromEntries(
39
+ Object.entries(where).map(([field, value]) => [
40
+ field,
41
+ fuzzyFields.has(field) && typeof value === 'string' ? { like: `%${value}%` } : value,
42
+ ]),
43
+ ) as ObjectWhereCondition<T>
44
+ }
45
+
46
+ /** 将 VO、默认模糊字段和分页参数转换为 Drizzle 查询所需的参数。 */
47
+ export function parseDrizzleQueryOptions<T extends SqliteTableType, VOs extends VOsMap>(
48
+ model: Model<T, VOs>,
49
+ options: QueryOptions<T, keyof VOs & string>,
50
+ ) {
51
+ const { voType, where, fuzzyWhere, page, limit, orderBy } = options
52
+ return {
53
+ selectFields: voType === undefined ? undefined : mapObjToTrue(model.getVO(voType)),
54
+ where: makeFuzzyWhere(where, fuzzyWhere || model.getFuzzyWhereFields()),
55
+ orderByObj: orderBy,
56
+ withTotal: !!options.withTotal,
57
+ ...calcLimit(page, limit),
58
+ }
59
+ }
60
+
61
+ /** 将 VO 的列映射转换为关系查询的 columns 选择对象,例如 { id: true }。 */
62
+ export function mapObjToTrue<V extends VO>(vo: V): VOColsOption<V> {
63
+ return Object.fromEntries(Object.keys(vo).map((key) => [key, true])) as VOColsOption<V>
64
+ }
65
+
66
+ // 使用 Drizzle 的单表条件编译能力,使读取和计数、写入使用相同的条件语义。
67
+ /** 把单表对象条件编译为 SQL,供 count、update、delete 使用;空条件返回 undefined。 */
68
+ export function objectWhereToSQL<T extends SqliteTableType>(
69
+ table: T,
70
+ where?: ObjectWhereCondition<T>,
71
+ ) {
72
+ if (where === undefined) return undefined
73
+ // 原生编译器接受擦除具体表类型的条件;递归 OR/AND 的泛型差异仅在此边界适配。
74
+ const { sql: chunks } = relationsFilterToSQL(table, where as AnyTableFilter)
75
+ return chunks === undefined ? undefined : sql.fromList(chunks)
76
+ }
77
+
78
+ /** 校验更新字段并编译条件,拒绝无更新字段或无条件的更新操作。 */
79
+ export function parseUpdateOptions<T extends SqliteTableType, VOs extends VOsMap>(
80
+ model: Model<T, VOs>,
81
+ options: UpdateOptions<T>,
82
+ ) {
83
+ if (!Object.values(options.value).some((value) => value !== undefined)) {
84
+ throw new Error('no update fields')
85
+ }
86
+ const conditions = objectWhereToSQL(
87
+ model.table,
88
+ options.fuzzyWhere ? makeFuzzyWhere(options.where, options.fuzzyWhere) : options.where,
89
+ )
90
+ if (!conditions) throw new Error('update requires a where condition')
91
+ return { value: options.value, conditions }
92
+ }
93
+
94
+ /** 编译删除条件并拒绝无条件删除;写入只对显式传入的 fuzzyWhere 做模糊匹配。 */
95
+ export function parseDeleteOptions<T extends SqliteTableType, VOs extends VOsMap>(
96
+ model: Model<T, VOs>,
97
+ options: DeleteOptions<T>,
98
+ ) {
99
+ const conditions = objectWhereToSQL(
100
+ model.table,
101
+ options.fuzzyWhere ? makeFuzzyWhere(options.where, options.fuzzyWhere) : options.where,
102
+ )
103
+ if (!conditions) throw new Error('delete requires a where condition')
104
+ return { conditions }
105
+ }
@@ -0,0 +1,139 @@
1
+ import { afterEach, beforeEach, describe, expect, it, vi } from 'vite-plus/test'
2
+ import { createSqliteClient } from '@repo/db/sqlite/sqlite'
3
+ import { createD1Client, type D1Binding } from '@repo/db/sqlite/d1-client'
4
+ import { eq, sql } from 'drizzle-orm'
5
+ import { sqliteTable, text } from 'drizzle-orm/sqlite-core'
6
+ import type { QueryDB } from '@/shared/types/db'
7
+ import { createPreparedQueries } from './query'
8
+
9
+ const records = sqliteTable('prepared_query_records', {
10
+ title: text('title').notNull(),
11
+ })
12
+
13
+ describe('createPreparedQueries', () => {
14
+ let client: ReturnType<typeof createSqliteClient>
15
+
16
+ beforeEach(() => {
17
+ // 只使用内存数据库,关闭连接后自动回收所有记录。
18
+ client = createSqliteClient(':memory:')
19
+ client.db.run(sql`CREATE TABLE prepared_query_records (title TEXT NOT NULL)`)
20
+ client.db
21
+ .insert(records)
22
+ .values([{ title: 'A' }, { title: 'B' }])
23
+ .run()
24
+ })
25
+
26
+ afterEach(() => {
27
+ client?.close()
28
+ })
29
+
30
+ it('首次使用才准备,同一 db 复用 statement,参数和查询结果不缓存', () => {
31
+ const prepare = vi.fn((db: QueryDB) => ({
32
+ byTitle: db
33
+ .select()
34
+ .from(records)
35
+ .where(eq(records.title, sql.placeholder('title')))
36
+ .prepare(),
37
+ }))
38
+ const prepareQueries = createPreparedQueries(prepare)
39
+ expect(prepare).not.toHaveBeenCalled()
40
+
41
+ // 显式预热与后续查询使用同一入口,也不访问数据。
42
+ const prepared = prepareQueries(client.db)
43
+ expect(prepareQueries(client.db)).toBe(prepared)
44
+ expect(prepared.byTitle.get({ title: 'A' })).toEqual({ title: 'A' })
45
+ expect(prepared.byTitle.get({ title: 'B' })).toEqual({ title: 'B' })
46
+ client.db.delete(records).where(eq(records.title, 'A')).run()
47
+ expect(prepareQueries(client.db).byTitle.get({ title: 'A' })).toBeUndefined()
48
+ expect(prepare).toHaveBeenCalledTimes(1)
49
+ })
50
+
51
+ it('不同 db 和 tx 分别准备,事务中的查询保持同步并参与回滚', () => {
52
+ const prepare = vi.fn((db: QueryDB) => ({
53
+ all: db.select().from(records).prepare(),
54
+ }))
55
+ const prepareQueries = createPreparedQueries(prepare)
56
+ const original = prepareQueries(client.db)
57
+ const other = createSqliteClient(':memory:')
58
+ try {
59
+ other.db.run(sql`CREATE TABLE prepared_query_records (title TEXT NOT NULL)`)
60
+ expect(prepareQueries(other.db)).not.toBe(original)
61
+ expect(prepareQueries(other.db).all.all()).toEqual([])
62
+
63
+ const failure = new Error('rollback')
64
+ expect(() =>
65
+ client.db.transaction((tx) => {
66
+ tx.insert(records).values({ title: 'C' }).run()
67
+ const prepared = prepareQueries(tx)
68
+ expect(prepared).not.toBe(original)
69
+ expect(prepareQueries(tx)).toBe(prepared)
70
+ expect(prepared.all.all()).toEqual([{ title: 'A' }, { title: 'B' }, { title: 'C' }])
71
+ throw failure
72
+ }),
73
+ ).toThrow(failure)
74
+ expect(original.all.all()).toEqual([{ title: 'A' }, { title: 'B' }])
75
+ expect(prepareQueries(client.db)).toBe(original)
76
+ expect(prepare).toHaveBeenCalledTimes(3)
77
+ } finally {
78
+ other.close()
79
+ }
80
+ })
81
+
82
+ it('模块各自创建缓存,同一 db 下的不同查询定义互不覆盖', () => {
83
+ const prepareModuleA = createPreparedQueries((db) => ({
84
+ query: db.select().from(records).where(eq(records.title, 'A')).prepare(),
85
+ }))
86
+ const prepareModuleB = createPreparedQueries((db) => ({
87
+ query: db.select().from(records).where(eq(records.title, 'B')).prepare(),
88
+ }))
89
+
90
+ expect(prepareModuleA(client.db)).not.toBe(prepareModuleB(client.db))
91
+ expect(prepareModuleA(client.db).query.get()).toEqual({ title: 'A' })
92
+ expect(prepareModuleB(client.db).query.get()).toEqual({ title: 'B' })
93
+ expect(prepareModuleA(client.db)).toBe(prepareModuleA(client.db))
94
+ expect(prepareModuleB(client.db)).toBe(prepareModuleB(client.db))
95
+ })
96
+
97
+ it('prepare 失败不缓存,修复后可重新准备', () => {
98
+ const failure = new Error('prepare failed')
99
+ const prepare = vi.fn((db: QueryDB) => ({
100
+ query: db.select().from(records).prepare(),
101
+ }))
102
+ prepare.mockImplementationOnce(() => {
103
+ throw failure
104
+ })
105
+ const prepareQueries = createPreparedQueries(prepare)
106
+
107
+ expect(() => prepareQueries(client.db)).toThrow(failure)
108
+ const prepared = prepareQueries(client.db)
109
+ expect(prepared.query.all()).toEqual([{ title: 'A' }, { title: 'B' }])
110
+ expect(prepareQueries(client.db)).toBe(prepared)
111
+ expect(prepare).toHaveBeenCalledTimes(2)
112
+ })
113
+
114
+ it('D1 复用未绑定参数的 statement,执行仍返回 Promise,参数逐次独立绑定', async () => {
115
+ // 使用真实 Drizzle D1 驱动和假的 binding,不请求 Cloudflare 或真实数据库。
116
+ const bind = vi.fn((title: string) => ({
117
+ raw: async () => [[title]],
118
+ }))
119
+ const nativePrepare = vi.fn(() => ({ bind }))
120
+ const { db } = createD1Client({ prepare: nativePrepare } as unknown as D1Binding)
121
+ const prepareQueries = createPreparedQueries((database) => ({
122
+ byTitle: database
123
+ .select()
124
+ .from(records)
125
+ .where(eq(records.title, sql.placeholder('title')))
126
+ .prepare(),
127
+ }))
128
+ const prepared = prepareQueries(db)
129
+ const first = prepared.byTitle.get({ title: 'A' })
130
+ const second = prepareQueries(db).byTitle.get({ title: 'B' })
131
+
132
+ expect(first).toBeInstanceOf(Promise)
133
+ expect(second).toBeInstanceOf(Promise)
134
+ await expect(first).resolves.toEqual({ title: 'A' })
135
+ await expect(second).resolves.toEqual({ title: 'B' })
136
+ expect(nativePrepare).toHaveBeenCalledTimes(1)
137
+ expect(bind.mock.calls).toEqual([['A'], ['B']])
138
+ })
139
+ })