@peaske7/readit 0.3.0-rc.0 → 0.3.0-rc.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (373) hide show
  1. package/dist/.vite/manifest.json +1111 -0
  2. package/dist/assets/_basePickBy-BMMA4Tou.js +1 -0
  3. package/dist/assets/_baseUniq-D40qku1I.js +1 -0
  4. package/dist/assets/arc-Ckg65iy8.js +1 -0
  5. package/dist/assets/architecture-YZFGNWBL-Dv3EY0zV.js +1 -0
  6. package/dist/assets/architectureDiagram-Q4EWVU46-ClRss4cm.js +36 -0
  7. package/dist/assets/array-Bjz-wYpJ.js +1 -0
  8. package/dist/assets/blockDiagram-DXYQGD6D-CBcFvoK1.js +132 -0
  9. package/dist/assets/c4Diagram-AHTNJAMY-D4d3ZLam.js +10 -0
  10. package/dist/assets/channel-D9EJxDy_.js +1 -0
  11. package/dist/assets/chunk-2KRD3SAO-DaFfaCGO.js +1 -0
  12. package/dist/assets/chunk-336JU56O-yLEQoF0v.js +2 -0
  13. package/dist/assets/chunk-426QAEUC-Uyzd4wAA.js +1 -0
  14. package/dist/assets/chunk-4BX2VUAB-DRuTD7x5.js +1 -0
  15. package/dist/assets/chunk-4TB4RGXK-3xbpIi_o.js +206 -0
  16. package/dist/assets/chunk-55IACEB6-BExiaAoD.js +1 -0
  17. package/dist/assets/chunk-5FUZZQ4R-DatVvHnF.js +62 -0
  18. package/dist/assets/chunk-5PVQY5BW-BKgvrGh8.js +2 -0
  19. package/dist/assets/chunk-67CJDMHE-DMt8LNEX.js +1 -0
  20. package/dist/assets/chunk-7N4EOEYR-CzLGefVf.js +1 -0
  21. package/dist/assets/chunk-AA7GKIK3-B6GFAk4U.js +1 -0
  22. package/dist/assets/chunk-BSJP7CBP-BK29yehL.js +1 -0
  23. package/dist/assets/chunk-CIAEETIT-D7hBXImP.js +1 -0
  24. package/dist/assets/chunk-Dlc7tRH4.js +1 -0
  25. package/dist/assets/chunk-EDXVE4YY-PYJdlmyH.js +1 -0
  26. package/dist/assets/chunk-ENJZ2VHE-DUHKBv6x.js +10 -0
  27. package/dist/assets/chunk-FMBD7UC4-2FWyCCAV.js +15 -0
  28. package/dist/assets/chunk-FOC6F5B3-DKFHrt4K.js +1 -0
  29. package/dist/assets/chunk-ICPOFSXX-Bh__D0ec.js +122 -0
  30. package/dist/assets/chunk-K5T4RW27-D51O7IkG.js +94 -0
  31. package/dist/assets/chunk-KGLVRYIC-DMHSCH4T.js +1 -0
  32. package/dist/assets/chunk-LIHQZDEY-C2aANxt9.js +1 -0
  33. package/dist/assets/chunk-ORNJ4GCN-Db_37NRX.js +1 -0
  34. package/dist/assets/chunk-OYMX7WX6-BltXOJLJ.js +231 -0
  35. package/dist/assets/chunk-QZHKN3VN-8Lcg9gti.js +1 -0
  36. package/dist/assets/chunk-U2HBQHQK-ByS6tilY.js +70 -0
  37. package/dist/assets/chunk-X2U36JSP-Bm-4Gqg_.js +1 -0
  38. package/dist/assets/chunk-XPW4576I-Bqbompq4.js +32 -0
  39. package/dist/assets/chunk-YZCP3GAM-CsC0imPb.js +1 -0
  40. package/dist/assets/chunk-ZZ45TVLE-CG-CqfPC.js +1 -0
  41. package/dist/assets/classDiagram-6PBFFD2Q-Jy1uFUk4.js +1 -0
  42. package/dist/assets/classDiagram-v2-HSJHXN6E-ChiLl3rR.js +1 -0
  43. package/dist/assets/clone-BBjvuERA.js +1 -0
  44. package/dist/assets/cose-bilkent-S5V4N54A-q90QeGKv.js +1 -0
  45. package/dist/assets/cytoscape.esm-BfXff3fb.js +321 -0
  46. package/dist/assets/dagre-KV5264BT-BQWiLFJB.js +4 -0
  47. package/dist/assets/dagre-nn_aIZ2E.js +1 -0
  48. package/dist/assets/defaultLocale-BwmRmqJp.js +1 -0
  49. package/dist/assets/diagram-5BDNPKRD-CJa7Y97H.js +10 -0
  50. package/dist/assets/diagram-G4DWMVQ6-tVQGBWfY.js +24 -0
  51. package/dist/assets/diagram-MMDJMWI5-CpimFldm.js +43 -0
  52. package/dist/assets/diagram-TYMM5635-D11WQVgy.js +24 -0
  53. package/dist/assets/dist-BNz65Ibc.js +1 -0
  54. package/dist/assets/erDiagram-SMLLAGMA-C2bLd0jS.js +85 -0
  55. package/dist/assets/flowDiagram-DWJPFMVM-Kw3fOOLT.js +162 -0
  56. package/dist/assets/ganttDiagram-T4ZO3ILL-fyMhyE2X.js +292 -0
  57. package/dist/assets/gitGraph-7Q5UKJZL-BGFRt2qs.js +1 -0
  58. package/dist/assets/gitGraphDiagram-UUTBAWPF-D4JoiOvg.js +106 -0
  59. package/dist/assets/graphlib-DGcD9J2L.js +1 -0
  60. package/dist/assets/index-Cow3qpoq.css +2 -0
  61. package/dist/assets/index-DUf7okYi.js +14 -0
  62. package/dist/assets/info-OMHHGYJF-DI6-Z9vh.js +1 -0
  63. package/dist/assets/infoDiagram-42DDH7IO-D1ZkeMBy.js +2 -0
  64. package/dist/assets/init-TPm5RB77.js +1 -0
  65. package/dist/assets/isArrayLikeObject-69BLnVNM.js +1 -0
  66. package/dist/assets/isEmpty-DUS28g5f.js +1 -0
  67. package/dist/assets/ishikawaDiagram-UXIWVN3A-Dv8hzjZB.js +70 -0
  68. package/dist/assets/journeyDiagram-VCZTEJTY-COeB7F5r.js +139 -0
  69. package/dist/assets/kanban-definition-6JOO6SKY-BbYmxCYU.js +89 -0
  70. package/dist/assets/katex-5SGEXwpi.js +261 -0
  71. package/dist/assets/line-_v2NGEdn.js +1 -0
  72. package/dist/assets/linear-CXMqTN8N.js +1 -0
  73. package/dist/assets/mermaid-config-C8a4L22x.js +1 -0
  74. package/dist/assets/mermaid-parser.core-CFmphzPP.js +4 -0
  75. package/dist/assets/mermaid.core-DnHAupTp.js +11 -0
  76. package/dist/assets/mindmap-definition-QFDTVHPH-D7_lIep7.js +96 -0
  77. package/dist/assets/ordinal-D7l-8DAO.js +1 -0
  78. package/dist/assets/packet-4T2RLAQJ-DidW3JFc.js +1 -0
  79. package/dist/assets/path-BVpCanzE.js +1 -0
  80. package/dist/assets/pie-ZZUOXDRM-Bff2e5hg.js +1 -0
  81. package/dist/assets/pieDiagram-DEJITSTG-DDvYHCT_.js +30 -0
  82. package/dist/assets/quadrantDiagram-34T5L4WZ-DcLcIrdi.js +7 -0
  83. package/dist/assets/radar-PYXPWWZC-CsdZBH3M.js +1 -0
  84. package/dist/assets/requirementDiagram-MS252O5E-DLX6ld7D.js +84 -0
  85. package/dist/assets/rough.esm-BoTisKeL.js +1 -0
  86. package/dist/assets/sankeyDiagram-XADWPNL6-D-1GtsHM.js +10 -0
  87. package/dist/assets/sequenceDiagram-FGHM5R23-Bwxs0YQg.js +157 -0
  88. package/dist/assets/src-CrmkjRpa.js +1 -0
  89. package/dist/assets/stateDiagram-FHFEXIEX-DW7rOcnQ.js +1 -0
  90. package/dist/assets/stateDiagram-v2-QKLJ7IA2-Jm-24vQ2.js +1 -0
  91. package/dist/assets/timeline-definition-GMOUNBTQ-DVdHyzxS.js +120 -0
  92. package/dist/assets/treeView-SZITEDCU-DPKseaET.js +1 -0
  93. package/dist/assets/treemap-W4RFUUIX-DH-7GZe_.js +1 -0
  94. package/dist/assets/vennDiagram-DHZGUBPP-DJaC6xmI.js +34 -0
  95. package/dist/assets/wardley-RL74JXVD-AgyXyBN5.js +1 -0
  96. package/dist/assets/wardleyDiagram-NUSXRM2D-CTKERPKv.js +20 -0
  97. package/dist/assets/xychartDiagram-5P7HB3ND-BuExiLXc.js +7 -0
  98. package/{index.html → dist/index.html} +2 -1
  99. package/dist/index.js +2539 -0
  100. package/package.json +11 -1
  101. package/.agents/skills/remotion-best-practices/SKILL.md +0 -61
  102. package/.agents/skills/remotion-best-practices/rules/3d.md +0 -86
  103. package/.agents/skills/remotion-best-practices/rules/animations.md +0 -27
  104. package/.agents/skills/remotion-best-practices/rules/assets/charts-bar-chart.tsx +0 -178
  105. package/.agents/skills/remotion-best-practices/rules/assets/text-animations-typewriter.tsx +0 -100
  106. package/.agents/skills/remotion-best-practices/rules/assets/text-animations-word-highlight.tsx +0 -108
  107. package/.agents/skills/remotion-best-practices/rules/assets.md +0 -78
  108. package/.agents/skills/remotion-best-practices/rules/audio-visualization.md +0 -198
  109. package/.agents/skills/remotion-best-practices/rules/audio.md +0 -169
  110. package/.agents/skills/remotion-best-practices/rules/calculate-metadata.md +0 -134
  111. package/.agents/skills/remotion-best-practices/rules/can-decode.md +0 -75
  112. package/.agents/skills/remotion-best-practices/rules/charts.md +0 -120
  113. package/.agents/skills/remotion-best-practices/rules/compositions.md +0 -154
  114. package/.agents/skills/remotion-best-practices/rules/display-captions.md +0 -184
  115. package/.agents/skills/remotion-best-practices/rules/extract-frames.md +0 -229
  116. package/.agents/skills/remotion-best-practices/rules/ffmpeg.md +0 -38
  117. package/.agents/skills/remotion-best-practices/rules/fonts.md +0 -152
  118. package/.agents/skills/remotion-best-practices/rules/get-audio-duration.md +0 -58
  119. package/.agents/skills/remotion-best-practices/rules/get-video-dimensions.md +0 -68
  120. package/.agents/skills/remotion-best-practices/rules/get-video-duration.md +0 -60
  121. package/.agents/skills/remotion-best-practices/rules/gifs.md +0 -141
  122. package/.agents/skills/remotion-best-practices/rules/images.md +0 -134
  123. package/.agents/skills/remotion-best-practices/rules/import-srt-captions.md +0 -69
  124. package/.agents/skills/remotion-best-practices/rules/light-leaks.md +0 -73
  125. package/.agents/skills/remotion-best-practices/rules/lottie.md +0 -70
  126. package/.agents/skills/remotion-best-practices/rules/maps.md +0 -412
  127. package/.agents/skills/remotion-best-practices/rules/measuring-dom-nodes.md +0 -34
  128. package/.agents/skills/remotion-best-practices/rules/measuring-text.md +0 -140
  129. package/.agents/skills/remotion-best-practices/rules/parameters.md +0 -109
  130. package/.agents/skills/remotion-best-practices/rules/sequencing.md +0 -118
  131. package/.agents/skills/remotion-best-practices/rules/sfx.md +0 -26
  132. package/.agents/skills/remotion-best-practices/rules/subtitles.md +0 -36
  133. package/.agents/skills/remotion-best-practices/rules/tailwind.md +0 -11
  134. package/.agents/skills/remotion-best-practices/rules/text-animations.md +0 -20
  135. package/.agents/skills/remotion-best-practices/rules/timing.md +0 -179
  136. package/.agents/skills/remotion-best-practices/rules/transcribe-captions.md +0 -70
  137. package/.agents/skills/remotion-best-practices/rules/transitions.md +0 -197
  138. package/.agents/skills/remotion-best-practices/rules/transparent-videos.md +0 -106
  139. package/.agents/skills/remotion-best-practices/rules/trimming.md +0 -51
  140. package/.agents/skills/remotion-best-practices/rules/videos.md +0 -171
  141. package/.agents/skills/remotion-best-practices/rules/voiceover.md +0 -99
  142. package/.agents/skills/simple/SKILL.md +0 -52
  143. package/.agents/skills/vercel-react-best-practices/AGENTS.md +0 -3254
  144. package/.agents/skills/vercel-react-best-practices/README.md +0 -123
  145. package/.agents/skills/vercel-react-best-practices/SKILL.md +0 -141
  146. package/.agents/skills/vercel-react-best-practices/rules/advanced-event-handler-refs.md +0 -55
  147. package/.agents/skills/vercel-react-best-practices/rules/advanced-init-once.md +0 -42
  148. package/.agents/skills/vercel-react-best-practices/rules/advanced-use-latest.md +0 -39
  149. package/.agents/skills/vercel-react-best-practices/rules/async-api-routes.md +0 -38
  150. package/.agents/skills/vercel-react-best-practices/rules/async-defer-await.md +0 -80
  151. package/.agents/skills/vercel-react-best-practices/rules/async-dependencies.md +0 -51
  152. package/.agents/skills/vercel-react-best-practices/rules/async-parallel.md +0 -28
  153. package/.agents/skills/vercel-react-best-practices/rules/async-suspense-boundaries.md +0 -99
  154. package/.agents/skills/vercel-react-best-practices/rules/bundle-barrel-imports.md +0 -59
  155. package/.agents/skills/vercel-react-best-practices/rules/bundle-conditional.md +0 -31
  156. package/.agents/skills/vercel-react-best-practices/rules/bundle-defer-third-party.md +0 -49
  157. package/.agents/skills/vercel-react-best-practices/rules/bundle-dynamic-imports.md +0 -35
  158. package/.agents/skills/vercel-react-best-practices/rules/bundle-preload.md +0 -50
  159. package/.agents/skills/vercel-react-best-practices/rules/client-event-listeners.md +0 -74
  160. package/.agents/skills/vercel-react-best-practices/rules/client-localstorage-schema.md +0 -71
  161. package/.agents/skills/vercel-react-best-practices/rules/client-passive-event-listeners.md +0 -48
  162. package/.agents/skills/vercel-react-best-practices/rules/client-swr-dedup.md +0 -56
  163. package/.agents/skills/vercel-react-best-practices/rules/js-batch-dom-css.md +0 -107
  164. package/.agents/skills/vercel-react-best-practices/rules/js-cache-function-results.md +0 -80
  165. package/.agents/skills/vercel-react-best-practices/rules/js-cache-property-access.md +0 -28
  166. package/.agents/skills/vercel-react-best-practices/rules/js-cache-storage.md +0 -70
  167. package/.agents/skills/vercel-react-best-practices/rules/js-combine-iterations.md +0 -32
  168. package/.agents/skills/vercel-react-best-practices/rules/js-early-exit.md +0 -50
  169. package/.agents/skills/vercel-react-best-practices/rules/js-flatmap-filter.md +0 -60
  170. package/.agents/skills/vercel-react-best-practices/rules/js-hoist-regexp.md +0 -45
  171. package/.agents/skills/vercel-react-best-practices/rules/js-index-maps.md +0 -37
  172. package/.agents/skills/vercel-react-best-practices/rules/js-length-check-first.md +0 -49
  173. package/.agents/skills/vercel-react-best-practices/rules/js-min-max-loop.md +0 -82
  174. package/.agents/skills/vercel-react-best-practices/rules/js-set-map-lookups.md +0 -24
  175. package/.agents/skills/vercel-react-best-practices/rules/js-tosorted-immutable.md +0 -57
  176. package/.agents/skills/vercel-react-best-practices/rules/rendering-activity.md +0 -26
  177. package/.agents/skills/vercel-react-best-practices/rules/rendering-animate-svg-wrapper.md +0 -47
  178. package/.agents/skills/vercel-react-best-practices/rules/rendering-conditional-render.md +0 -40
  179. package/.agents/skills/vercel-react-best-practices/rules/rendering-content-visibility.md +0 -38
  180. package/.agents/skills/vercel-react-best-practices/rules/rendering-hoist-jsx.md +0 -46
  181. package/.agents/skills/vercel-react-best-practices/rules/rendering-hydration-no-flicker.md +0 -82
  182. package/.agents/skills/vercel-react-best-practices/rules/rendering-hydration-suppress-warning.md +0 -30
  183. package/.agents/skills/vercel-react-best-practices/rules/rendering-resource-hints.md +0 -85
  184. package/.agents/skills/vercel-react-best-practices/rules/rendering-script-defer-async.md +0 -68
  185. package/.agents/skills/vercel-react-best-practices/rules/rendering-svg-precision.md +0 -28
  186. package/.agents/skills/vercel-react-best-practices/rules/rendering-usetransition-loading.md +0 -75
  187. package/.agents/skills/vercel-react-best-practices/rules/rerender-defer-reads.md +0 -39
  188. package/.agents/skills/vercel-react-best-practices/rules/rerender-dependencies.md +0 -45
  189. package/.agents/skills/vercel-react-best-practices/rules/rerender-derived-state-no-effect.md +0 -40
  190. package/.agents/skills/vercel-react-best-practices/rules/rerender-derived-state.md +0 -29
  191. package/.agents/skills/vercel-react-best-practices/rules/rerender-functional-setstate.md +0 -74
  192. package/.agents/skills/vercel-react-best-practices/rules/rerender-lazy-state-init.md +0 -58
  193. package/.agents/skills/vercel-react-best-practices/rules/rerender-memo-with-default-value.md +0 -38
  194. package/.agents/skills/vercel-react-best-practices/rules/rerender-memo.md +0 -44
  195. package/.agents/skills/vercel-react-best-practices/rules/rerender-move-effect-to-event.md +0 -45
  196. package/.agents/skills/vercel-react-best-practices/rules/rerender-no-inline-components.md +0 -82
  197. package/.agents/skills/vercel-react-best-practices/rules/rerender-simple-expression-in-memo.md +0 -35
  198. package/.agents/skills/vercel-react-best-practices/rules/rerender-transitions.md +0 -40
  199. package/.agents/skills/vercel-react-best-practices/rules/rerender-use-ref-transient-values.md +0 -73
  200. package/.agents/skills/vercel-react-best-practices/rules/server-after-nonblocking.md +0 -73
  201. package/.agents/skills/vercel-react-best-practices/rules/server-auth-actions.md +0 -96
  202. package/.agents/skills/vercel-react-best-practices/rules/server-cache-lru.md +0 -41
  203. package/.agents/skills/vercel-react-best-practices/rules/server-cache-react.md +0 -76
  204. package/.agents/skills/vercel-react-best-practices/rules/server-dedup-props.md +0 -65
  205. package/.agents/skills/vercel-react-best-practices/rules/server-hoist-static-io.md +0 -142
  206. package/.agents/skills/vercel-react-best-practices/rules/server-parallel-fetching.md +0 -83
  207. package/.agents/skills/vercel-react-best-practices/rules/server-serialization.md +0 -38
  208. package/.claude/CLAUDE.md +0 -184
  209. package/.claude/commands/review.md +0 -120
  210. package/.claude/commands/sync-docs.md +0 -71
  211. package/.claude/roadmap.md +0 -121
  212. package/.claude/rules/style-guide.md +0 -830
  213. package/.claude/settings.json +0 -18
  214. package/.claude/user-stories.md +0 -333
  215. package/AGENTS.md +0 -68
  216. package/Makefile +0 -32
  217. package/biome.json +0 -79
  218. package/bun.lock +0 -854
  219. package/bunfig.toml +0 -2
  220. package/docs/design.md +0 -563
  221. package/docs/perf-baseline.md +0 -130
  222. package/docs/plans/2026-03-13-client-mode-design.md +0 -86
  223. package/docs/plans/2026-03-13-client-mode-plan.md +0 -605
  224. package/docs/plans/2026-03-13-keyboard-shortcuts-design.md +0 -129
  225. package/docs/plans/2026-03-13-keyboard-shortcuts-plan.md +0 -1471
  226. package/docs/plans/2026-03-13-multi-document-design.md +0 -183
  227. package/docs/plans/2026-03-13-performance-benchmarks-design.md +0 -121
  228. package/docs/superpowers/plans/2026-03-26-surgical-pruning.md +0 -1176
  229. package/docs/superpowers/specs/2026-03-27-go-server-rewrite-design.md +0 -284
  230. package/e2e/comments.spec.ts +0 -81
  231. package/e2e/document-load.spec.ts +0 -32
  232. package/e2e/export.spec.ts +0 -58
  233. package/e2e/fixtures/sample.md +0 -7
  234. package/e2e/perf/add-comment.spec.ts +0 -116
  235. package/e2e/perf/fixtures/generate.ts +0 -327
  236. package/e2e/perf/initial-load.spec.ts +0 -49
  237. package/e2e/perf/perf.setup.ts +0 -23
  238. package/e2e/perf/perf.teardown.ts +0 -9
  239. package/e2e/perf/screenshot-final.png +0 -0
  240. package/e2e/perf/scroll.spec.ts +0 -39
  241. package/e2e/perf/tab-switch.spec.ts +0 -69
  242. package/e2e/perf/text-selection.spec.ts +0 -119
  243. package/e2e/perf/utils/metrics.ts +0 -350
  244. package/e2e/perf/utils/perf-cli.ts +0 -86
  245. package/e2e/persistence-file.spec.ts +0 -357
  246. package/e2e/utils/cli.ts +0 -84
  247. package/e2e/utils/selection.ts +0 -79
  248. package/go/cmd/readit/main.go +0 -416
  249. package/go/go.mod +0 -20
  250. package/go/go.sum +0 -41
  251. package/go/internal/server/anchor.go +0 -302
  252. package/go/internal/server/anchor_test.go +0 -111
  253. package/go/internal/server/comments.go +0 -390
  254. package/go/internal/server/documents.go +0 -113
  255. package/go/internal/server/embed.go +0 -17
  256. package/go/internal/server/headings.go +0 -33
  257. package/go/internal/server/headings_test.go +0 -75
  258. package/go/internal/server/htmltext.go +0 -123
  259. package/go/internal/server/markdown.go +0 -157
  260. package/go/internal/server/markdown_bench_test.go +0 -42
  261. package/go/internal/server/markdown_test.go +0 -79
  262. package/go/internal/server/server.go +0 -453
  263. package/go/internal/server/server_bench_test.go +0 -122
  264. package/go/internal/server/settings.go +0 -110
  265. package/go/internal/server/sse.go +0 -140
  266. package/go/internal/server/storage.go +0 -275
  267. package/go/internal/server/storage_test.go +0 -152
  268. package/go/internal/server/template.go +0 -66
  269. package/go/internal/server/types.go +0 -101
  270. package/go/internal/server/watcher.go +0 -74
  271. package/lefthook.yml +0 -8
  272. package/nvim-readit/lua/readit/health.lua +0 -64
  273. package/nvim-readit/lua/readit/init.lua +0 -463
  274. package/nvim-readit/plugin/readit.lua +0 -19
  275. package/playwright.config.ts +0 -34
  276. package/skills-lock.json +0 -20
  277. package/src/App.svelte +0 -890
  278. package/src/cli.ts +0 -881
  279. package/src/components/ActionsMenu.svelte +0 -95
  280. package/src/components/CommentBadge.svelte +0 -67
  281. package/src/components/CommentErrorBanner.svelte +0 -33
  282. package/src/components/CommentInput.svelte +0 -75
  283. package/src/components/CommentListItem.svelte +0 -95
  284. package/src/components/CommentManager.svelte +0 -129
  285. package/src/components/CommentNav.svelte +0 -109
  286. package/src/components/DocumentViewer.svelte +0 -233
  287. package/src/components/FloatingComment.svelte +0 -107
  288. package/src/components/Header.svelte +0 -76
  289. package/src/components/InlineEditor.svelte +0 -72
  290. package/src/components/MarginNote.svelte +0 -167
  291. package/src/components/MarginNotesContainer.svelte +0 -33
  292. package/src/components/MermaidEnhancer.svelte +0 -218
  293. package/src/components/MermaidModal.svelte +0 -67
  294. package/src/components/RawModal.svelte +0 -126
  295. package/src/components/ReanchorConfirm.svelte +0 -30
  296. package/src/components/SettingsModal.svelte +0 -220
  297. package/src/components/ShortcutCapture.svelte +0 -82
  298. package/src/components/ShortcutList.svelte +0 -145
  299. package/src/components/TabBar.svelte +0 -52
  300. package/src/components/TableOfContents.svelte +0 -125
  301. package/src/components/ui/ActionLink.svelte +0 -40
  302. package/src/components/ui/Button.svelte +0 -53
  303. package/src/components/ui/Dialog.svelte +0 -97
  304. package/src/components/ui/DropdownMenu.svelte +0 -85
  305. package/src/components/ui/DropdownMenuItem.svelte +0 -38
  306. package/src/components/ui/DropdownMenuSeparator.svelte +0 -11
  307. package/src/components/ui/Text.svelte +0 -42
  308. package/src/env.d.ts +0 -6
  309. package/src/index.css +0 -859
  310. package/src/lib/__fixtures__/bench-data.ts +0 -114
  311. package/src/lib/anchor.bench.ts +0 -91
  312. package/src/lib/anchor.test.ts +0 -527
  313. package/src/lib/anchor.ts +0 -381
  314. package/src/lib/comment-storage.bench.ts +0 -49
  315. package/src/lib/comment-storage.test.ts +0 -694
  316. package/src/lib/comment-storage.ts +0 -226
  317. package/src/lib/export.bench.ts +0 -21
  318. package/src/lib/export.ts +0 -36
  319. package/src/lib/fetch-or-throw.test.ts +0 -59
  320. package/src/lib/fetch-or-throw.ts +0 -12
  321. package/src/lib/headings.test.ts +0 -103
  322. package/src/lib/headings.ts +0 -44
  323. package/src/lib/highlight/core.test.ts +0 -93
  324. package/src/lib/highlight/dom.ts +0 -187
  325. package/src/lib/highlight/highlight-registry.ts +0 -221
  326. package/src/lib/highlight/highlight.bench.ts +0 -92
  327. package/src/lib/highlight/highlighter.ts +0 -247
  328. package/src/lib/highlight/resolver.ts +0 -38
  329. package/src/lib/highlight/types.ts +0 -17
  330. package/src/lib/html-text.test.ts +0 -162
  331. package/src/lib/html-text.ts +0 -161
  332. package/src/lib/i18n/en.ts +0 -124
  333. package/src/lib/i18n/index.ts +0 -3
  334. package/src/lib/i18n/ja.ts +0 -126
  335. package/src/lib/i18n/translations.ts +0 -27
  336. package/src/lib/i18n/types.ts +0 -130
  337. package/src/lib/key-lock.test.ts +0 -104
  338. package/src/lib/key-lock.ts +0 -23
  339. package/src/lib/margin-layout.bench.ts +0 -61
  340. package/src/lib/margin-layout.ts +0 -71
  341. package/src/lib/markdown-renderer.test.ts +0 -154
  342. package/src/lib/markdown-renderer.ts +0 -178
  343. package/src/lib/mermaid-config.ts +0 -38
  344. package/src/lib/mermaid-renderer.ts +0 -162
  345. package/src/lib/mermaid-worker.ts +0 -60
  346. package/src/lib/positions.ts +0 -157
  347. package/src/lib/shortcut-registry.ts +0 -244
  348. package/src/lib/utils.ts +0 -15
  349. package/src/main.ts +0 -16
  350. package/src/schema.ts +0 -92
  351. package/src/server.ts +0 -1216
  352. package/src/stores/app.svelte.ts +0 -231
  353. package/src/stores/locale.svelte.ts +0 -46
  354. package/src/stores/settings.svelte.ts +0 -90
  355. package/src/stores/shortcuts.svelte.ts +0 -104
  356. package/src/stores/ui.svelte.ts +0 -12
  357. package/src/template.ts +0 -104
  358. package/src/test-setup.ts +0 -48
  359. package/svelte.config.js +0 -5
  360. package/test.md +0 -74
  361. package/tsconfig.cli.json +0 -12
  362. package/tsconfig.json +0 -20
  363. package/vite.config.ts +0 -47
  364. package/vitest.config.ts +0 -15
  365. package/vscode-readit/.mcp.json +0 -7
  366. package/vscode-readit/.vscodeignore +0 -7
  367. package/vscode-readit/bun.lock +0 -78
  368. package/vscode-readit/icon.svg +0 -10
  369. package/vscode-readit/package.json +0 -110
  370. package/vscode-readit/src/extension.ts +0 -117
  371. package/vscode-readit/src/server-manager.ts +0 -272
  372. package/vscode-readit/src/webview-provider.ts +0 -204
  373. package/vscode-readit/tsconfig.json +0 -20
@@ -1,830 +0,0 @@
1
- # Style Guide
2
-
3
- ## 1 Introduction
4
-
5
- Good software is fast, reliable, and beautiful.
6
- Fast software requires focus and clarity.
7
- Reliable software requires simplicity and correctness.
8
- Beautiful software requires attention to detail and aesthetics.
9
-
10
- ### 1.1 Core Philosophy
11
-
12
- - **Obvious over clever** - Code should be immediately understandable and easy to reason about. The wtf metric should be less than 1.
13
- - **Code is a liability** - Less code means fewer bugs, easier maintenance, and faster comprehension. Also, well written code is usually shorter, since it's much more focused and concise.
14
- - **Type safety without overhead** - Leverage TypeScript without runtime cost
15
- - **Work with the language, not against it** - Prefer native JavaScript patterns over complex type gymnastics. My aversion to complex libraries like rxjs, effect-ts, functional-ts stems from the fact that force the writer a "non-native" way of thinking about the language.
16
- - **Immutability by default** - Prevent bugs through data flow clarity
17
- - **Composition over inheritance** - Build complex behavior from simple, reusable parts
18
- - **Fail fast, recover explicitly** - Make error states visible and recoverable
19
- - **Duplication over wrong abstraction** - Write it twice, abstract on the third time
20
- - **Practicality over rigid rules** - Adapt patterns to the problem at hand
21
- - **Tight, clean implementations** - Functions should do their job without ceremony or overhead
22
-
23
- ## 2 Type Design
24
-
25
- ### 2.1 Parse, Don't Validate
26
-
27
- **Why:** Transform data into types that make invalid states impossible, but stay pragmatic with native JavaScript types.
28
-
29
- ```ts
30
- // Good - parse into a stronger type, but keep it simple
31
- function parseUserId(input: unknown): string {
32
- if (typeof input !== 'string' || !input.startsWith('user_')) {
33
- throw new Error('Invalid user ID format');
34
- }
35
- return input; // Now we know it's a valid user ID
36
- }
37
-
38
- // Good - use native JavaScript types
39
- function parseDate(input: string): Date {
40
- const date = new Date(input);
41
- if (isNaN(date.getTime())) {
42
- throw new Error('Invalid date');
43
- }
44
- return date;
45
- }
46
-
47
- // Bad - bespoke utility types that fight the language
48
- type ParsedString<T extends string> = T extends `${infer P}` ? P : never;
49
- type ValidatedNumber<Min extends number, Max extends number> = number & {
50
- __min: Min;
51
- __max: Max;
52
- };
53
-
54
- // Bad - validate repeatedly instead of parsing once
55
- function processUser(userId: string) {
56
- if (!isValidUserId(userId)) throw new Error();
57
- // ... later in code
58
- if (!isValidUserId(userId)) throw new Error(); // Validating again!
59
- }
60
- ```
61
-
62
- ### 2.2 Discriminated Unions Over Classes
63
-
64
- **Why:** Exhaustive checking, no inheritance complexity, better tree-shaking.
65
-
66
- ```ts
67
- // Good
68
- type State =
69
- | { status: 'idle' }
70
- | { status: 'loading' }
71
- | { status: 'success'; data: User[] }
72
- | { status: 'error'; error: Error };
73
-
74
- // Bad - classes add unnecessary complexity
75
- class IdleState {}
76
- class LoadingState {}
77
- class SuccessState { constructor(public data: User[]) {} }
78
- ```
79
-
80
- Exception: use classes only for stateful services with multiple collaborators (dependency injection). Do not model state machines or data with classes. See 8.1.
81
-
82
- ### 2.3 Null vs Undefined Policy
83
-
84
- - Prefer `undefined` for optionals; optional means possibly `undefined`, not `null`.
85
- - Normalize external `null` at the boundary (e.g., request parsing, DB hydration).
86
- - `tsconfig`: ensure `exactOptionalPropertyTypes: true`.
87
-
88
- ## 3 Function Design
89
-
90
- ### 3.1 Simple Over Complex
91
-
92
- **Why:** Prefer simple, imperative solutions over complex functional abstractions. Every line of code is a potential bug and requires maintenance.
93
-
94
- ```ts
95
- // Good - simple, imperative, easy to follow
96
- async function processUsers(users: User[]) {
97
- const results = [] as ProcessedUser[];
98
- for (const user of users) {
99
- if (user.isActive) {
100
- const processed = await processUser(user);
101
- results.push(processed);
102
- }
103
- }
104
- return results;
105
- }
106
-
107
- // Bad - complex functional approach with custom types
108
- const processUsersFancy = pipe(
109
- filter(isActive),
110
- traverseArray(Task.of),
111
- map(processUser),
112
- sequence(Task),
113
- fold(
114
- onError(handleError),
115
- onSuccess(identity)
116
- )
117
- );
118
-
119
- // Bad - TypeScript gymnastics that fight the language
120
- type DeepPartial<T> = T extends object ? { [P in keyof T]?: DeepPartial<T[P]> } : T;
121
-
122
- type RecursiveRequired<T> = T extends object ? { [P in keyof T]-?: RecursiveRequired<T[P]> } : T;
123
-
124
- // Good - use built-in types or simple solutions
125
- type UserUpdate = Partial<User>;
126
- ```
127
-
128
- ### 3.2 Balanced Function Decomposition
129
-
130
- **Why:** Balance between monolithic functions and over-abstraction. Extract functions only when they encapsulate meaningful complexity or represent distinct business operations.
131
-
132
- Extract when:
133
-
134
- - Function encapsulates 30+ lines of complex logic
135
- - Represents a distinct business operation
136
- - Has multiple responsibilities that can be separated
137
- - Contains complex error handling or transaction management
138
- - Introduces a second abstraction level (loops inside loops, nested conditions)
139
-
140
- Inline when:
141
-
142
- - Simple string manipulation (`name.replace(/_id_seq$/, "")`)
143
- - Basic array operations (`array.filter(condition)`)
144
- - Trivial database query wrappers
145
- - One-line utility functions
146
- - Operations used only once
147
-
148
- ```ts
149
- // Bad - excessive abstractions for trivial operations
150
- function parseSequenceName(name: string): string {
151
- return name.replace(/_id_seq$/, "");
152
- }
153
-
154
- function findCommonColumns(source: string[], target: string[]): string[] {
155
- return source.filter(col => target.includes(col));
156
- }
157
-
158
- // Good - inline trivial operations
159
- async function resetSequences(targetClient: PoolClient): Promise<void> {
160
- const sequencesResult = await targetClient.query(/* ... */);
161
-
162
- for (const seq of sequencesResult.rows) {
163
- const tableName = seq.sequence_name.replace(/_id_seq$/, ""); // Inline simple operations
164
- await targetClient.query(/* ... */);
165
- }
166
- }
167
-
168
- // Good - extract when there's meaningful complexity
169
- async function syncTableData(
170
- sourceClient: PoolClient,
171
- targetClient: PoolClient,
172
- table: string,
173
- signal: AbortSignal,
174
- ): Promise<{ skipped: boolean }> {
175
- // 40+ lines of complex column mapping, data streaming, error handling
176
- // This encapsulates meaningful business logic
177
- return { skipped: false };
178
- }
179
-
180
- // Bad - monolithic function doing too much
181
- function seedDatabaseAndValidateAndLogAndEmailAndCreateBackup() {
182
- // 230+ lines of nested logic...
183
- }
184
-
185
- // Good - clean orchestration using meaningful business operations
186
- async function seedFromStaging(signal: AbortSignal): Promise<void> {
187
- let sourceClient: PoolClient | null = null;
188
- let targetClient: PoolClient | null = null;
189
-
190
- try {
191
- const sourcePool = new Pool({ connectionString: config.staging.directUrl });
192
- sourceClient = await sourcePool.connect();
193
-
194
- // Clear orchestration of business operations
195
- for (const table of tables) {
196
- await syncTableData(sourceClient, targetClient!, table, signal);
197
- }
198
- await resetSequences(targetClient!);
199
- await createSupabaseUsers(targetClient!);
200
- } finally {
201
- // ...
202
- }
203
- }
204
- ```
205
-
206
- ### 3.3 Early Returns and Guard Clauses
207
-
208
- **Why:** Reduces nesting, puts happy path at root indentation, fails fast.
209
-
210
- ```ts
211
- // Good
212
- function processUser(user: User | undefined): ProcessedUser {
213
- if (!user) {
214
- throw new Error('User is required');
215
- }
216
-
217
- if (!user.isActive) {
218
- throw new Error('User is inactive');
219
- }
220
-
221
- // Happy path at root indentation
222
- return transform(user);
223
- }
224
-
225
- // Bad - nested pyramid of doom
226
- function processUserNested(user: User | undefined): ProcessedUser {
227
- if (user) {
228
- if (user.isActive) {
229
- return transform(user);
230
- } else {
231
- throw new Error('User is inactive');
232
- }
233
- } else {
234
- throw new Error('User is required');
235
- }
236
- }
237
- ```
238
-
239
- #### 3.3.1 Prefer Early Returns Over Accumulating State with let
240
-
241
- Avoid patterns where you declare `let` variables and then mutate them across large `if/else` blocks. Instead, compute and return early for each branch; this keeps logic linear and types narrower.
242
-
243
- ```ts
244
- // Bad - accumulating state with let + if/else chains
245
- let template: Template | null = null;
246
- let sections: Section[] = [];
247
- if (forcedTemplateId) {
248
- template = await fetchTemplate(forcedTemplateId);
249
- sections = await fetchSections(template.id);
250
- } else {
251
- const inferred = await inferTemplate(transcript);
252
- template = await fetchTemplate(inferred.id);
253
- sections = await fetchSections(template.id);
254
- }
255
- return { template: template!, sections };
256
-
257
- // Good - early returns per branch
258
- if (forcedTemplateId) {
259
- const template = await fetchTemplate(forcedTemplateId);
260
- const sections = await fetchSections(template.id);
261
- return { template, sections };
262
- }
263
- const inferred = await inferTemplate(transcript);
264
- const template = await fetchTemplate(inferred.id);
265
- const sections = await fetchSections(template.id);
266
- return { template, sections };
267
- ```
268
-
269
- ### 3.4 Naming Conventions
270
-
271
- **Why:** Clear, concise names make code self-documenting. Avoid redundancy and be specific.
272
-
273
- ```ts
274
- // Good - concise, clear names
275
- interface User {
276
- id: string;
277
- email: string;
278
- isActive: boolean;
279
- }
280
-
281
- function getUser(id: string): User { /* sync, O(1) lookup only */ }
282
- async function fetchUser(id: string): Promise<User> { /* I/O */ }
283
- async function loadDashboard(id: string): Promise<Dashboard> { /* I/O + compose */ }
284
- async function saveUser(id: string, data: Partial<User>): Promise<void> { /* writes */ }
285
-
286
- // Boolean naming - use is/has/can/should prefixes
287
- const isActive = user.status === 'active';
288
- const hasPermission = user.roles.includes('admin');
289
- const canEdit = user.permissions.edit;
290
- const shouldRetry = attempts < maxRetries;
291
-
292
- // Time units - suffix with Ms for numbers representing milliseconds
293
- const retryDelayMs = 250;
294
- const timeoutMs = 5_000;
295
-
296
- // Bad - redundant or unclear names
297
- interface UserInterface { } // Don't suffix interfaces
298
- class UserClass { } // Don't suffix classes
299
- type UserType = { } // Don't suffix types
300
-
301
- function getUserById(userId: string) { } // Prefer this shape only when multiple selectors exist (e.g. getUser, getUserByEmail)
302
- function processUserDataAndSave() { } // Too vague, doing multiple things
303
- function doStuff() { } // Meaningless
304
-
305
- // Bad - Hungarian notation or type prefixes
306
- const strName = 'John'; // Type is obvious from TypeScript
307
- const arrUsers = []; // Don't encode type in name
308
- const objConfig = {}; // Let TypeScript handle types
309
-
310
- // Good - array naming is plural, clear what it contains
311
- const users: User[] = [];
312
- const activeUsers = users.filter(u => u.isActive);
313
-
314
- // Bad - unclear array naming
315
- const userArray: User[] = [];
316
- const data = users.filter(u => u.isActive); // What data?
317
- ```
318
-
319
- ### 3.5 Object Destructuring for Opaque Parameters
320
-
321
- **Why:** When a parameter's type (e.g., `string`, `number`) doesn't convey its purpose, use object destructuring to make the function self-documenting.
322
-
323
- ```ts
324
- // Good - parameter name is part of the signature
325
- function subscribe({ eventId }: { eventId: string }): void { }
326
- function fetchUser({ userId }: { userId: string }): Promise<User> { }
327
- function cancelBot({ eventId }: { eventId: string }): Promise<void> { }
328
-
329
- // Bad - "string" tells you nothing
330
- function subscribe(eventId: string): void { }
331
- function fetchUser(userId: string): Promise<User> { }
332
- function cancelBot(eventId: string): Promise<void> { }
333
- ```
334
-
335
- **When to apply:**
336
-
337
- - Single string/number parameters where the type doesn't describe the value
338
- - Multiple parameters of the same type (avoids argument order confusion)
339
- - Public API boundaries (interfaces, exported functions)
340
-
341
- **When to skip:**
342
-
343
- - Private helper methods with obvious context
344
- - Standard patterns like `formatDate(date: Date)` where the type is descriptive
345
- - Primitive math utilities like `clamp(value: number, min: number, max: number)`
346
-
347
- ## 4 Error Handling
348
-
349
- ### 4.1 Single Try-Catch for Related Operations
350
-
351
- **Why:** Group related operations in a single try-catch block for cleaner error handling. Avoid multiple try-catch blocks at the same level - extract operations to separate functions instead.
352
-
353
- ```ts
354
- // Good - single try-catch for related operations
355
- async function disconnectCalendar(calendarId: string) {
356
- try {
357
- const calendar = await getCalendar(calendarId);
358
- if (!calendar) {
359
- throw new NotFoundError('Calendar not found');
360
- }
361
-
362
- const events = await getEvents(calendar.id);
363
-
364
- // All related operations in one try block
365
- for (const event of events) {
366
- if (event.id) {
367
- await cancelBot(event.id);
368
- await deleteEvent(event.id);
369
- }
370
- }
371
-
372
- await deleteCalendar(calendar.id);
373
- await logAuditEvent(calendar);
374
-
375
- return { success: true, message: 'Calendar disconnected' };
376
- } catch (error) {
377
- logger.error(error, '[disconnect-calendar] error');
378
-
379
- if (error instanceof NotFoundError) {
380
- return { error: error.message, status: 404 };
381
- }
382
-
383
- if (error instanceof BadRequestError) {
384
- return { error: error.message, status: 400 };
385
- }
386
-
387
- return { error: 'Internal server error', status: 500 };
388
- }
389
- }
390
-
391
- // Good - extract unrelated operations to separate functions
392
- async function setupUser(userData: UserData) {
393
- const user = await createUser(userData);
394
-
395
- // Handle optional operations separately
396
- await sendWelcomeEmailSafely(user.email);
397
-
398
- return user;
399
- }
400
-
401
- async function sendWelcomeEmailSafely(email: string) {
402
- try {
403
- await sendWelcomeEmail(email);
404
- } catch (error) {
405
- logger.warn(`Failed to send welcome email to ${email}: ${(error as Error).message}`);
406
- }
407
- }
408
-
409
- // Bad - multiple try-catch blocks for related operations
410
- async function disconnectCalendarManyTrys(calendarId: string) {
411
- let calendar: Calendar;
412
- try {
413
- calendar = await getCalendar(calendarId);
414
- } catch (error) {
415
- return { error: 'Failed to get calendar' };
416
- }
417
-
418
- let events: Event[];
419
- try {
420
- events = await getEvents(calendar.id);
421
- } catch (error) {
422
- return { error: 'Failed to get events' };
423
- }
424
- }
425
- ```
426
-
427
- ## 5 Data Manipulation
428
-
429
- ### 5.1 Immutable Updates vs Imperative Operations
430
-
431
- **Why:** Use immutable updates for data transformations, but imperative patterns are often cleaner for business operations with side effects.
432
-
433
- ```ts
434
- // Good - immutable updates for data transformations
435
- function updateUser(user: User, updates: Partial<User>): User {
436
- return { ...user, ...updates };
437
- }
438
-
439
- // Good - nested immutable updates for pure data operations
440
- function addItemToCart(cart: Cart, item: Item): Cart {
441
- return {
442
- ...cart,
443
- items: [...cart.items, item],
444
- total: cart.total + item.price,
445
- };
446
- }
447
-
448
- // Good - imperative for complex validation with early returns
449
- function validateUserData(data: unknown): User {
450
- if (!data || typeof data !== 'object') {
451
- throw new Error('Invalid data format');
452
- }
453
-
454
- const obj = data as Record<string, unknown>;
455
-
456
- if (!obj.email || typeof obj.email !== 'string') {
457
- throw new Error('Email is required');
458
- }
459
-
460
- if (!obj.name || typeof obj.name !== 'string') {
461
- throw new Error('Name is required');
462
- }
463
-
464
- return { email: obj.email, name: obj.name } as User;
465
- }
466
- ```
467
-
468
- ### 5.2 Choose the Right Iteration Pattern
469
-
470
- **Why:** Different patterns work better in different contexts. Use array methods for pure transformations, loops for side effects and async operations.
471
-
472
- ```ts
473
- // Good - array methods for pure data transformations
474
- const activeAdminEmails = users
475
- .filter(u => u.isActive)
476
- .filter(u => u.role === 'admin')
477
- .map(u => u.email);
478
-
479
- // Good - for loops for async operations with side effects
480
- for (const event of eventsToDelete) {
481
- if (event.id) {
482
- await botsDomain.cancelBotForEvent(event.id);
483
- await calendarStorage.deleteEvent({ eventId: event.id });
484
- }
485
- }
486
-
487
- // Good - for loops when you need sequential processing
488
- for (const user of users) {
489
- if (await shouldProcessUser(user)) {
490
- await processUser(user);
491
- await logUserProcessed(user);
492
- }
493
- }
494
-
495
- // Good - batched processing for high volume
496
- const batchSize = 5;
497
- for (let i = 0; i < users.length; i += batchSize) {
498
- const batch = users.slice(i, i + batchSize);
499
- await Promise.all(batch.map(processUser));
500
- }
501
-
502
- // Bad - array methods that force Promise.all when you need sequential
503
- await Promise.all(
504
- users.map(async user => {
505
- if (await shouldProcessUser(user)) {
506
- await processUser(user); // This runs in parallel, might overwhelm DB
507
- }
508
- })
509
- );
510
-
511
- // Bad - forEach with async callbacks
512
- users.forEach(async user => {
513
- await processUser(user); // fire-and-forget, order not guaranteed
514
- });
515
- ```
516
-
517
- ## 6 Type Utilities
518
-
519
- ### 6.1 Type Predicates for Narrowing
520
-
521
- **Why:** Provides type-safe runtime checks that TypeScript understands.
522
-
523
- ```ts
524
- // Good - type predicate
525
- function isUser(value: unknown): value is User {
526
- return typeof value === 'object' &&
527
- value !== null &&
528
- 'id' in value &&
529
- 'email' in value;
530
- }
531
-
532
- function isError(value: unknown): value is Error {
533
- return value instanceof Error;
534
- }
535
-
536
- // Usage - TypeScript narrows the type
537
- if (isUser(data)) {
538
- console.log(data.email); // TypeScript knows data is User
539
- }
540
- ```
541
-
542
- ### 6.2 Const Assertions
543
-
544
- **Why:** Narrowest possible types without explicit annotation.
545
-
546
- ```ts
547
- // Good
548
- const ROLES = ['admin', 'user', 'guest'] as const;
549
- type Role = typeof ROLES[number]; // 'admin' | 'user' | 'guest'
550
-
551
- // Bad - loses type information
552
- const ROLES2 = ['admin', 'user', 'guest'];
553
- type AnyRole = string; // Too broad
554
- ```
555
-
556
- #### Naming for const-asserted maps and derived unions
557
-
558
- - Keep identifiers distinct for the runtime value and its derived union type (improves readability and import ergonomics).
559
- - Using the same identifier for both a value and a type is valid but discouraged.
560
-
561
- ```ts
562
- // Good - distinct names for value (runtime) and type
563
- export const PublicWorkflowJobTypes = {
564
- TITLE_GENERATION: 'TITLE_GENERATION',
565
- // ... other entries ...
566
- } as const;
567
-
568
- export type PublicWorkflowJobType =
569
- (typeof PublicWorkflowJobTypes)[keyof typeof PublicWorkflowJobTypes];
570
- ```
571
-
572
- ### 6.3 No Enums - Use Const Object Pattern
573
-
574
- **Why:**
575
-
576
- 1. **Single Source of Truth:** The `const` object serves as the single source for both runtime values and type definitions (via `typeof obj[keyof typeof obj]`). This avoids "magic strings" and ensures that type checking is grounded in actual runtime values, unlike raw string unions where type assertions are often the only check.
577
- 2. **Predictability:** Enums effectively introduce a "custom syntax" on top of JavaScript with unintuitive behaviors (like reverse mappings for numeric enums) that many developers find confusing. `const` objects are just standard JavaScript.
578
- 3. **Performance:** `const` objects compile to simple objects (or plain literals if inlined), ensuring zero runtime overhead and optimal tree-shaking, though the semantic benefits above are the primary driver.
579
-
580
- **Pattern:**
581
-
582
- ```ts
583
- // Good - const object + derived type
584
- export const PublicVideoStatuses = {
585
- UPLOADING: "UPLOADING",
586
- CREATED: "CREATED",
587
- READY: "READY",
588
- ERROR: "ERROR",
589
- ARCHIVED: "ARCHIVED",
590
- } as const;
591
- export type PublicVideoStatus =
592
- (typeof PublicVideoStatuses)[keyof typeof PublicVideoStatuses];
593
-
594
- // Bad - TypeScript enum
595
- enum VideoStatus {
596
- UPLOADING = "UPLOADING",
597
- CREATED = "CREATED",
598
- // ...
599
- }
600
-
601
- // Bad - plain union (no runtime values)
602
- type VideoStatus = "UPLOADING" | "CREATED" | "READY" | "ERROR" | "ARCHIVED";
603
- ```
604
-
605
- **Naming convention:**
606
-
607
- - **Const object**: Plural form (`PublicVideoStatuses`, `PublicJobExecutionStatuses`)
608
- - **Derived type**: Singular form (`PublicVideoStatus`, `PublicJobExecutionStatus`)
609
- - Keep both names distinct for clear imports
610
-
611
- **Usage:**
612
-
613
- ```ts
614
- // Runtime comparison (use the const object)
615
- if (video.status === PublicVideoStatuses.READY) { ... }
616
-
617
- // Type annotation (use the derived type)
618
- function processVideo(status: PublicVideoStatus): void { ... }
619
-
620
- // Exhaustive switch
621
- function getStatusLabel(status: PublicVideoStatus): string {
622
- switch (status) {
623
- case PublicVideoStatuses.UPLOADING: return "アップロード中";
624
- case PublicVideoStatuses.READY: return "準備完了";
625
- // TypeScript ensures all cases are covered
626
- }
627
- }
628
- ```
629
-
630
- **Reference:** See `apps/web/src/lib/domain/` for canonical examples (e.g., `video/video.ts`, `job/job.ts`, `workflow/workflow.ts`).
631
-
632
- ## 7 Async Patterns
633
-
634
- ### 7.1 Async/Await Over Promise Chains
635
-
636
- **Why:** Linear flow, better error handling, easier debugging.
637
-
638
- ```ts
639
- // Good
640
- async function fetchUserData(id: string): Promise<UserData> {
641
- const user = await fetchUser(id);
642
- const profile = await fetchProfile(user.profileId);
643
- return { user, profile };
644
- }
645
-
646
- // Bad - callback hell
647
- function fetchUserDataChained(id: string): Promise<UserData> {
648
- return fetchUser(id)
649
- .then(user => fetchProfile(user.profileId)
650
- .then(profile => ({ user, profile })));
651
- }
652
- ```
653
-
654
- ### 7.2 Sequential vs Parallel Processing
655
-
656
- **Why:** Sequential processing is safer for side effects, parallel processing is better for independent operations. Choose based on the operation's requirements.
657
-
658
- ```ts
659
- // Good - sequential for operations with side effects
660
- async function disconnectCalendar(calendarId: string) {
661
- const eventsToDelete = await getEvents(calendarId);
662
-
663
- // Process events sequentially to avoid overwhelming external APIs
664
- for (const event of eventsToDelete) {
665
- if (event.id) {
666
- await cancelBotForEvent(event.id); // External API call
667
- await deleteEventFromDB(event.id); // Database operation
668
- }
669
- }
670
-
671
- await deleteCalendar(calendarId);
672
- }
673
-
674
- // Bad - parallel processing that can overwhelm external services
675
- async function disconnectCalendarParallel(calendarId: string) {
676
- const eventsToDelete = await getEvents(calendarId);
677
- await Promise.allSettled(
678
- eventsToDelete.map(async event => {
679
- if (event.id) {
680
- await cancelBotForEvent(event.id);
681
- await deleteEventFromDB(event.id);
682
- }
683
- })
684
- );
685
- }
686
- ```
687
-
688
- ## 8 Module Design
689
-
690
- ### 8.1 Dependency Injection Over Singletons
691
-
692
- **Why:** Testable, configurable, explicit dependencies. Classes work well for stateful services with multiple dependencies.
693
-
694
- ```ts
695
- // Good - class-based dependency injection for complex services
696
- export class RecallCalendarsApiImpl implements RecallCalendarsApi {
697
- constructor(
698
- private readonly router: Router = express.Router(),
699
- private readonly calendarStorage: CalendarStorage,
700
- private readonly recallApiClient: RecallApiClient,
701
- private readonly auditLogger: AuditLogger,
702
- private readonly botsDomain: BotsDomain,
703
- private readonly logger: Logger,
704
- ) {}
705
-
706
- async disconnectCalendar(calendarId: string) {
707
- this.logger.debug('[disconnect-calendar] request received');
708
-
709
- try {
710
- const calendar = await this.calendarStorage.getCalendar(calendarId);
711
- const events = await this.calendarStorage.getEvents(calendar.id);
712
-
713
- for (const event of events) {
714
- await this.botsDomain.cancelBot(event.id);
715
- await this.calendarStorage.deleteEvent(event.id);
716
- }
717
-
718
- await this.recallApiClient.deleteCalendar(calendar.recall_id);
719
- await this.auditLogger.log({ action: 'calendar.disconnected' });
720
-
721
- } catch (error) {
722
- this.logger.error(error, '[disconnect-calendar] error');
723
- throw error;
724
- }
725
- }
726
- }
727
-
728
- // Good - functional approach for simpler operations
729
- interface UserRepository {
730
- create(user: User): Promise<User>;
731
- findById(id: string): Promise<User | null>;
732
- }
733
-
734
- interface Logger {
735
- info(message: string): void;
736
- error(error: Error, message: string): void;
737
- }
738
-
739
- async function createUser(
740
- userData: UserData,
741
- userRepo: UserRepository,
742
- logger: Logger,
743
- ): Promise<User> {
744
- logger.info(`Creating user: ${userData.email}`);
745
-
746
- try {
747
- return await userRepo.create(userData);
748
- } catch (error) {
749
- logger.error(error as Error, 'Failed to create user');
750
- throw error;
751
- }
752
- }
753
- ```
754
-
755
- ### 8.3 Exports & File Naming
756
-
757
- - Prefer named exports in backend code; reach for default exports only when the module truly presents a single concept (config, logger, etc.).
758
- - Top-level names: PascalCase for types/classes, camelCase for values.
759
- - One module = one responsibility; avoid `index.ts` barrels unless they truly clarify usage.
760
-
761
- ## 9 Refactoring Approach
762
-
763
- ### 9.1 From Monolith to Balanced Decomposition
764
-
765
- **Why:** Large monolithic functions are hard to understand, but excessive decomposition creates indirection. Strike a balance by extracting functions that represent meaningful business operations.
766
-
767
- ```ts
768
- // Stage 1: Monolithic function (hard to read/maintain)
769
- async function seedDatabase(signal: AbortSignal): Promise<void> {
770
- // 230+ lines of:
771
- // - Database connection logic
772
- // - Table discovery and filtering
773
- // - Complex column mapping per table
774
- // - Data streaming with error handling
775
- // - Sequence resetting logic
776
- // - Supabase user creation with tracking
777
- // - Transaction management
778
- // - Progress reporting
779
- }
780
-
781
- // Stage 2: Over-abstracted (too many trivial helpers)
782
- function parseSequenceName(name: string): string { return name.replace(/_id_seq$/, ""); }
783
- function findCommonColumns(source: string[], target: string[]): string[] { return source.filter(col => target.includes(col)); }
784
- async function getTableColumns(client: PoolClient, table: string): Promise<string[]> { /* Simple query wrapper */ return []; }
785
-
786
- // Stage 3: Balanced decomposition (extract meaningful operations)
787
- async function syncTableData(
788
- sourceClient: PoolClient,
789
- targetClient: PoolClient,
790
- table: string,
791
- signal: AbortSignal,
792
- ): Promise<{ skipped: boolean }> {
793
- // 40+ lines of complex column mapping, data streaming, error handling
794
- return { skipped: false };
795
- }
796
-
797
- async function resetSequences(targetClient: PoolClient): Promise<void> {
798
- // Meaningful database operation
799
- const tableName = seq.sequence_name.replace(/_id_seq$/, ""); // Inline simple operations
800
- }
801
-
802
- async function createSupabaseUsers(targetClient: PoolClient): Promise<void> {
803
- // Distinct business operation for user management
804
- }
805
-
806
- async function seedFromStaging(signal: AbortSignal): Promise<void> {
807
- // Clean orchestration function
808
- // - Database setup
809
- // - Transaction management
810
- // - Calling business operations
811
- // - Error handling
812
- }
813
- ```
814
-
815
- ### 9.2 Refactoring & Anti-Patterns
816
-
817
- **Why:** Keep code obvious by iterating in stages, avoiding abstraction or optimization until the third time you feel the pain.
818
-
819
- **Do:**
820
-
821
- - Start with the direct implementation; extract helpers only when they encapsulate meaningful business logic.
822
- - Duplicate logic on the second occurrence, then factor a helper on the third.
823
- - Keep related logic together; prefer pragmatic refactors over scattering files across faux layers.
824
-
825
- **Avoid:**
826
-
827
- - Abstractions invented after a single use (e.g., `AbstractDataProcessor` classes).
828
- - Clever function composition helpers when sequential code is clearer.
829
- - Premature caches, memoization, or micro-optimizations without evidence.
830
- - Dynamic property access that defeats type safety.