skill-calorie 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (373) hide show
  1. package/dist/analysis/anomaly/common.d.ts +53 -0
  2. package/dist/analysis/anomaly/common.d.ts.map +1 -0
  3. package/dist/analysis/anomaly/common.js +69 -0
  4. package/dist/analysis/anomaly/common.js.map +1 -0
  5. package/dist/analysis/anomaly/diet.d.ts +8 -0
  6. package/dist/analysis/anomaly/diet.d.ts.map +1 -0
  7. package/dist/analysis/anomaly/diet.js +95 -0
  8. package/dist/analysis/anomaly/diet.js.map +1 -0
  9. package/dist/analysis/anomaly/exercise.d.ts +9 -0
  10. package/dist/analysis/anomaly/exercise.d.ts.map +1 -0
  11. package/dist/analysis/anomaly/exercise.js +116 -0
  12. package/dist/analysis/anomaly/exercise.js.map +1 -0
  13. package/dist/analysis/anomaly/index.d.ts +19 -0
  14. package/dist/analysis/anomaly/index.d.ts.map +1 -0
  15. package/dist/analysis/anomaly/index.js +51 -0
  16. package/dist/analysis/anomaly/index.js.map +1 -0
  17. package/dist/analysis/anomaly/overall.d.ts +13 -0
  18. package/dist/analysis/anomaly/overall.d.ts.map +1 -0
  19. package/dist/analysis/anomaly/overall.js +128 -0
  20. package/dist/analysis/anomaly/overall.js.map +1 -0
  21. package/dist/analysis/anomaly/overall2.d.ts +5 -0
  22. package/dist/analysis/anomaly/overall2.d.ts.map +1 -0
  23. package/dist/analysis/anomaly/overall2.js +94 -0
  24. package/dist/analysis/anomaly/overall2.js.map +1 -0
  25. package/dist/analysis/anomaly/weight.d.ts +8 -0
  26. package/dist/analysis/anomaly/weight.d.ts.map +1 -0
  27. package/dist/analysis/anomaly/weight.js +106 -0
  28. package/dist/analysis/anomaly/weight.js.map +1 -0
  29. package/dist/analysis/anomaly/weight2.d.ts +4 -0
  30. package/dist/analysis/anomaly/weight2.d.ts.map +1 -0
  31. package/dist/analysis/anomaly/weight2.js +59 -0
  32. package/dist/analysis/anomaly/weight2.js.map +1 -0
  33. package/dist/analysis/contraindications.d.ts +63 -0
  34. package/dist/analysis/contraindications.d.ts.map +1 -0
  35. package/dist/analysis/contraindications.js +121 -0
  36. package/dist/analysis/contraindications.js.map +1 -0
  37. package/dist/analysis/cross.d.ts +75 -0
  38. package/dist/analysis/cross.d.ts.map +1 -0
  39. package/dist/analysis/cross.js +283 -0
  40. package/dist/analysis/cross.js.map +1 -0
  41. package/dist/analysis/dashboard.d.ts +18 -0
  42. package/dist/analysis/dashboard.d.ts.map +1 -0
  43. package/dist/analysis/dashboard.js +48 -0
  44. package/dist/analysis/dashboard.js.map +1 -0
  45. package/dist/analysis/deficit.d.ts +44 -0
  46. package/dist/analysis/deficit.d.ts.map +1 -0
  47. package/dist/analysis/deficit.js +54 -0
  48. package/dist/analysis/deficit.js.map +1 -0
  49. package/dist/analysis/diet.d.ts +76 -0
  50. package/dist/analysis/diet.d.ts.map +1 -0
  51. package/dist/analysis/diet.js +164 -0
  52. package/dist/analysis/diet.js.map +1 -0
  53. package/dist/analysis/exercise.d.ts +98 -0
  54. package/dist/analysis/exercise.d.ts.map +1 -0
  55. package/dist/analysis/exercise.js +298 -0
  56. package/dist/analysis/exercise.js.map +1 -0
  57. package/dist/analysis/exerciseReview.d.ts +36 -0
  58. package/dist/analysis/exerciseReview.d.ts.map +1 -0
  59. package/dist/analysis/exerciseReview.js +50 -0
  60. package/dist/analysis/exerciseReview.js.map +1 -0
  61. package/dist/analysis/precision.d.ts +15 -0
  62. package/dist/analysis/precision.d.ts.map +1 -0
  63. package/dist/analysis/precision.js +38 -0
  64. package/dist/analysis/precision.js.map +1 -0
  65. package/dist/analysis/result.d.ts +15 -0
  66. package/dist/analysis/result.d.ts.map +1 -0
  67. package/dist/analysis/result.js +13 -0
  68. package/dist/analysis/result.js.map +1 -0
  69. package/dist/analysis/review.d.ts +118 -0
  70. package/dist/analysis/review.d.ts.map +1 -0
  71. package/dist/analysis/review.js +195 -0
  72. package/dist/analysis/review.js.map +1 -0
  73. package/dist/analysis/series.d.ts +42 -0
  74. package/dist/analysis/series.d.ts.map +1 -0
  75. package/dist/analysis/series.js +127 -0
  76. package/dist/analysis/series.js.map +1 -0
  77. package/dist/analysis/simulate.d.ts +43 -0
  78. package/dist/analysis/simulate.d.ts.map +1 -0
  79. package/dist/analysis/simulate.js +109 -0
  80. package/dist/analysis/simulate.js.map +1 -0
  81. package/dist/analysis/simulate2.d.ts +65 -0
  82. package/dist/analysis/simulate2.d.ts.map +1 -0
  83. package/dist/analysis/simulate2.js +173 -0
  84. package/dist/analysis/simulate2.js.map +1 -0
  85. package/dist/analysis/trend.d.ts +39 -0
  86. package/dist/analysis/trend.d.ts.map +1 -0
  87. package/dist/analysis/trend.js +48 -0
  88. package/dist/analysis/trend.js.map +1 -0
  89. package/dist/analysis/utils.d.ts +17 -0
  90. package/dist/analysis/utils.d.ts.map +1 -0
  91. package/dist/analysis/utils.js +52 -0
  92. package/dist/analysis/utils.js.map +1 -0
  93. package/dist/analysis/volatility.d.ts +43 -0
  94. package/dist/analysis/volatility.d.ts.map +1 -0
  95. package/dist/analysis/volatility.js +96 -0
  96. package/dist/analysis/volatility.js.map +1 -0
  97. package/dist/analysis/weight.d.ts +86 -0
  98. package/dist/analysis/weight.d.ts.map +1 -0
  99. package/dist/analysis/weight.js +219 -0
  100. package/dist/analysis/weight.js.map +1 -0
  101. package/dist/analysis/weightCompare.d.ts +63 -0
  102. package/dist/analysis/weightCompare.d.ts.map +1 -0
  103. package/dist/analysis/weightCompare.js +212 -0
  104. package/dist/analysis/weightCompare.js.map +1 -0
  105. package/dist/analysis/weightCompare2.d.ts +63 -0
  106. package/dist/analysis/weightCompare2.d.ts.map +1 -0
  107. package/dist/analysis/weightCompare2.js +211 -0
  108. package/dist/analysis/weightCompare2.js.map +1 -0
  109. package/dist/analysis/weightCompare3.d.ts +13 -0
  110. package/dist/analysis/weightCompare3.d.ts.map +1 -0
  111. package/dist/analysis/weightCompare3.js +199 -0
  112. package/dist/analysis/weightCompare3.js.map +1 -0
  113. package/dist/cli/cmd_read.d.ts +8 -0
  114. package/dist/cli/cmd_read.d.ts.map +1 -0
  115. package/dist/cli/cmd_read.js +719 -0
  116. package/dist/cli/cmd_read.js.map +1 -0
  117. package/dist/cli/keys.d.ts +333 -0
  118. package/dist/cli/keys.d.ts.map +1 -0
  119. package/dist/cli/keys.js +103 -0
  120. package/dist/cli/keys.js.map +1 -0
  121. package/dist/cli/write.d.ts +24 -0
  122. package/dist/cli/write.d.ts.map +1 -0
  123. package/dist/cli/write.js +832 -0
  124. package/dist/cli/write.js.map +1 -0
  125. package/dist/fetch/audit.d.ts +25 -0
  126. package/dist/fetch/audit.d.ts.map +1 -0
  127. package/dist/fetch/audit.js +46 -0
  128. package/dist/fetch/audit.js.map +1 -0
  129. package/dist/fetch/batch.d.ts +68 -0
  130. package/dist/fetch/batch.d.ts.map +1 -0
  131. package/dist/fetch/batch.js +183 -0
  132. package/dist/fetch/batch.js.map +1 -0
  133. package/dist/fetch/body.d.ts +89 -0
  134. package/dist/fetch/body.d.ts.map +1 -0
  135. package/dist/fetch/body.js +217 -0
  136. package/dist/fetch/body.js.map +1 -0
  137. package/dist/fetch/cli.d.ts +3 -0
  138. package/dist/fetch/cli.d.ts.map +1 -0
  139. package/dist/fetch/cli.js +195 -0
  140. package/dist/fetch/cli.js.map +1 -0
  141. package/dist/fetch/cross-skill.d.ts +29 -0
  142. package/dist/fetch/cross-skill.d.ts.map +1 -0
  143. package/dist/fetch/cross-skill.js +42 -0
  144. package/dist/fetch/cross-skill.js.map +1 -0
  145. package/dist/fetch/db.d.ts +5 -0
  146. package/dist/fetch/db.d.ts.map +1 -0
  147. package/dist/fetch/db.js +4 -0
  148. package/dist/fetch/db.js.map +1 -0
  149. package/dist/fetch/diet.d.ts +174 -0
  150. package/dist/fetch/diet.d.ts.map +1 -0
  151. package/dist/fetch/diet.js +239 -0
  152. package/dist/fetch/diet.js.map +1 -0
  153. package/dist/fetch/errors.d.ts +5 -0
  154. package/dist/fetch/errors.d.ts.map +1 -0
  155. package/dist/fetch/errors.js +8 -0
  156. package/dist/fetch/errors.js.map +1 -0
  157. package/dist/fetch/exercise.d.ts +72 -0
  158. package/dist/fetch/exercise.d.ts.map +1 -0
  159. package/dist/fetch/exercise.js +270 -0
  160. package/dist/fetch/exercise.js.map +1 -0
  161. package/dist/fetch/goal.d.ts +48 -0
  162. package/dist/fetch/goal.d.ts.map +1 -0
  163. package/dist/fetch/goal.js +73 -0
  164. package/dist/fetch/goal.js.map +1 -0
  165. package/dist/fetch/goalHistory.d.ts +24 -0
  166. package/dist/fetch/goalHistory.d.ts.map +1 -0
  167. package/dist/fetch/goalHistory.js +51 -0
  168. package/dist/fetch/goalHistory.js.map +1 -0
  169. package/dist/fetch/history.d.ts +28 -0
  170. package/dist/fetch/history.d.ts.map +1 -0
  171. package/dist/fetch/history.js +29 -0
  172. package/dist/fetch/history.js.map +1 -0
  173. package/dist/fetch/index.d.ts +23 -0
  174. package/dist/fetch/index.d.ts.map +1 -0
  175. package/dist/fetch/index.js +16 -0
  176. package/dist/fetch/index.js.map +1 -0
  177. package/dist/fetch/nutritionGoal.d.ts +115 -0
  178. package/dist/fetch/nutritionGoal.d.ts.map +1 -0
  179. package/dist/fetch/nutritionGoal.js +154 -0
  180. package/dist/fetch/nutritionGoal.js.map +1 -0
  181. package/dist/fetch/photos.d.ts +76 -0
  182. package/dist/fetch/photos.d.ts.map +1 -0
  183. package/dist/fetch/photos.js +193 -0
  184. package/dist/fetch/photos.js.map +1 -0
  185. package/dist/fetch/plan.d.ts +145 -0
  186. package/dist/fetch/plan.d.ts.map +1 -0
  187. package/dist/fetch/plan.js +258 -0
  188. package/dist/fetch/plan.js.map +1 -0
  189. package/dist/fetch/products.d.ts +62 -0
  190. package/dist/fetch/products.d.ts.map +1 -0
  191. package/dist/fetch/products.js +73 -0
  192. package/dist/fetch/products.js.map +1 -0
  193. package/dist/fetch/profile.d.ts +46 -0
  194. package/dist/fetch/profile.d.ts.map +1 -0
  195. package/dist/fetch/profile.js +105 -0
  196. package/dist/fetch/profile.js.map +1 -0
  197. package/dist/fetch/shapes.d.ts +41 -0
  198. package/dist/fetch/shapes.d.ts.map +1 -0
  199. package/dist/fetch/shapes.js +38 -0
  200. package/dist/fetch/shapes.js.map +1 -0
  201. package/dist/fetch/validate.d.ts +11 -0
  202. package/dist/fetch/validate.d.ts.map +1 -0
  203. package/dist/fetch/validate.js +64 -0
  204. package/dist/fetch/validate.js.map +1 -0
  205. package/dist/fetch/weight.d.ts +109 -0
  206. package/dist/fetch/weight.d.ts.map +1 -0
  207. package/dist/fetch/weight.js +210 -0
  208. package/dist/fetch/weight.js.map +1 -0
  209. package/dist/fetch/xunji-catalog.d.ts +17 -0
  210. package/dist/fetch/xunji-catalog.d.ts.map +1 -0
  211. package/dist/fetch/xunji-catalog.js +57 -0
  212. package/dist/fetch/xunji-catalog.js.map +1 -0
  213. package/dist/index.d.ts +17 -0
  214. package/dist/index.d.ts.map +1 -0
  215. package/dist/index.js +12 -0
  216. package/dist/index.js.map +1 -0
  217. package/dist/kcal.d.ts +21 -0
  218. package/dist/kcal.d.ts.map +1 -0
  219. package/dist/kcal.js +27 -0
  220. package/dist/kcal.js.map +1 -0
  221. package/dist/migrate/migrate.d.ts +71 -0
  222. package/dist/migrate/migrate.d.ts.map +1 -0
  223. package/dist/migrate/migrate.js +450 -0
  224. package/dist/migrate/migrate.js.map +1 -0
  225. package/dist/paths.d.ts +6 -0
  226. package/dist/paths.d.ts.map +1 -0
  227. package/dist/paths.js +34 -0
  228. package/dist/paths.js.map +1 -0
  229. package/dist/render/analysisPlate.d.ts +49 -0
  230. package/dist/render/analysisPlate.d.ts.map +1 -0
  231. package/dist/render/analysisPlate.js +149 -0
  232. package/dist/render/analysisPlate.js.map +1 -0
  233. package/dist/render/bodyPlate.d.ts +55 -0
  234. package/dist/render/bodyPlate.d.ts.map +1 -0
  235. package/dist/render/bodyPlate.js +97 -0
  236. package/dist/render/bodyPlate.js.map +1 -0
  237. package/dist/render/diet.d.ts +47 -0
  238. package/dist/render/diet.d.ts.map +1 -0
  239. package/dist/render/diet.js +94 -0
  240. package/dist/render/diet.js.map +1 -0
  241. package/dist/render/envelope.d.ts +60 -0
  242. package/dist/render/envelope.d.ts.map +1 -0
  243. package/dist/render/envelope.js +111 -0
  244. package/dist/render/envelope.js.map +1 -0
  245. package/dist/render/errors.d.ts +10 -0
  246. package/dist/render/errors.d.ts.map +1 -0
  247. package/dist/render/errors.js +9 -0
  248. package/dist/render/errors.js.map +1 -0
  249. package/dist/render/exercise.d.ts +20 -0
  250. package/dist/render/exercise.d.ts.map +1 -0
  251. package/dist/render/exercise.js +32 -0
  252. package/dist/render/exercise.js.map +1 -0
  253. package/dist/render/goal.d.ts +24 -0
  254. package/dist/render/goal.d.ts.map +1 -0
  255. package/dist/render/goal.js +23 -0
  256. package/dist/render/goal.js.map +1 -0
  257. package/dist/render/goalExtra.d.ts +45 -0
  258. package/dist/render/goalExtra.d.ts.map +1 -0
  259. package/dist/render/goalExtra.js +94 -0
  260. package/dist/render/goalExtra.js.map +1 -0
  261. package/dist/render/goalPlate.d.ts +66 -0
  262. package/dist/render/goalPlate.d.ts.map +1 -0
  263. package/dist/render/goalPlate.js +110 -0
  264. package/dist/render/goalPlate.js.map +1 -0
  265. package/dist/render/health.d.ts +21 -0
  266. package/dist/render/health.d.ts.map +1 -0
  267. package/dist/render/health.js +41 -0
  268. package/dist/render/health.js.map +1 -0
  269. package/dist/render/help.d.ts +18 -0
  270. package/dist/render/help.d.ts.map +1 -0
  271. package/dist/render/help.js +58 -0
  272. package/dist/render/help.js.map +1 -0
  273. package/dist/render/home.d.ts +30 -0
  274. package/dist/render/home.d.ts.map +1 -0
  275. package/dist/render/home.js +73 -0
  276. package/dist/render/home.js.map +1 -0
  277. package/dist/render/html.d.ts +66 -0
  278. package/dist/render/html.d.ts.map +1 -0
  279. package/dist/render/html.js +510 -0
  280. package/dist/render/html.js.map +1 -0
  281. package/dist/render/index.d.ts +47 -0
  282. package/dist/render/index.d.ts.map +1 -0
  283. package/dist/render/index.js +26 -0
  284. package/dist/render/index.js.map +1 -0
  285. package/dist/render/insightPlate.d.ts +55 -0
  286. package/dist/render/insightPlate.d.ts.map +1 -0
  287. package/dist/render/insightPlate.js +97 -0
  288. package/dist/render/insightPlate.js.map +1 -0
  289. package/dist/render/library.d.ts +32 -0
  290. package/dist/render/library.d.ts.map +1 -0
  291. package/dist/render/library.js +54 -0
  292. package/dist/render/library.js.map +1 -0
  293. package/dist/render/photo.d.ts +101 -0
  294. package/dist/render/photo.d.ts.map +1 -0
  295. package/dist/render/photo.js +241 -0
  296. package/dist/render/photo.js.map +1 -0
  297. package/dist/render/planPlate.d.ts +40 -0
  298. package/dist/render/planPlate.d.ts.map +1 -0
  299. package/dist/render/planPlate.js +72 -0
  300. package/dist/render/planPlate.js.map +1 -0
  301. package/dist/render/profilePlate.d.ts +17 -0
  302. package/dist/render/profilePlate.d.ts.map +1 -0
  303. package/dist/render/profilePlate.js +18 -0
  304. package/dist/render/profilePlate.js.map +1 -0
  305. package/dist/render/ranking.d.ts +22 -0
  306. package/dist/render/ranking.d.ts.map +1 -0
  307. package/dist/render/ranking.js +63 -0
  308. package/dist/render/ranking.js.map +1 -0
  309. package/dist/render/receipt.d.ts +71 -0
  310. package/dist/render/receipt.d.ts.map +1 -0
  311. package/dist/render/receipt.js +65 -0
  312. package/dist/render/receipt.js.map +1 -0
  313. package/dist/render/weightPlate.d.ts +54 -0
  314. package/dist/render/weightPlate.d.ts.map +1 -0
  315. package/dist/render/weightPlate.js +110 -0
  316. package/dist/render/weightPlate.js.map +1 -0
  317. package/dist/schema.d.ts +25 -0
  318. package/dist/schema.d.ts.map +1 -0
  319. package/dist/schema.js +281 -0
  320. package/dist/schema.js.map +1 -0
  321. package/dist/triggers/help-lookup.d.ts +28 -0
  322. package/dist/triggers/help-lookup.d.ts.map +1 -0
  323. package/dist/triggers/help-lookup.js +163 -0
  324. package/dist/triggers/help-lookup.js.map +1 -0
  325. package/dist/triggers/index.d.ts +24 -0
  326. package/dist/triggers/index.d.ts.map +1 -0
  327. package/dist/triggers/index.js +78 -0
  328. package/dist/triggers/index.js.map +1 -0
  329. package/dist/triggers/scene-01-home.d.ts +4 -0
  330. package/dist/triggers/scene-01-home.d.ts.map +1 -0
  331. package/dist/triggers/scene-01-home.js +13 -0
  332. package/dist/triggers/scene-01-home.js.map +1 -0
  333. package/dist/triggers/scene-02-diet.d.ts +4 -0
  334. package/dist/triggers/scene-02-diet.d.ts.map +1 -0
  335. package/dist/triggers/scene-02-diet.js +74 -0
  336. package/dist/triggers/scene-02-diet.js.map +1 -0
  337. package/dist/triggers/scene-03-weight.d.ts +4 -0
  338. package/dist/triggers/scene-03-weight.d.ts.map +1 -0
  339. package/dist/triggers/scene-03-weight.js +62 -0
  340. package/dist/triggers/scene-03-weight.js.map +1 -0
  341. package/dist/triggers/scene-04-exercise.d.ts +4 -0
  342. package/dist/triggers/scene-04-exercise.d.ts.map +1 -0
  343. package/dist/triggers/scene-04-exercise.js +43 -0
  344. package/dist/triggers/scene-04-exercise.js.map +1 -0
  345. package/dist/triggers/scene-05-workout.d.ts +4 -0
  346. package/dist/triggers/scene-05-workout.d.ts.map +1 -0
  347. package/dist/triggers/scene-05-workout.js +36 -0
  348. package/dist/triggers/scene-05-workout.js.map +1 -0
  349. package/dist/triggers/scene-06-goal.d.ts +4 -0
  350. package/dist/triggers/scene-06-goal.d.ts.map +1 -0
  351. package/dist/triggers/scene-06-goal.js +29 -0
  352. package/dist/triggers/scene-06-goal.js.map +1 -0
  353. package/dist/triggers/scene-07-profile.d.ts +4 -0
  354. package/dist/triggers/scene-07-profile.d.ts.map +1 -0
  355. package/dist/triggers/scene-07-profile.js +8 -0
  356. package/dist/triggers/scene-07-profile.js.map +1 -0
  357. package/dist/triggers/scene-08-body.d.ts +4 -0
  358. package/dist/triggers/scene-08-body.d.ts.map +1 -0
  359. package/dist/triggers/scene-08-body.js +17 -0
  360. package/dist/triggers/scene-08-body.js.map +1 -0
  361. package/dist/triggers/scene-09-photo.d.ts +4 -0
  362. package/dist/triggers/scene-09-photo.d.ts.map +1 -0
  363. package/dist/triggers/scene-09-photo.js +14 -0
  364. package/dist/triggers/scene-09-photo.js.map +1 -0
  365. package/dist/triggers/scene-10-analysis.d.ts +4 -0
  366. package/dist/triggers/scene-10-analysis.d.ts.map +1 -0
  367. package/dist/triggers/scene-10-analysis.js +180 -0
  368. package/dist/triggers/scene-10-analysis.js.map +1 -0
  369. package/dist/triggers/types.d.ts +66 -0
  370. package/dist/triggers/types.d.ts.map +1 -0
  371. package/dist/triggers/types.js +3 -0
  372. package/dist/triggers/types.js.map +1 -0
  373. package/package.json +35 -0
@@ -0,0 +1,149 @@
1
+ import { MEAL_WINDOWS, inferMealType, listMeals } from '../fetch/diet.js';
2
+ import { buildSeries, resolveWindow, seriesCount } from '../analysis/series.js';
3
+ import { analyzePair, PAIRS } from '../analysis/cross.js';
4
+ import { buildDeficitData } from '../analysis/deficit.js';
5
+ import { buildTrendData } from '../analysis/trend.js';
6
+ import { dietCalorieTrend, dietDeficitAnalysis, dietMacroRatio } from '../analysis/diet.js';
7
+ import { FetchError } from '../fetch/errors.js';
8
+ import { round2 } from '../kcal.js';
9
+ import { CalorieRenderError } from './errors.js';
10
+ import { MEAL_BUCKETS } from './diet.js';
11
+ export const COMBINED_PAIRS = Object.keys(PAIRS);
12
+ function assertDate(s) {
13
+ if (!/^\d{4}-\d{2}-\d{2}$/.test(s) || Number.isNaN(Date.parse(s + 'T12:00:00Z'))) {
14
+ throw new CalorieRenderError('bad-input', '日期非法: ' + String(s));
15
+ }
16
+ }
17
+ /** 组合分析盘(combined_analysis):pair + 窗口别名,series→analyzePair 直通。 */
18
+ export function buildCombinedAnalysis(db, pair, window = '30d', start, end, today) {
19
+ if (!(pair in PAIRS))
20
+ throw new CalorieRenderError('bad-input', '未知配对 ' + String(pair) + ',可选: ' + Object.keys(PAIRS).join(', '));
21
+ let s;
22
+ let e;
23
+ if (start && end) {
24
+ assertDate(start);
25
+ assertDate(end);
26
+ if (start > end)
27
+ throw new CalorieRenderError('bad-input', 'start 不得晚于 end');
28
+ s = start;
29
+ e = end;
30
+ }
31
+ else {
32
+ try {
33
+ const [rs, re] = resolveWindow(window, start ?? null, end ?? null, today);
34
+ s = rs;
35
+ e = re;
36
+ }
37
+ catch (err) {
38
+ if (err instanceof FetchError)
39
+ throw new CalorieRenderError('bad-input', err.message);
40
+ throw err;
41
+ }
42
+ }
43
+ const series = buildSeries(db, s, e);
44
+ let analysis;
45
+ try {
46
+ analysis = analyzePair(series, pair, db, window);
47
+ }
48
+ catch (err) {
49
+ if (err instanceof FetchError)
50
+ throw new CalorieRenderError('bad-input', err.message);
51
+ throw err;
52
+ }
53
+ if (analysis.aCount === 0 && analysis.bCount === 0) {
54
+ throw new CalorieRenderError('missing-data', '组合无数据(' + pair + ',' + s + ' ~ ' + e + ')');
55
+ }
56
+ return { pair, window, start: s, end: e, series, analysis };
57
+ }
58
+ /** 缺口盘(calorie_deficit):T7 薄壳直通,空窗转 missing-data。 */
59
+ export function buildDeficitPlate(db, start, end) {
60
+ assertDate(start);
61
+ assertDate(end);
62
+ if (start > end)
63
+ throw new CalorieRenderError('bad-input', 'start 不得晚于 end');
64
+ try {
65
+ return buildDeficitData(db, start, end);
66
+ }
67
+ catch (e) {
68
+ if (e instanceof FetchError)
69
+ throw new CalorieRenderError('missing-data', e.message);
70
+ throw e;
71
+ }
72
+ }
73
+ /** 趋势盘(render_analysis 趋势段):T7 薄壳直通。 */
74
+ export function buildTrendPlate(db, start, end) {
75
+ assertDate(start);
76
+ assertDate(end);
77
+ if (start > end)
78
+ throw new CalorieRenderError('bad-input', 'start 不得晚于 end');
79
+ try {
80
+ return buildTrendData(db, start, end);
81
+ }
82
+ catch (e) {
83
+ if (e instanceof FetchError)
84
+ throw new CalorieRenderError('missing-data', e.message);
85
+ throw e;
86
+ }
87
+ }
88
+ function assertMealWindows() {
89
+ for (const k of ['早餐', '午餐', '下午茶', '晚餐', '夜宵', '加餐']) {
90
+ if (!MEAL_WINDOWS[k])
91
+ throw new CalorieRenderError('missing-data', 'MEAL_WINDOWS 缺餐别: ' + k);
92
+ }
93
+ }
94
+ function bucketOf(time) {
95
+ if (!time)
96
+ return '其他';
97
+ const m = inferMealType(time);
98
+ if (m === '早餐' || m === '午餐' || m === '晚餐')
99
+ return m;
100
+ if (m === '下午茶' || m === '夜宵')
101
+ return '加餐';
102
+ return '其他';
103
+ }
104
+ /** 饮食复盘盘(diet_review/nutrition_ratio):T5 三件套 + 按餐桶汇总(MEAL_WINDOWS 同源)。 */
105
+ export function buildDietReview(db, start, end) {
106
+ assertDate(start);
107
+ assertDate(end);
108
+ if (start > end)
109
+ throw new CalorieRenderError('bad-input', 'start 不得晚于 end');
110
+ assertMealWindows();
111
+ const trend = dietCalorieTrend(db, start, end);
112
+ const macro = dietMacroRatio(db, start, end);
113
+ const deficit = dietDeficitAnalysis(db, start, end);
114
+ if (trend.status !== 'ok' && macro.status !== 'ok' && deficit.status !== 'ok') {
115
+ throw new CalorieRenderError('missing-data', '无饮食记录(' + start + ' ~ ' + end + ')');
116
+ }
117
+ const series = buildSeries(db, start, end);
118
+ const loggedDays = seriesCount(series, 'calories');
119
+ if (loggedDays === 0)
120
+ throw new CalorieRenderError('missing-data', '无饮食记录(' + start + ' ~ ' + end + ')');
121
+ const agg = new Map();
122
+ for (const d of series) {
123
+ let rows;
124
+ try {
125
+ rows = listMeals(db, d.date).filter((r) => r.food_name !== '💧水');
126
+ }
127
+ catch {
128
+ continue;
129
+ }
130
+ for (const r of rows) {
131
+ const b = bucketOf(r.time);
132
+ if (b === '其他')
133
+ continue;
134
+ let e = agg.get(b);
135
+ if (!e) {
136
+ e = { days: new Set(), total: 0 };
137
+ agg.set(b, e);
138
+ }
139
+ e.days.add(d.date);
140
+ e.total += r.calories;
141
+ }
142
+ }
143
+ const byMeal = MEAL_BUCKETS.map((meal) => {
144
+ const e = agg.get(meal);
145
+ return { meal, days: e ? e.days.size : 0, totalCalories: e ? round2(e.total) : 0 };
146
+ });
147
+ return { start, end, trend, macro, deficit, loggedDays, byMeal };
148
+ }
149
+ //# sourceMappingURL=analysisPlate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"analysisPlate.js","sourceRoot":"","sources":["../../src/render/analysisPlate.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,55 @@
1
+ /** #41 · 身体细节读链 render 数据(体成分看/围度看)。
2
+ *
3
+ * 数据源全复用 fetch/body.ts,不自算:
4
+ * 体成分=listCompositions + trendComposition(source 缺省取 latestSource);
5
+ * 围度=listMeasurements + trendMeasurement(metric 可选)。
6
+ * 录链(composition-add/remove、measure-add/remove)已由 #40 写键承接,本票只补看链。
7
+ * 空库一律 missing-data,不返空数组冒充正常。
8
+ */
9
+ import type { DatabaseSync } from 'node:sqlite';
10
+ export interface BodyCompositionView {
11
+ source: string | null;
12
+ items: Record<string, unknown>[];
13
+ total: number;
14
+ trend: {
15
+ date: string;
16
+ avgPct: number;
17
+ n: number;
18
+ }[];
19
+ latestPct: number | null;
20
+ }
21
+ export declare function buildBodyCompositionView(db: DatabaseSync, opts?: {
22
+ days?: number;
23
+ source?: string;
24
+ limit?: number;
25
+ }): BodyCompositionView;
26
+ export declare function buildBodyCompositionCompare(db: DatabaseSync, fromDate: string, toDate: string, source?: string): Record<string, unknown>;
27
+ export interface BodyMeasureView {
28
+ metric: string | null;
29
+ items: Record<string, unknown>[];
30
+ total: number;
31
+ trend: {
32
+ date: string;
33
+ avgVal: number;
34
+ n: number;
35
+ }[];
36
+ latestVal: number | null;
37
+ }
38
+ export declare function buildBodyMeasureView(db: DatabaseSync, opts?: {
39
+ metric?: string;
40
+ days?: number;
41
+ limit?: number;
42
+ dateFrom?: string;
43
+ dateTo?: string;
44
+ }): BodyMeasureView;
45
+ export declare function buildBodyMeasureCompare(db: DatabaseSync, date1: string, date2: string): {
46
+ date1: string;
47
+ date2: string;
48
+ deltas: Record<string, {
49
+ before: number;
50
+ after: number;
51
+ delta: number;
52
+ }>;
53
+ nCompared: number;
54
+ };
55
+ //# sourceMappingURL=bodyPlate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bodyPlate.d.ts","sourceRoot":"","sources":["../../src/render/bodyPlate.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAKhD,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC;IACjC,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IACrD,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AAED,wBAAgB,wBAAwB,CACtC,EAAE,EAAE,YAAY,EAChB,IAAI,GAAE;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAO,GAC5D,mBAAmB,CAsBrB;AAED,wBAAgB,2BAA2B,CACzC,EAAE,EAAE,YAAY,EAChB,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,EACd,MAAM,CAAC,EAAE,MAAM,GACd,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAUzB;AAED,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC;IACjC,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IACrD,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AAED,wBAAgB,oBAAoB,CAClC,EAAE,EAAE,YAAY,EAChB,IAAI,GAAE;IAAE,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAO,GAChG,eAAe,CAiCjB;AAED,wBAAgB,uBAAuB,CAAC,EAAE,EAAE,YAAY,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG;IACvF,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;CAC3H,CAUA"}
@@ -0,0 +1,97 @@
1
+ import { compareCompositions, compareMeasurements, listCompositions, listMeasurements, trendComposition, trendMeasurement } from '../fetch/body.js';
2
+ import { FetchError } from '../fetch/errors.js';
3
+ import { CalorieRenderError } from './errors.js';
4
+ export function buildBodyCompositionView(db, opts = {}) {
5
+ const days = opts.days ?? 90;
6
+ if (!Number.isInteger(days) || days < 1 || days > 365) {
7
+ throw new CalorieRenderError('bad-input', 'days 须为 1..365 整数');
8
+ }
9
+ const limit = opts.limit ?? 20;
10
+ if (!Number.isInteger(limit) || limit < 1 || limit > 100) {
11
+ throw new CalorieRenderError('bad-input', 'limit 须为 1..100 整数');
12
+ }
13
+ try {
14
+ const items = listCompositions(db, { days, source: opts.source, limit });
15
+ if (items.length === 0)
16
+ throw new CalorieRenderError('missing-data', '无体成分记录(近' + days + '天)');
17
+ const trend = trendComposition(db, days, opts.source);
18
+ const first = items[0];
19
+ const latestPct = typeof first?.body_fat_pct === 'number' ? first.body_fat_pct : null;
20
+ const source = typeof first?.source === 'string' ? first.source : (opts.source ?? null);
21
+ return { source, items, total: items.length, trend, latestPct };
22
+ }
23
+ catch (e) {
24
+ if (e instanceof CalorieRenderError)
25
+ throw e;
26
+ if (e instanceof FetchError)
27
+ throw new CalorieRenderError('missing-data', e.message);
28
+ throw e;
29
+ }
30
+ }
31
+ export function buildBodyCompositionCompare(db, fromDate, toDate, source) {
32
+ if (!/^\d{4}-\d{2}-\d{2}$/.test(fromDate) || !/^\d{4}-\d{2}-\d{2}$/.test(toDate)) {
33
+ throw new CalorieRenderError('bad-input', '日期非法:' + fromDate + ' ~ ' + toDate);
34
+ }
35
+ try {
36
+ return compareCompositions(db, fromDate, toDate, source);
37
+ }
38
+ catch (e) {
39
+ if (e instanceof FetchError)
40
+ throw new CalorieRenderError('missing-data', e.message);
41
+ throw e;
42
+ }
43
+ }
44
+ export function buildBodyMeasureView(db, opts = {}) {
45
+ const days = opts.days ?? 90;
46
+ if (!Number.isInteger(days) || days < 1 || days > 365) {
47
+ throw new CalorieRenderError('bad-input', 'days 须为 1..365 整数');
48
+ }
49
+ const limit = opts.limit ?? 20;
50
+ if (!Number.isInteger(limit) || limit < 1 || limit > 100) {
51
+ throw new CalorieRenderError('bad-input', 'limit 须为 1..100 整数');
52
+ }
53
+ if (opts.dateFrom && !/^\d{4}-\d{2}-\d{2}$/.test(opts.dateFrom))
54
+ throw new CalorieRenderError('bad-input', 'dateFrom 非法:' + opts.dateFrom);
55
+ if (opts.dateTo && !/^\d{4}-\d{2}-\d{2}$/.test(opts.dateTo))
56
+ throw new CalorieRenderError('bad-input', 'dateTo 非法:' + opts.dateTo);
57
+ try {
58
+ const items = listMeasurements(db, {
59
+ metric: opts.metric,
60
+ days: opts.dateFrom && opts.dateTo ? undefined : days,
61
+ dateFrom: opts.dateFrom,
62
+ dateTo: opts.dateTo,
63
+ limit,
64
+ });
65
+ if (items.length === 0)
66
+ throw new CalorieRenderError('missing-data', '无围度记录');
67
+ let trend = [];
68
+ let latestVal = null;
69
+ if (opts.metric) {
70
+ trend = trendMeasurement(db, opts.metric, days);
71
+ const v = items[0][opts.metric];
72
+ latestVal = typeof v === 'number' ? v : null;
73
+ }
74
+ return { metric: opts.metric ?? null, items, total: items.length, trend, latestVal };
75
+ }
76
+ catch (e) {
77
+ if (e instanceof CalorieRenderError)
78
+ throw e;
79
+ if (e instanceof FetchError)
80
+ throw new CalorieRenderError('missing-data', e.message);
81
+ throw e;
82
+ }
83
+ }
84
+ export function buildBodyMeasureCompare(db, date1, date2) {
85
+ if (!/^\d{4}-\d{2}-\d{2}$/.test(date1) || !/^\d{4}-\d{2}-\d{2}$/.test(date2)) {
86
+ throw new CalorieRenderError('bad-input', '日期非法:' + date1 + ' ~ ' + date2);
87
+ }
88
+ try {
89
+ return compareMeasurements(db, date1, date2);
90
+ }
91
+ catch (e) {
92
+ if (e instanceof FetchError)
93
+ throw new CalorieRenderError('missing-data', e.message);
94
+ throw e;
95
+ }
96
+ }
97
+ //# sourceMappingURL=bodyPlate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bodyPlate.js","sourceRoot":"","sources":["../../src/render/bodyPlate.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,47 @@
1
+ /** T8 #27 · 饮食视图数据(diet_overview + meal_distribution 对应)。
2
+ *
3
+ * 死规矩 1:餐别窗口跟 diet.ts MEAL_WINDOWS(已合入方向,老家旧口径作废)。
4
+ * 本文件不硬编码任何小时区间:单条归类走 inferMealType(与 MEAL_WINDOWS 同源),
5
+ * 加餐聚合 = 下午茶 + 夜宵(diet.ts 注释口径),窗口存在性断言 MEAL_WINDOWS。
6
+ * 死规矩 3:日数列用 T5 series(buildSeries/seriesSum/seriesAvg),不自造 SUM。
7
+ * 饮食总览趋势段用 T7 buildTrendData(同样以 series 为源)。空窗即 missing-data。
8
+ */
9
+ import type { DatabaseSync } from 'node:sqlite';
10
+ import type { DaySeries } from '../analysis/series.js';
11
+ import type { TrendData } from '../analysis/trend.js';
12
+ /** 四桶分布(触发词 meal_dist_all 口径):早餐/午餐/晚餐/加餐(加餐=下午茶+夜宵)。 */
13
+ export declare const MEAL_BUCKETS: readonly ["早餐", "午餐", "晚餐", "加餐"];
14
+ export type MealBucket = (typeof MEAL_BUCKETS)[number];
15
+ export interface MealSlice {
16
+ meal: MealBucket;
17
+ count: number;
18
+ calories: number;
19
+ pct: number;
20
+ }
21
+ export interface MealDistribution {
22
+ date: string;
23
+ totalCalories: number;
24
+ slices: MealSlice[];
25
+ /** 明细 5 类(早/午/下午茶/晚/夜宵)+ 加餐聚合,供模板展开。 */
26
+ detail: {
27
+ meal: string;
28
+ count: number;
29
+ calories: number;
30
+ }[];
31
+ }
32
+ /** C4 #43 · 空日零分布(窗内有数但尾日无记录时回零,不掀整窗 missing)。 */
33
+ export declare function zeroMealDistribution(date: string): MealDistribution;
34
+ export declare function buildMealDistribution(db: DatabaseSync, date: string): MealDistribution;
35
+ export interface DietOverview {
36
+ start: string;
37
+ end: string;
38
+ days: number;
39
+ loggedDays: number;
40
+ totalCalories: number;
41
+ avgCalories: number | null;
42
+ calorieGoal: number;
43
+ trend: TrendData;
44
+ series: DaySeries[];
45
+ }
46
+ export declare function buildDietOverview(db: DatabaseSync, start: string, end: string): DietOverview;
47
+ //# sourceMappingURL=diet.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"diet.d.ts","sourceRoot":"","sources":["../../src/render/diet.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAGhD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAEvD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAItD,yDAAyD;AACzD,eAAO,MAAM,YAAY,mCAAoC,CAAC;AAC9D,MAAM,MAAM,UAAU,GAAG,CAAC,OAAO,YAAY,CAAC,CAAC,MAAM,CAAC,CAAC;AAEvD,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,UAAU,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,GAAG,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,aAAa,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,SAAS,EAAE,CAAC;IACpB,wCAAwC;IACxC,MAAM,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;CAC7D;AAiBD,kDAAkD;AAClD,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,gBAAgB,CAQnE;AAED,wBAAgB,qBAAqB,CAAC,EAAE,EAAE,YAAY,EAAE,IAAI,EAAE,MAAM,GAAG,gBAAgB,CAgCtF;AAED,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,SAAS,CAAC;IACjB,MAAM,EAAE,SAAS,EAAE,CAAC;CACrB;AAED,wBAAgB,iBAAiB,CAAC,EAAE,EAAE,YAAY,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,YAAY,CAoB5F"}
@@ -0,0 +1,94 @@
1
+ import { MEAL_WINDOWS, inferMealType, listMeals } from '../fetch/diet.js';
2
+ import { buildSeries, seriesAvg, seriesCount, seriesSum } from '../analysis/series.js';
3
+ import { buildTrendData } from '../analysis/trend.js';
4
+ import { round2 } from '../kcal.js';
5
+ import { CalorieRenderError } from './errors.js';
6
+ /** 四桶分布(触发词 meal_dist_all 口径):早餐/午餐/晚餐/加餐(加餐=下午茶+夜宵)。 */
7
+ export const MEAL_BUCKETS = ['早餐', '午餐', '晚餐', '加餐'];
8
+ function assertMealWindows() {
9
+ for (const k of ['早餐', '午餐', '下午茶', '晚餐', '夜宵', '加餐']) {
10
+ if (!MEAL_WINDOWS[k])
11
+ throw new CalorieRenderError('missing-data', 'MEAL_WINDOWS 缺餐别: ' + k);
12
+ }
13
+ }
14
+ /** 单条时间归桶:先 inferMealType(MEAL_WINDOWS 同源),再把下午茶/夜宵并入加餐。 */
15
+ function bucketOf(time) {
16
+ if (!time)
17
+ return '其他';
18
+ const m = inferMealType(time);
19
+ if (m === '早餐' || m === '午餐' || m === '晚餐')
20
+ return m;
21
+ if (m === '下午茶' || m === '夜宵')
22
+ return '加餐';
23
+ return '其他';
24
+ }
25
+ /** C4 #43 · 空日零分布(窗内有数但尾日无记录时回零,不掀整窗 missing)。 */
26
+ export function zeroMealDistribution(date) {
27
+ if (!/^\d{4}-\d{2}-\d{2}$/.test(date))
28
+ throw new CalorieRenderError('bad-input', '日期非法: ' + date);
29
+ return {
30
+ date,
31
+ totalCalories: 0,
32
+ slices: MEAL_BUCKETS.map((meal) => ({ meal, count: 0, calories: 0, pct: 0 })),
33
+ detail: [],
34
+ };
35
+ }
36
+ export function buildMealDistribution(db, date) {
37
+ assertMealWindows();
38
+ if (!/^\d{4}-\d{2}-\d{2}$/.test(date))
39
+ throw new CalorieRenderError('bad-input', '日期非法: ' + date);
40
+ const rows = listMeals(db, date).filter((r) => r.food_name !== '💧水');
41
+ // 日总量以 T5 series 为准(死规矩 3),分组小计仅做桶内累加,分母不自造。
42
+ const series = buildSeries(db, date, date);
43
+ const totalCalories = series[0]?.calories ?? 0;
44
+ if (rows.length === 0 || totalCalories === 0) {
45
+ throw new CalorieRenderError('missing-data', '无饮食记录(' + date + ')');
46
+ }
47
+ const byBucket = new Map();
48
+ const byDetail = new Map();
49
+ for (const r of rows) {
50
+ const b = bucketOf(r.time);
51
+ if (b === '其他')
52
+ continue;
53
+ const e = byBucket.get(b) ?? { count: 0, calories: 0 };
54
+ e.count += 1;
55
+ e.calories += r.calories;
56
+ byBucket.set(b, e);
57
+ const dm = inferMealType(String(r.time ?? ''));
58
+ const d = byDetail.get(dm) ?? { count: 0, calories: 0 };
59
+ d.count += 1;
60
+ d.calories += r.calories;
61
+ byDetail.set(dm, d);
62
+ }
63
+ const total = totalCalories;
64
+ const slices = MEAL_BUCKETS.map((meal) => {
65
+ const e = byBucket.get(meal) ?? { count: 0, calories: 0 };
66
+ return { meal, count: e.count, calories: round2(e.calories), pct: total ? round2((e.calories / total) * 100) : 0 };
67
+ });
68
+ const detail = [...byDetail.entries()].map(([meal, e]) => ({ meal, count: e.count, calories: round2(e.calories) }));
69
+ return { date, totalCalories, slices, detail };
70
+ }
71
+ export function buildDietOverview(db, start, end) {
72
+ if (!/^\d{4}-\d{2}-\d{2}$/.test(start) || !/^\d{4}-\d{2}-\d{2}$/.test(end)) {
73
+ throw new CalorieRenderError('bad-input', '起止日期非法: ' + start + ' ~ ' + end);
74
+ }
75
+ if (start > end)
76
+ throw new CalorieRenderError('bad-input', 'start 不得晚于 end');
77
+ const series = buildSeries(db, start, end);
78
+ const loggedDays = seriesCount(series, 'calories');
79
+ if (loggedDays === 0)
80
+ throw new CalorieRenderError('missing-data', '无饮食记录(' + start + ' ~ ' + end + ')');
81
+ const trend = buildTrendData(db, start, end);
82
+ return {
83
+ start,
84
+ end,
85
+ days: series.length,
86
+ loggedDays,
87
+ totalCalories: seriesSum(series, 'calories'),
88
+ avgCalories: seriesAvg(series, 'calories'),
89
+ calorieGoal: series[0]?.calorieGoal ?? 1800,
90
+ trend,
91
+ series,
92
+ };
93
+ }
94
+ //# sourceMappingURL=diet.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"diet.js","sourceRoot":"","sources":["../../src/render/diet.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,60 @@
1
+ /** T8 #27 + T9 #28 + T10 #29 + #41 · 主视图 + 目标分析盘 + 身体照片 + 读链补齐 envelope 键表(T11 出口复用同一命名;仅 type-only 消费 link-core)。
2
+ *
3
+ * T8 四主视图 key 另立 view 域:calorie.view_home / calorie.view_diet / calorie.view_exercise / calorie.view_goal(一律 stat)。
4
+ * T9 目标分析盘:goal_config / goal_recommend / goal_weight / goal_progress / goal_status / combined / deficit / diet_review / health / ranking / library / search(一律 stat)。
5
+ * T10 照片视图 key 挂 calorie.photo_*(gallery/compare/viewer/gif/help/receipt 六键,就近形状)。
6
+ */
7
+ import type { EnvelopeShape } from 'base-link-core';
8
+ export declare const VIEW_KEYS: {
9
+ readonly home: string;
10
+ readonly diet: string;
11
+ readonly exercise: string;
12
+ readonly goal: string;
13
+ readonly goalConfig: string;
14
+ readonly goalRecommend: string;
15
+ readonly goalWeight: string;
16
+ readonly goalProgress: string;
17
+ readonly goalStatus: string;
18
+ readonly combined: string;
19
+ readonly deficit: string;
20
+ readonly dietReview: string;
21
+ readonly health: string;
22
+ readonly ranking: string;
23
+ readonly library: string;
24
+ readonly search: string;
25
+ readonly weight: string;
26
+ readonly weightHistory: string;
27
+ readonly weightCompare: string;
28
+ readonly weightReview: string;
29
+ readonly volatility: string;
30
+ readonly bodyComposition: string;
31
+ readonly bodyMeasure: string;
32
+ readonly plan: string;
33
+ readonly planWizard: string;
34
+ readonly exerciseGoal: string;
35
+ readonly goalExpiring: string;
36
+ readonly goalPredict: string;
37
+ readonly goalVsActual: string;
38
+ readonly predict: string;
39
+ readonly anomaly: string;
40
+ readonly contraindication: string;
41
+ readonly dedupe: string;
42
+ readonly profile: string;
43
+ };
44
+ export type ViewName = keyof typeof VIEW_KEYS;
45
+ export declare const VIEW_SHAPES: Record<ViewName, EnvelopeShape>;
46
+ export declare function viewShapeFor(key: string): EnvelopeShape;
47
+ /** stat 载荷守卫:metrics 须全 number(envelope 全字段前置校验,坏载荷不进渲染)。 */
48
+ export declare function assertStatMetrics(metrics: Record<string, unknown>): asserts metrics is Record<string, number>;
49
+ export declare const PHOTO_VIEW_KEYS: {
50
+ readonly gallery: string;
51
+ readonly viewer: string;
52
+ readonly compare: string;
53
+ readonly receipt: string;
54
+ readonly gif: string;
55
+ readonly help: string;
56
+ };
57
+ export type PhotoViewName = keyof typeof PHOTO_VIEW_KEYS;
58
+ export declare const PHOTO_VIEW_SHAPES: Record<PhotoViewName, EnvelopeShape>;
59
+ export declare function photoShapeFor(key: string): EnvelopeShape;
60
+ //# sourceMappingURL=envelope.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"envelope.d.ts","sourceRoot":"","sources":["../../src/render/envelope.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAIpD,eAAO,MAAM,SAAS;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAmCZ,CAAC;AACX,MAAM,MAAM,QAAQ,GAAG,MAAM,OAAO,SAAS,CAAC;AAE9C,eAAO,MAAM,WAAW,EAAE,MAAM,CAAC,QAAQ,EAAE,aAAa,CAmCvD,CAAC;AAEF,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,aAAa,CAIvD;AAED,6DAA6D;AAC7D,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,OAAO,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAM7G;AAED,eAAO,MAAM,eAAe;;;;;;;CAOlB,CAAC;AACX,MAAM,MAAM,aAAa,GAAG,MAAM,OAAO,eAAe,CAAC;AAEzD,eAAO,MAAM,iBAAiB,EAAE,MAAM,CAAC,aAAa,EAAE,aAAa,CAOlE,CAAC;AAEF,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,aAAa,CAIxD"}