@storyshelf/core 0.7.0 → 0.9.0-next-d2cc082

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 (537) hide show
  1. package/dist/adapter/capture-queue.d.mts +0 -2
  2. package/dist/adapter/capture-runner.d.mts +10 -4
  3. package/dist/adapter/capture-runner.mjs +7 -2
  4. package/dist/adapter/database.d.mts +63 -3
  5. package/dist/adapter/database.mjs +4 -3
  6. package/dist/adapter/database.mjs.map +1 -1
  7. package/dist/adapter/design.d.mts +0 -2
  8. package/dist/adapter/design.mjs +1 -2
  9. package/dist/adapter/email-sender.d.mts +0 -2
  10. package/dist/adapter/metadata.d.mts +0 -2
  11. package/dist/adapter/oauth.d.mts +0 -2
  12. package/dist/adapter/setup.d.mts +0 -2
  13. package/dist/adapter/setup.mjs +1 -2
  14. package/dist/adapter/storage.d.mts +1 -3
  15. package/dist/adapter/webhook-events.d.mts +0 -2
  16. package/dist/adapter/webhook-events.mjs +12 -13
  17. package/dist/adapter/webhook-events.mjs.map +1 -1
  18. package/dist/adapters/capture-target-offers.d.mts +0 -2
  19. package/dist/adapters/capture-target-offers.mjs +1 -2
  20. package/dist/adapters/capture-target.d.mts +0 -2
  21. package/dist/adapters/capture-target.mjs +1 -2
  22. package/dist/adapters/notifier/build-comment.d.mts +0 -2
  23. package/dist/adapters/notifier/build-comment.mjs +1 -2
  24. package/dist/adapters/notifier/build-state.d.mts +0 -2
  25. package/dist/adapters/notifier/build-state.mjs +1 -2
  26. package/dist/adapters/notifier/events.d.mts +0 -2
  27. package/dist/adapters/notifier/events.mjs +1 -2
  28. package/dist/adapters/notifier/format.d.mts +0 -2
  29. package/dist/adapters/notifier/format.mjs +1 -2
  30. package/dist/adapters/notifier/provider.d.mts +0 -2
  31. package/dist/adapters/notifier/provider.mjs +1 -2
  32. package/dist/adapters/notifier/state.d.mts +0 -2
  33. package/dist/adapters/notifier/state.mjs +1 -2
  34. package/dist/adapters/notifier/types.d.mts +0 -2
  35. package/dist/adapters/remote-browser.d.mts +120 -31
  36. package/dist/adapters/remote-browser.mjs +134 -34
  37. package/dist/adapters/remote-browser.mjs.map +1 -1
  38. package/dist/adapters/remote-lease-attempt.mjs +39 -0
  39. package/dist/adapters/remote-lease-attempt.mjs.map +1 -0
  40. package/dist/adapters/remote-lease-error.d.mts +25 -0
  41. package/dist/adapters/remote-lease-error.mjs +54 -0
  42. package/dist/adapters/remote-lease-error.mjs.map +1 -0
  43. package/dist/adapters/remote-lease-held.d.mts +15 -0
  44. package/dist/adapters/remote-lease-held.mjs +86 -0
  45. package/dist/adapters/remote-lease-held.mjs.map +1 -0
  46. package/dist/adapters/remote-lease-shutdown.d.mts +12 -0
  47. package/dist/adapters/remote-lease-shutdown.mjs +32 -0
  48. package/dist/adapters/remote-lease-shutdown.mjs.map +1 -0
  49. package/dist/adapters/remote-lease.d.mts +39 -0
  50. package/dist/adapters/remote-lease.mjs +175 -0
  51. package/dist/adapters/remote-lease.mjs.map +1 -0
  52. package/dist/adapters/remote-limiter.mjs +121 -0
  53. package/dist/adapters/remote-limiter.mjs.map +1 -0
  54. package/dist/adapters/remote-pool.d.mts +56 -0
  55. package/dist/adapters/remote-pool.mjs +195 -0
  56. package/dist/adapters/remote-pool.mjs.map +1 -0
  57. package/dist/adapters/remote-sizing.d.mts +44 -0
  58. package/dist/adapters/remote-sizing.mjs +53 -0
  59. package/dist/adapters/remote-sizing.mjs.map +1 -0
  60. package/dist/adapters/render-runs.d.mts +0 -2
  61. package/dist/adapters/render-runs.mjs +1 -2
  62. package/dist/adapters/setup-runner.d.mts +0 -2
  63. package/dist/adapters/setup-runner.mjs +1 -2
  64. package/dist/adapters/sources.d.mts +0 -2
  65. package/dist/adapters/sources.mjs +1 -2
  66. package/dist/adapters/validate.d.mts +0 -2
  67. package/dist/adapters/validate.mjs +2 -2
  68. package/dist/adapters/validate.mjs.map +1 -1
  69. package/dist/ai.d.mts +0 -2
  70. package/dist/ai.mjs +1 -2
  71. package/dist/auth.d.mts +0 -2
  72. package/dist/auth.mjs +1 -2
  73. package/dist/brand-packs.d.mts +0 -2
  74. package/dist/brand-packs.mjs +0 -1
  75. package/dist/capture/a11y/axe-source.d.mts +0 -2
  76. package/dist/capture/a11y/axe-source.mjs +1 -2
  77. package/dist/capture/a11y/in-page.d.mts +1 -3
  78. package/dist/capture/a11y/in-page.mjs +3 -4
  79. package/dist/capture/a11y/in-page.mjs.map +1 -1
  80. package/dist/capture/a11y/map.d.mts +0 -2
  81. package/dist/capture/a11y/map.mjs +1 -2
  82. package/dist/capture/a11y/options.d.mts +0 -2
  83. package/dist/capture/a11y/options.mjs +1 -2
  84. package/dist/capture/adapter.d.mts +5 -2
  85. package/dist/capture/adapter.mjs +1 -2
  86. package/dist/capture/adapter.mjs.map +1 -1
  87. package/dist/capture/affected.d.mts +0 -2
  88. package/dist/capture/affected.mjs +1 -2
  89. package/dist/capture/attempt-logger.mjs +1 -2
  90. package/dist/capture/browsers.d.mts +0 -2
  91. package/dist/capture/browsers.mjs +1 -2
  92. package/dist/capture/build-state/deliver.d.mts +0 -2
  93. package/dist/capture/build-state/deliver.mjs +1 -2
  94. package/dist/capture/build-state/notify-job.d.mts +0 -2
  95. package/dist/capture/build-state/notify-job.mjs +1 -2
  96. package/dist/capture/build-state/publisher.d.mts +0 -2
  97. package/dist/capture/build-state/publisher.mjs +1 -2
  98. package/dist/capture/build-state/resume.d.mts +0 -2
  99. package/dist/capture/build-state/resume.mjs +1 -2
  100. package/dist/capture/build-state/types.d.mts +1 -3
  101. package/dist/capture/catalog/catalogue.d.mts +0 -2
  102. package/dist/capture/catalog/catalogue.mjs +1 -2
  103. package/dist/capture/catalog/components.d.mts +0 -2
  104. package/dist/capture/catalog/components.mjs +1 -2
  105. package/dist/capture/catalog/history.d.mts +0 -2
  106. package/dist/capture/catalog/history.mjs +1 -2
  107. package/dist/capture/catalog/hook.d.mts +0 -2
  108. package/dist/capture/catalog/hook.mjs +1 -2
  109. package/dist/capture/catalog/index.d.mts +2 -2
  110. package/dist/capture/catalog/load.d.mts +0 -2
  111. package/dist/capture/catalog/load.mjs +1 -2
  112. package/dist/capture/catalog/parse.d.mts +0 -2
  113. package/dist/capture/catalog/parse.mjs +1 -2
  114. package/dist/capture/catalog/read.d.mts +6 -3
  115. package/dist/capture/catalog/read.mjs +14 -8
  116. package/dist/capture/catalog/read.mjs.map +1 -1
  117. package/dist/capture/catalog/types.d.mts +0 -2
  118. package/dist/capture/comparison-reference.d.mts +27 -0
  119. package/dist/capture/comparison-reference.mjs +75 -0
  120. package/dist/capture/comparison-reference.mjs.map +1 -0
  121. package/dist/capture/consumer/capture-handler.d.mts +1 -3
  122. package/dist/capture/consumer/capture-handler.mjs +1 -2
  123. package/dist/capture/consumer/idle.d.mts +0 -2
  124. package/dist/capture/consumer/idle.mjs +1 -2
  125. package/dist/capture/consumer/index.d.mts +0 -2
  126. package/dist/capture/consumer/index.mjs +1 -2
  127. package/dist/capture/consumer/loop.mjs +1 -2
  128. package/dist/capture/consumer/metrics.d.mts +0 -2
  129. package/dist/capture/consumer/metrics.mjs +1 -2
  130. package/dist/capture/consumer/retry.d.mts +0 -2
  131. package/dist/capture/consumer/retry.mjs +1 -2
  132. package/dist/capture/consumer/run-job.mjs +1 -2
  133. package/dist/capture/consumer/startup.mjs +1 -2
  134. package/dist/capture/consumer/types.d.mts +0 -2
  135. package/dist/capture/crash-failure.d.mts +0 -2
  136. package/dist/capture/crash-failure.mjs +1 -2
  137. package/dist/capture/db-queue/cadence.d.mts +0 -2
  138. package/dist/capture/db-queue/cadence.mjs +1 -2
  139. package/dist/capture/db-queue/claim.d.mts +0 -2
  140. package/dist/capture/db-queue/claim.mjs +20 -4
  141. package/dist/capture/db-queue/claim.mjs.map +1 -1
  142. package/dist/capture/db-queue/clock.mjs +1 -2
  143. package/dist/capture/db-queue/codec.mjs +1 -2
  144. package/dist/capture/db-queue/index.d.mts +0 -2
  145. package/dist/capture/db-queue/index.mjs +3 -3
  146. package/dist/capture/db-queue/index.mjs.map +1 -1
  147. package/dist/capture/db-queue/keeper.d.mts +0 -2
  148. package/dist/capture/db-queue/keeper.mjs +1 -2
  149. package/dist/capture/db-queue/lease.mjs +1 -2
  150. package/dist/capture/db-queue/lifecycle.mjs +1 -2
  151. package/dist/capture/db-queue/operations.mjs +1 -2
  152. package/dist/capture/db-queue/poll-loop.d.mts +0 -2
  153. package/dist/capture/db-queue/poll-loop.mjs +1 -2
  154. package/dist/capture/db-queue/poll.d.mts +0 -2
  155. package/dist/capture/db-queue/poll.mjs +1 -2
  156. package/dist/capture/db-queue/timers.d.mts +0 -2
  157. package/dist/capture/db-queue/timers.mjs +1 -2
  158. package/dist/capture/db-queue/types.d.mts +0 -2
  159. package/dist/capture/db-queue/types.mjs +1 -2
  160. package/dist/capture/db-queue/types.mjs.map +1 -1
  161. package/dist/capture/db-queue/wake.mjs +1 -2
  162. package/dist/capture/design.mjs +1 -2
  163. package/dist/capture/diff-record.mjs +1 -2
  164. package/dist/capture/dispatch.d.mts +1 -3
  165. package/dist/capture/dispatch.mjs +17 -5
  166. package/dist/capture/dispatch.mjs.map +1 -1
  167. package/dist/capture/enrich.mjs +1 -2
  168. package/dist/capture/failure-sets.mjs +1 -2
  169. package/dist/capture/fingerprint.d.mts +19 -8
  170. package/dist/capture/fingerprint.mjs +43 -14
  171. package/dist/capture/fingerprint.mjs.map +1 -1
  172. package/dist/capture/forked/channel.d.mts +0 -2
  173. package/dist/capture/forked/child.d.mts +0 -2
  174. package/dist/capture/forked/child.mjs +1 -2
  175. package/dist/capture/forked/crash-tracker.mjs +1 -2
  176. package/dist/capture/forked/decision.d.mts +0 -2
  177. package/dist/capture/forked/decision.mjs +1 -2
  178. package/dist/capture/forked/exit.mjs +1 -2
  179. package/dist/capture/forked/fork.d.mts +0 -2
  180. package/dist/capture/forked/fork.mjs +1 -2
  181. package/dist/capture/forked/forked-capture.d.mts +0 -2
  182. package/dist/capture/forked/forked-capture.mjs +1 -2
  183. package/dist/capture/forked/lease-book.mjs +1 -2
  184. package/dist/capture/forked/protocol.d.mts +0 -2
  185. package/dist/capture/forked/protocol.mjs +1 -2
  186. package/dist/capture/forked/runs.mjs +1 -2
  187. package/dist/capture/guards.mjs +1 -2
  188. package/dist/capture/ignore-regions.d.mts +36 -0
  189. package/dist/capture/ignore-regions.mjs +65 -0
  190. package/dist/capture/ignore-regions.mjs.map +1 -0
  191. package/dist/capture/isolate.mjs +1 -2
  192. package/dist/capture/job-lease.d.mts +0 -2
  193. package/dist/capture/job-lease.mjs +1 -2
  194. package/dist/capture/matrix.mjs +4 -4
  195. package/dist/capture/matrix.mjs.map +1 -1
  196. package/dist/capture/modes.d.mts +0 -2
  197. package/dist/capture/modes.mjs +2 -3
  198. package/dist/capture/modes.mjs.map +1 -1
  199. package/dist/capture/orchestrator.d.mts +0 -2
  200. package/dist/capture/orchestrator.mjs +2 -3
  201. package/dist/capture/pipeline.d.mts +0 -2
  202. package/dist/capture/pipeline.mjs +44 -10
  203. package/dist/capture/pipeline.mjs.map +1 -1
  204. package/dist/capture/progress.d.mts +0 -2
  205. package/dist/capture/progress.mjs +1 -2
  206. package/dist/capture/publish/accept.d.mts +0 -2
  207. package/dist/capture/publish/accept.mjs +1 -2
  208. package/dist/capture/publish/complete.d.mts +1 -3
  209. package/dist/capture/publish/complete.mjs +1 -2
  210. package/dist/capture/publish/extract.d.mts +0 -2
  211. package/dist/capture/publish/extract.mjs +6 -5
  212. package/dist/capture/publish/extract.mjs.map +1 -1
  213. package/dist/capture/publish/handler.d.mts +1 -3
  214. package/dist/capture/publish/handler.mjs +1 -2
  215. package/dist/capture/publish/index.d.mts +2 -1
  216. package/dist/capture/publish/invalid-zip.d.mts +8 -0
  217. package/dist/capture/publish/invalid-zip.mjs +11 -0
  218. package/dist/capture/publish/invalid-zip.mjs.map +1 -0
  219. package/dist/capture/publish/limits.d.mts +0 -2
  220. package/dist/capture/publish/limits.mjs +1 -2
  221. package/dist/capture/publish/published.d.mts +0 -2
  222. package/dist/capture/publish/published.mjs +1 -2
  223. package/dist/capture/publish/run.d.mts +1 -3
  224. package/dist/capture/publish/run.mjs +1 -2
  225. package/dist/capture/rediff.d.mts +1 -3
  226. package/dist/capture/rediff.mjs +19 -6
  227. package/dist/capture/rediff.mjs.map +1 -1
  228. package/dist/capture/render.mjs +1 -2
  229. package/dist/capture/render.mjs.map +1 -1
  230. package/dist/capture/scratch.d.mts +0 -2
  231. package/dist/capture/scratch.mjs +2 -4
  232. package/dist/capture/scratch.mjs.map +1 -1
  233. package/dist/capture/shard-plan.mjs +1 -2
  234. package/dist/capture/shards/context.mjs +3 -3
  235. package/dist/capture/shards/context.mjs.map +1 -1
  236. package/dist/capture/shards/execute.mjs +4 -3
  237. package/dist/capture/shards/execute.mjs.map +1 -1
  238. package/dist/capture/shards/fan-out.mjs +1 -2
  239. package/dist/capture/shards/history.mjs +1 -2
  240. package/dist/capture/shards/reduce.mjs +1 -2
  241. package/dist/capture/sizing.d.mts +0 -2
  242. package/dist/capture/sizing.mjs +1 -2
  243. package/dist/capture/skip-checks.mjs +1 -2
  244. package/dist/capture/statics.d.mts +0 -2
  245. package/dist/capture/statics.mjs +2 -4
  246. package/dist/capture/statics.mjs.map +1 -1
  247. package/dist/capture/storybook-urls.d.mts +0 -2
  248. package/dist/capture/storybook-urls.mjs +3 -3
  249. package/dist/capture/storybook-urls.mjs.map +1 -1
  250. package/dist/capture/storybook.d.mts +0 -2
  251. package/dist/capture/storybook.mjs +8 -4
  252. package/dist/capture/storybook.mjs.map +1 -1
  253. package/dist/capture/target.mjs +1 -2
  254. package/dist/capture/targets.d.mts +0 -2
  255. package/dist/capture/targets.mjs +1 -2
  256. package/dist/capture/tests.d.mts +0 -2
  257. package/dist/capture/tests.mjs +13 -3
  258. package/dist/capture/tests.mjs.map +1 -1
  259. package/dist/capture/token-chunks.mjs +24 -7
  260. package/dist/capture/token-chunks.mjs.map +1 -1
  261. package/dist/capture.d.mts +5 -2
  262. package/dist/capture.mjs +5 -2
  263. package/dist/config.d.mts +16 -3
  264. package/dist/config.mjs +6 -3
  265. package/dist/config.mjs.map +1 -1
  266. package/dist/diff.d.mts +0 -2
  267. package/dist/diff.mjs +1 -2
  268. package/dist/index.d.mts +2 -2
  269. package/dist/index.mjs +2 -2
  270. package/dist/insights/budget.d.mts +0 -2
  271. package/dist/insights/budget.mjs +1 -2
  272. package/dist/insights/envelope.d.mts +0 -2
  273. package/dist/insights/envelope.mjs +1 -2
  274. package/dist/insights/evidence-health.d.mts +0 -2
  275. package/dist/insights/evidence-health.mjs +1 -2
  276. package/dist/insights/evidence.d.mts +0 -2
  277. package/dist/insights/evidence.mjs +1 -2
  278. package/dist/insights/hash.d.mts +0 -2
  279. package/dist/insights/hash.mjs +1 -2
  280. package/dist/insights/limits.d.mts +0 -2
  281. package/dist/insights/limits.mjs +1 -2
  282. package/dist/insights/prompts/v1.d.mts +0 -2
  283. package/dist/insights/prompts/v1.mjs +1 -2
  284. package/dist/insights/redact.d.mts +0 -2
  285. package/dist/insights/redact.mjs +1 -2
  286. package/dist/insights/text.mjs +1 -2
  287. package/dist/logger.d.mts +0 -2
  288. package/dist/logger.mjs +1 -2
  289. package/dist/models/ai-budget-alert.d.mts +0 -2
  290. package/dist/models/ai-budget-alert.mjs +1 -2
  291. package/dist/models/ai-usage.d.mts +0 -2
  292. package/dist/models/ai-usage.mjs +1 -2
  293. package/dist/models/baseline-approval.d.mts +18 -0
  294. package/dist/models/baseline-approval.mjs +41 -0
  295. package/dist/models/baseline-approval.mjs.map +1 -0
  296. package/dist/models/baseline-archive.d.mts +0 -2
  297. package/dist/models/baseline-archive.mjs +1 -2
  298. package/dist/models/baseline-atomic.mjs +66 -0
  299. package/dist/models/baseline-atomic.mjs.map +1 -0
  300. package/dist/models/baseline-conflict.d.mts +6 -0
  301. package/dist/models/baseline-conflict.mjs +9 -0
  302. package/dist/models/baseline-conflict.mjs.map +1 -0
  303. package/dist/models/baseline-export.d.mts +0 -2
  304. package/dist/models/baseline-export.mjs +1 -2
  305. package/dist/models/baseline-import.mjs +1 -2
  306. package/dist/models/baseline-origin.d.mts +16 -0
  307. package/dist/models/baseline-origin.mjs +35 -0
  308. package/dist/models/baseline-origin.mjs.map +1 -0
  309. package/dist/models/baseline-status.d.mts +0 -2
  310. package/dist/models/baseline-status.mjs +1 -2
  311. package/dist/models/baseline-transfer.d.mts +1 -2
  312. package/dist/models/baseline-transfer.mjs +30 -2
  313. package/dist/models/baseline-transfer.mjs.map +1 -1
  314. package/dist/models/baseline.d.mts +5 -2
  315. package/dist/models/baseline.mjs +91 -12
  316. package/dist/models/baseline.mjs.map +1 -1
  317. package/dist/models/build-component.d.mts +0 -2
  318. package/dist/models/build-component.mjs +1 -2
  319. package/dist/models/build-review-status.d.mts +0 -2
  320. package/dist/models/build-review-status.mjs +1 -2
  321. package/dist/models/build-review.d.mts +0 -2
  322. package/dist/models/build-review.mjs +1 -2
  323. package/dist/models/build.d.mts +1 -3
  324. package/dist/models/build.mjs +14 -15
  325. package/dist/models/build.mjs.map +1 -1
  326. package/dist/models/capture-attempt.d.mts +0 -2
  327. package/dist/models/capture-attempt.mjs +1 -2
  328. package/dist/models/capture-job.d.mts +0 -2
  329. package/dist/models/capture-job.mjs +1 -2
  330. package/dist/models/capture-log.d.mts +0 -2
  331. package/dist/models/capture-log.mjs +1 -2
  332. package/dist/models/capture-shard.d.mts +0 -2
  333. package/dist/models/capture-shard.mjs +1 -2
  334. package/dist/models/changeset.d.mts +0 -2
  335. package/dist/models/changeset.mjs +1 -2
  336. package/dist/models/columns.mjs +1 -2
  337. package/dist/models/comment.d.mts +0 -2
  338. package/dist/models/comment.mjs +1 -2
  339. package/dist/models/component.d.mts +0 -2
  340. package/dist/models/component.mjs +1 -2
  341. package/dist/models/connections.d.mts +0 -2
  342. package/dist/models/connections.mjs +1 -2
  343. package/dist/models/content-ref.d.mts +0 -2
  344. package/dist/models/content-ref.mjs +1 -2
  345. package/dist/models/credentials.d.mts +0 -2
  346. package/dist/models/credentials.mjs +1 -2
  347. package/dist/models/design-credentials.d.mts +0 -2
  348. package/dist/models/design-credentials.mjs +1 -2
  349. package/dist/models/design-health.d.mts +16 -3
  350. package/dist/models/design-health.mjs +21 -3
  351. package/dist/models/design-health.mjs.map +1 -1
  352. package/dist/models/design-nodes.d.mts +11 -4
  353. package/dist/models/design-nodes.mjs +18 -3
  354. package/dist/models/design-nodes.mjs.map +1 -1
  355. package/dist/models/design-renders.mjs +1 -2
  356. package/dist/models/design-resolve.d.mts +0 -2
  357. package/dist/models/design-resolve.mjs +1 -2
  358. package/dist/models/design.d.mts +0 -2
  359. package/dist/models/design.mjs +1 -2
  360. package/dist/models/inherited-ratio.d.mts +0 -2
  361. package/dist/models/inherited-ratio.mjs +1 -2
  362. package/dist/models/insight.d.mts +0 -2
  363. package/dist/models/insight.mjs +1 -2
  364. package/dist/models/label.d.mts +0 -2
  365. package/dist/models/label.mjs +1 -2
  366. package/dist/models/member.d.mts +0 -2
  367. package/dist/models/member.mjs +1 -2
  368. package/dist/models/notification-channel.d.mts +0 -2
  369. package/dist/models/notification-channel.mjs +1 -2
  370. package/dist/models/notification-subscription.d.mts +0 -2
  371. package/dist/models/notification-subscription.mjs +1 -2
  372. package/dist/models/project-group-mapping.d.mts +0 -2
  373. package/dist/models/project-group-mapping.mjs +1 -2
  374. package/dist/models/project.d.mts +0 -2
  375. package/dist/models/project.mjs +1 -2
  376. package/dist/models/public-origin.d.mts +0 -2
  377. package/dist/models/public-origin.mjs +1 -2
  378. package/dist/models/review-switch-off.d.mts +0 -2
  379. package/dist/models/review-switch-off.mjs +1 -2
  380. package/dist/models/site-settings.d.mts +0 -2
  381. package/dist/models/site-settings.mjs +1 -2
  382. package/dist/models/snapshot.d.mts +3 -2
  383. package/dist/models/snapshot.mjs +4 -2
  384. package/dist/models/snapshot.mjs.map +1 -1
  385. package/dist/models/token.d.mts +0 -2
  386. package/dist/models/token.mjs +1 -2
  387. package/dist/models/usage.d.mts +0 -2
  388. package/dist/models/usage.mjs +1 -2
  389. package/dist/models/user.d.mts +0 -2
  390. package/dist/models/user.mjs +1 -2
  391. package/dist/models/webhook.d.mts +0 -2
  392. package/dist/models/webhook.mjs +1 -2
  393. package/dist/models.d.mts +6 -3
  394. package/dist/models.mjs +6 -3
  395. package/dist/options.d.mts +0 -2
  396. package/dist/options.mjs +2 -4
  397. package/dist/options.mjs.map +1 -1
  398. package/dist/paths.d.mts +3 -3
  399. package/dist/paths.mjs +7 -3
  400. package/dist/paths.mjs.map +1 -1
  401. package/dist/retention/ai.d.mts +1 -3
  402. package/dist/retention/ai.mjs +0 -1
  403. package/dist/retention/pending.mjs +1 -2
  404. package/dist/retention/published-gzip-sweep.mjs +28 -0
  405. package/dist/retention/published-gzip-sweep.mjs.map +1 -0
  406. package/dist/retention/screenshot-sweep.d.mts +0 -2
  407. package/dist/retention/screenshot-sweep.mjs +1 -2
  408. package/dist/retention.d.mts +0 -2
  409. package/dist/retention.mjs +5 -2
  410. package/dist/retention.mjs.map +1 -1
  411. package/dist/schema/baseline.d.mts +2 -2
  412. package/dist/schema/build-component.d.mts +0 -2
  413. package/dist/schema/build-review.d.mts +0 -2
  414. package/dist/schema/build.d.mts +0 -2
  415. package/dist/schema/capture-attempt.d.mts +0 -2
  416. package/dist/schema/capture-job.d.mts +0 -2
  417. package/dist/schema/capture-shard.d.mts +0 -2
  418. package/dist/schema/comment.d.mts +0 -2
  419. package/dist/schema/connection.d.mts +0 -2
  420. package/dist/schema/content-ref.d.mts +0 -2
  421. package/dist/schema/design.d.mts +2 -2
  422. package/dist/schema/insight.d.mts +0 -2
  423. package/dist/schema/label.d.mts +0 -2
  424. package/dist/schema/member.d.mts +0 -2
  425. package/dist/schema/notification.d.mts +0 -2
  426. package/dist/schema/project-group-mapping.d.mts +0 -2
  427. package/dist/schema/project.d.mts +0 -2
  428. package/dist/schema/site-setting.d.mts +0 -2
  429. package/dist/schema/snapshot.d.mts +5 -2
  430. package/dist/schema/token.d.mts +0 -2
  431. package/dist/schema/usage.d.mts +0 -2
  432. package/dist/schema/user.d.mts +0 -2
  433. package/dist/schema/webhook.d.mts +0 -2
  434. package/dist/schema.d.mts +4 -6
  435. package/dist/serverless.d.mts +0 -2
  436. package/dist/serverless.mjs +1 -2
  437. package/dist/test-helpers/build-reference-contract.d.mts +14 -0
  438. package/dist/test-helpers/build-reference-contract.mjs +66 -0
  439. package/dist/test-helpers/build-reference-contract.mjs.map +1 -0
  440. package/dist/test-helpers/create-project.d.mts +0 -2
  441. package/dist/test-helpers/create-project.mjs +1 -2
  442. package/dist/test-helpers/database-contract.d.mts +0 -2
  443. package/dist/test-helpers/database-contract.mjs +37 -2
  444. package/dist/test-helpers/database-contract.mjs.map +1 -1
  445. package/dist/test-helpers/deferred.d.mts +0 -2
  446. package/dist/test-helpers/deferred.mjs +1 -2
  447. package/dist/test-helpers/design-contract.d.mts +0 -2
  448. package/dist/test-helpers/design-contract.mjs +1 -2
  449. package/dist/test-helpers/fake-adapters.d.mts +0 -2
  450. package/dist/test-helpers/fake-adapters.mjs +1 -2
  451. package/dist/test-helpers/fake-database.d.mts +0 -2
  452. package/dist/test-helpers/fake-database.mjs +7 -2
  453. package/dist/test-helpers/fake-database.mjs.map +1 -1
  454. package/dist/test-helpers/fake-design.d.mts +0 -2
  455. package/dist/test-helpers/fake-design.mjs +1 -2
  456. package/dist/test-helpers/fake-notifier.d.mts +0 -2
  457. package/dist/test-helpers/fake-notifier.mjs +1 -2
  458. package/dist/test-helpers/fake-storage.d.mts +0 -2
  459. package/dist/test-helpers/fake-storage.mjs +1 -2
  460. package/dist/test-helpers/fake-tables.mjs +6 -2
  461. package/dist/test-helpers/fake-tables.mjs.map +1 -1
  462. package/dist/test-helpers/manual-clock.d.mts +0 -2
  463. package/dist/test-helpers/manual-clock.mjs +1 -2
  464. package/dist/test-helpers/notifier-contract.d.mts +0 -2
  465. package/dist/test-helpers/notifier-contract.mjs +1 -2
  466. package/dist/test-helpers/queue-contract.d.mts +0 -2
  467. package/dist/test-helpers/queue-contract.mjs +1 -2
  468. package/dist/test-helpers/queue-multi-instance.d.mts +0 -2
  469. package/dist/test-helpers/queue-multi-instance.mjs +1 -2
  470. package/dist/test-helpers/runner-contract.d.mts +0 -2
  471. package/dist/test-helpers/runner-contract.mjs +1 -2
  472. package/dist/test-helpers/sql-chunks.mjs +2 -2
  473. package/dist/test-helpers/sql-chunks.mjs.map +1 -1
  474. package/dist/test-helpers/storage-contract.d.mts +0 -2
  475. package/dist/test-helpers/storage-contract.mjs +1 -2
  476. package/dist/test-helpers.d.mts +2 -1
  477. package/dist/test-helpers.mjs +2 -1
  478. package/dist/tracing.mjs +36 -6
  479. package/dist/tracing.mjs.map +1 -1
  480. package/dist/types.d.mts +0 -2
  481. package/dist/types.mjs +1 -2
  482. package/dist/urls.d.mts +0 -2
  483. package/dist/urls.mjs +1 -2
  484. package/dist/utils/adaptive-concurrency.d.mts +0 -2
  485. package/dist/utils/adaptive-concurrency.mjs +1 -2
  486. package/dist/utils/connect-retry.d.mts +2 -2
  487. package/dist/utils/connect-retry.mjs +2 -3
  488. package/dist/utils/connect-retry.mjs.map +1 -1
  489. package/dist/utils/encrypt.d.mts +0 -2
  490. package/dist/utils/encrypt.mjs +1 -2
  491. package/dist/utils/environment.d.mts +0 -2
  492. package/dist/utils/environment.mjs +1 -2
  493. package/dist/utils/hash.d.mts +0 -2
  494. package/dist/utils/hash.mjs +1 -2
  495. package/dist/utils/http.d.mts +13 -3
  496. package/dist/utils/http.mjs +79 -17
  497. package/dist/utils/http.mjs.map +1 -1
  498. package/dist/utils/map-adaptive.d.mts +0 -2
  499. package/dist/utils/map-adaptive.mjs +1 -2
  500. package/dist/utils/map-limit.d.mts +0 -2
  501. package/dist/utils/map-limit.mjs +1 -2
  502. package/dist/utils/mime.d.mts +0 -2
  503. package/dist/utils/mime.mjs +1 -2
  504. package/dist/utils/page-limiter.d.mts +0 -2
  505. package/dist/utils/page-limiter.mjs +1 -2
  506. package/dist/utils/path-security.d.mts +0 -2
  507. package/dist/utils/path-security.mjs +2 -4
  508. package/dist/utils/path-security.mjs.map +1 -1
  509. package/dist/utils/public-origin.d.mts +0 -2
  510. package/dist/utils/public-origin.mjs +1 -2
  511. package/dist/utils/redact.d.mts +0 -2
  512. package/dist/utils/redact.mjs +1 -2
  513. package/dist/utils/remote-environment.d.mts +40 -3
  514. package/dist/utils/remote-environment.mjs +52 -3
  515. package/dist/utils/remote-environment.mjs.map +1 -1
  516. package/dist/utils/resources.d.mts +0 -2
  517. package/dist/utils/resources.mjs +1 -2
  518. package/dist/utils/screenshot-store.d.mts +0 -2
  519. package/dist/utils/screenshot-store.mjs +1 -2
  520. package/dist/utils/session-gate.d.mts +7 -6
  521. package/dist/utils/session-gate.mjs +28 -12
  522. package/dist/utils/session-gate.mjs.map +1 -1
  523. package/dist/utils/storybook-token.d.mts +11 -3
  524. package/dist/utils/storybook-token.mjs +10 -9
  525. package/dist/utils/storybook-token.mjs.map +1 -1
  526. package/dist/utils/storybook-url.d.mts +0 -2
  527. package/dist/utils/storybook-url.mjs +1 -2
  528. package/dist/utils/timing.d.mts +0 -2
  529. package/dist/utils/timing.mjs +1 -2
  530. package/dist/utils/ulid.d.mts +0 -2
  531. package/dist/utils/ulid.mjs +1 -2
  532. package/dist/utils/zip-read.mjs +1 -2
  533. package/dist/utils/zip-write.d.mts +0 -2
  534. package/dist/utils/zip-write.mjs +1 -2
  535. package/dist/utils.d.mts +5 -5
  536. package/dist/utils.mjs +5 -5
  537. package/package.json +6 -15
@@ -3,7 +3,7 @@ import "node:url";
3
3
  import.meta.url;
4
4
  import { BuildModel } from "./build.mjs";
5
5
  import { BuildComponentModel } from "./build-component.mjs";
6
- //#region src/models/changeset.ts
6
+ // src/models/changeset.ts
7
7
  /**
8
8
  * Compare a build's components with the baseline's. Components match by story file, or by title
9
9
  * when the index carries no file. With no baseline (`null`) every component is `added`.
@@ -75,7 +75,6 @@ function summary(row) {
75
75
  function byTitle(a, b) {
76
76
  return a.title.localeCompare(b.title) || a.importPath.localeCompare(b.importPath);
77
77
  }
78
- //#endregion
79
78
  export { changesetOf, computeChangeset, findChangesetBaseline };
80
79
 
81
80
  //# sourceMappingURL=changeset.mjs.map
@@ -2,7 +2,7 @@ import "node:path";
2
2
  import "node:url";
3
3
  import.meta.url;
4
4
  import { getTableColumns } from "drizzle-orm";
5
- //#region src/models/columns.ts
5
+ // src/models/columns.ts
6
6
  /**
7
7
  * Column handle for query builders (`eq`, `inArray`, `desc`, …) from a table
8
8
  * handle and a property name. Throws on an unknown name instead of letting
@@ -13,7 +13,6 @@ function col(table, name) {
13
13
  if (!column) throw new Error(`Unknown column "${name}"`);
14
14
  return column;
15
15
  }
16
- //#endregion
17
16
  export { col };
18
17
 
19
18
  //# sourceMappingURL=columns.mjs.map
@@ -3,7 +3,6 @@ import __tsdown_shims_url from 'node:url';
3
3
  import { DatabaseAdapter } from "../adapter/database.mjs";
4
4
  import { Comment } from "../schema/comment.mjs";
5
5
  import { Table } from "drizzle-orm";
6
- //#region src/models/comment.d.ts
7
6
  /** Tables required by {@link CommentModel}. */
8
7
  interface CommentTables {
9
8
  comments: Table;
@@ -27,6 +26,5 @@ interface CommentCreateInput {
27
26
  storyId?: string;
28
27
  parentId?: string;
29
28
  }
30
- //#endregion
31
29
  export { CommentCreateInput, CommentModel, CommentTables };
32
30
  //# sourceMappingURL=comment.d.mts.map
@@ -4,7 +4,7 @@ import.meta.url;
4
4
  import { ulid } from "../utils/ulid.mjs";
5
5
  import { col } from "./columns.mjs";
6
6
  import { eq } from "drizzle-orm";
7
- //#region src/models/comment.ts
7
+ // src/models/comment.ts
8
8
  /** Build and snapshot review comments. */
9
9
  /** Data operations for review comments. */
10
10
  var CommentModel = class {
@@ -45,7 +45,6 @@ var CommentModel = class {
45
45
  });
46
46
  }
47
47
  };
48
- //#endregion
49
48
  export { CommentModel };
50
49
 
51
50
  //# sourceMappingURL=comment.mjs.map
@@ -3,7 +3,6 @@ import __tsdown_shims_url from 'node:url';
3
3
  import { SnapshotStatus } from "../types.mjs";
4
4
  import { Build } from "../schema/build.mjs";
5
5
  import { Snapshot } from "../schema/snapshot.mjs";
6
- //#region src/models/component.d.ts
7
6
  /** The most attention-worthy status of a set (unchanged when empty). */
8
7
  declare function worstStatus(statuses: readonly SnapshotStatus[]): SnapshotStatus;
9
8
  /** One build's view of a component. */
@@ -38,6 +37,5 @@ type ComponentPresence = {
38
37
  * every file, used to find a moved file by its last-seen title.
39
38
  */
40
39
  declare function componentPresence(history: readonly ComponentHistoryRow[], latestBuildId: string | null, latest: readonly Pick<Snapshot, "storyTitle" | "storyImportPath">[], lastTitle: string | null): ComponentPresence;
41
- //#endregion
42
40
  export { ComponentHistoryRow, ComponentPresence, componentHistory, componentPresence, worstStatus };
43
41
  //# sourceMappingURL=component.d.mts.map
@@ -1,7 +1,7 @@
1
1
  import "node:path";
2
2
  import "node:url";
3
3
  import.meta.url;
4
- //#region src/models/component.ts
4
+ // src/models/component.ts
5
5
  /** Worst-first order used for status roll-ups. */
6
6
  const STATUS_PRIORITY = [
7
7
  "rejected",
@@ -54,7 +54,6 @@ function componentPresence(history, latestBuildId, latest, lastTitle) {
54
54
  lastSeen: newest.build
55
55
  };
56
56
  }
57
- //#endregion
58
57
  export { componentHistory, componentPresence, worstStatus };
59
58
 
60
59
  //# sourceMappingURL=component.mjs.map
@@ -5,7 +5,6 @@ import { OAuthClient, OAuthTokens } from "../adapter/oauth.mjs";
5
5
  import { SecretInput } from "../utils/encrypt.mjs";
6
6
  import { UserConnection } from "../schema/connection.mjs";
7
7
  import { Table } from "drizzle-orm";
8
- //#region src/models/connections.d.ts
9
8
  /** Tables required by {@link ConnectionModel}. */
10
9
  interface ConnectionTables {
11
10
  userConnections: Table;
@@ -36,6 +35,5 @@ declare class ConnectionModel {
36
35
  accessToken(userId: string, service: string, oauth?: Pick<OAuthClient, "refresh">, now?: Date): Promise<string | null>;
37
36
  private refreshOnce;
38
37
  }
39
- //#endregion
40
38
  export { ConnectionModel, ConnectionTables };
41
39
  //# sourceMappingURL=connections.d.mts.map
@@ -5,7 +5,7 @@ import { decryptCredential, encrypt } from "../utils/encrypt.mjs";
5
5
  import { ulid } from "../utils/ulid.mjs";
6
6
  import { col } from "./columns.mjs";
7
7
  import { and, eq } from "drizzle-orm";
8
- //#region src/models/connections.ts
8
+ // src/models/connections.ts
9
9
  /** A user's own OAuth connections to external services: encrypted tokens, lazy refresh on use. */
10
10
  /** Refresh this long before expiry so a call never starts with a token about to lapse. */
11
11
  const REFRESH_MARGIN_MS = 6e4;
@@ -106,7 +106,6 @@ var ConnectionModel = class {
106
106
  function isExpiring(expiresAt, now) {
107
107
  return expiresAt !== null && Date.parse(expiresAt) - now.getTime() < REFRESH_MARGIN_MS;
108
108
  }
109
- //#endregion
110
109
  export { ConnectionModel };
111
110
 
112
111
  //# sourceMappingURL=connections.mjs.map
@@ -3,7 +3,6 @@ import __tsdown_shims_url from 'node:url';
3
3
  import { DatabaseAdapter } from "../adapter/database.mjs";
4
4
  import { ContentRef } from "../schema/content-ref.mjs";
5
5
  import { Table } from "drizzle-orm";
6
- //#region src/models/content-ref.d.ts
7
6
  /** Content refs for deduplicated Storybook assets. */
8
7
  declare class ContentRefModel {
9
8
  private readonly tables;
@@ -16,6 +15,5 @@ declare class ContentRefModel {
16
15
  decrement(hash: string): Promise<ContentRef | null>;
17
16
  listStale(cutoff: string): Promise<ContentRef[]>;
18
17
  }
19
- //#endregion
20
18
  export { ContentRefModel };
21
19
  //# sourceMappingURL=content-ref.d.mts.map
@@ -1,7 +1,7 @@
1
1
  import "node:path";
2
2
  import "node:url";
3
3
  import.meta.url;
4
- //#region src/models/content-ref.ts
4
+ // src/models/content-ref.ts
5
5
  /** Content refs for deduplicated Storybook assets. */
6
6
  var ContentRefModel = class {
7
7
  tables;
@@ -43,7 +43,6 @@ var ContentRefModel = class {
43
43
  return (await this.db.list(this.tables.contentRefs)).filter((r) => r.refCount === 0 && r.lastSeenAt < cutoff);
44
44
  }
45
45
  };
46
- //#endregion
47
46
  export { ContentRefModel };
48
47
 
49
48
  //# sourceMappingURL=content-ref.mjs.map
@@ -2,7 +2,6 @@ import __tsdown_shims_path from 'node:path';
2
2
  import __tsdown_shims_url from 'node:url';
3
3
  import { DatabaseAdapter } from "../adapter/database.mjs";
4
4
  import { SecretKeys } from "../utils/encrypt.mjs";
5
- //#region src/models/credentials.d.ts
6
5
  /** A row no configured key can decrypt. */
7
6
  interface UnreadableCredential {
8
7
  source: string;
@@ -31,6 +30,5 @@ declare function probeCredentials(db: DatabaseAdapter, keys: SecretKeys): Promis
31
30
  * write does not stop the rest.
32
31
  */
33
32
  declare function reencryptCredentials(db: DatabaseAdapter, keys: SecretKeys): Promise<ReencryptResult>;
34
- //#endregion
35
33
  export { CredentialProbe, ReencryptResult, UnreadableCredential, probeCredentials, reencryptCredentials };
36
34
  //# sourceMappingURL=credentials.d.mts.map
@@ -2,7 +2,7 @@ import "node:path";
2
2
  import "node:url";
3
3
  import.meta.url;
4
4
  import { decryptWithKey, encrypt } from "../utils/encrypt.mjs";
5
- //#region src/models/credentials.ts
5
+ // src/models/credentials.ts
6
6
  /** Tables that hold encrypted credentials, with the column to read. */
7
7
  const SOURCES = [
8
8
  {
@@ -107,7 +107,6 @@ async function reencryptOne(db, keys, item) {
107
107
  return { state: "failed" };
108
108
  }
109
109
  }
110
- //#endregion
111
110
  export { probeCredentials, reencryptCredentials };
112
111
 
113
112
  //# sourceMappingURL=credentials.mjs.map
@@ -5,7 +5,6 @@ import { SecretInput } from "../utils/encrypt.mjs";
5
5
  import { DesignCredential } from "../schema/design.mjs";
6
6
  import { DesignCredentialStatus } from "./design-resolve.mjs";
7
7
  import { Table } from "drizzle-orm";
8
- //#region src/models/design-credentials.d.ts
9
8
  /** Tables required by {@link DesignCredentialModel}. */
10
9
  interface DesignCredentialTables {
11
10
  designCredentials: Table;
@@ -36,6 +35,5 @@ declare class DesignCredentialModel {
36
35
  recordStatus(projectId: string, provider: string, status: DesignCredentialStatus): Promise<void>;
37
36
  private replace;
38
37
  }
39
- //#endregion
40
38
  export { DesignCredentialModel, DesignCredentialTables, PROJECT_SCOPE };
41
39
  //# sourceMappingURL=design-credentials.d.mts.map
@@ -5,7 +5,7 @@ import { decryptCredential, encrypt } from "../utils/encrypt.mjs";
5
5
  import { ulid } from "../utils/ulid.mjs";
6
6
  import { col } from "./columns.mjs";
7
7
  import { and, eq } from "drizzle-orm";
8
- //#region src/models/design-credentials.ts
8
+ // src/models/design-credentials.ts
9
9
  /** Encrypted design-provider credentials: the project-wide token an admin pastes. */
10
10
  /** Cache scope of the shared project credential (`design_nodes.viewer_id`); a user id marks a member's own connection. */
11
11
  const PROJECT_SCOPE = "";
@@ -80,7 +80,6 @@ var DesignCredentialModel = class {
80
80
  return true;
81
81
  }
82
82
  };
83
- //#endregion
84
83
  export { DesignCredentialModel, PROJECT_SCOPE };
85
84
 
86
85
  //# sourceMappingURL=design-credentials.mjs.map
@@ -1,16 +1,29 @@
1
1
  import __tsdown_shims_path from 'node:path';
2
2
  import __tsdown_shims_url from 'node:url';
3
3
  import { DesignNode } from "../schema/design.mjs";
4
- //#region src/models/design-health.d.ts
5
4
  /** Cached metadata younger than this is served without a refetch. */
6
5
  declare const DESIGN_TTL_MS: number;
7
6
  /** True when the design changed after the baseline it is compared with was approved. */
8
7
  declare function isDesignStale(node: Pick<DesignNode, "state" | "lastModified"> | null | undefined, baselineVersion: string | null | undefined): boolean;
8
+ /** What happened to a design after its baseline was approved. */
9
+ type DesignChangeKind = "modified-after-approval" | "deleted";
10
+ /** A design change to tell people about, with the identity it is de-duplicated on. */
11
+ interface DesignChange {
12
+ change: DesignChangeKind;
13
+ /** `<change>:<version>`: one notification per design version and kind of change. */
14
+ key: string;
15
+ /** The design's version when it changed (a deleted node keeps its last known one). */
16
+ designVersion: string | null;
17
+ }
18
+ /**
19
+ * The change worth a `design:changed` notification, or null. A deleted node always counts; a
20
+ * modified one only when it moved after `baselineVersion` (see {@link isDesignStale}).
21
+ */
22
+ declare function designChange(node: Pick<DesignNode, "state" | "version" | "lastModified"> | null | undefined, baselineVersion: string | null | undefined): DesignChange | null;
9
23
  /**
10
24
  * Days until a credential passes its provider's maximum lifetime (negative once
11
25
  * past). `issuedAt` is when the credential was set or replaced — never a status write.
12
26
  */
13
27
  declare function credentialDaysLeft(issuedAt: string, maxAgeDays: number, now?: Date): number;
14
- //#endregion
15
- export { DESIGN_TTL_MS, credentialDaysLeft, isDesignStale };
28
+ export { DESIGN_TTL_MS, DesignChange, DesignChangeKind, credentialDaysLeft, designChange, isDesignStale };
16
29
  //# sourceMappingURL=design-health.d.mts.map
@@ -1,7 +1,7 @@
1
1
  import "node:path";
2
2
  import "node:url";
3
3
  import.meta.url;
4
- //#region src/models/design-health.ts
4
+ // src/models/design-health.ts
5
5
  /** Cached metadata younger than this is served without a refetch. */
6
6
  const DESIGN_TTL_MS = 9e5;
7
7
  /** True when the design changed after the baseline it is compared with was approved. */
@@ -12,6 +12,25 @@ function isDesignStale(node, baselineVersion) {
12
12
  return Number.isFinite(modified) && Number.isFinite(approved) && modified > approved;
13
13
  }
14
14
  /**
15
+ * The change worth a `design:changed` notification, or null. A deleted node always counts; a
16
+ * modified one only when it moved after `baselineVersion` (see {@link isDesignStale}).
17
+ */
18
+ function designChange(node, baselineVersion) {
19
+ if (!node) return null;
20
+ const designVersion = node.version ?? node.lastModified;
21
+ if (node.state === "deleted") return {
22
+ change: "deleted",
23
+ key: `deleted:${designVersion ?? ""}`,
24
+ designVersion
25
+ };
26
+ if (!isDesignStale(node, baselineVersion)) return null;
27
+ return {
28
+ change: "modified-after-approval",
29
+ key: `modified-after-approval:${designVersion ?? ""}`,
30
+ designVersion
31
+ };
32
+ }
33
+ /**
15
34
  * Days until a credential passes its provider's maximum lifetime (negative once
16
35
  * past). `issuedAt` is when the credential was set or replaced — never a status write.
17
36
  */
@@ -19,7 +38,6 @@ function credentialDaysLeft(issuedAt, maxAgeDays, now = /* @__PURE__ */ new Date
19
38
  const ageDays = (now.getTime() - Date.parse(issuedAt)) / 864e5;
20
39
  return Math.ceil(maxAgeDays - ageDays);
21
40
  }
22
- //#endregion
23
- export { DESIGN_TTL_MS, credentialDaysLeft, isDesignStale };
41
+ export { DESIGN_TTL_MS, credentialDaysLeft, designChange, isDesignStale };
24
42
 
25
43
  //# sourceMappingURL=design-health.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"design-health.mjs","names":[],"sources":["../../src/models/design-health.ts"],"sourcesContent":["/** Design cache freshness, staleness and credential-age rules (provider-neutral). */\nimport type { DesignNode } from \"../schema/design.ts\";\n\n/** Cached metadata younger than this is served without a refetch. */\nexport const DESIGN_TTL_MS = 15 * 60_000;\n\n/** True when the design changed after the baseline it is compared with was approved. */\nexport function isDesignStale(\n node: Pick<DesignNode, \"state\" | \"lastModified\"> | null | undefined,\n baselineVersion: string | null | undefined,\n): boolean {\n if (node?.state !== \"ok\" || !node.lastModified || !baselineVersion) return false;\n const modified = Date.parse(node.lastModified);\n const approved = Date.parse(baselineVersion);\n return Number.isFinite(modified) && Number.isFinite(approved) && modified > approved;\n}\n\n/**\n * Days until a credential passes its provider's maximum lifetime (negative once\n * past). `issuedAt` is when the credential was set or replaced — never a status write.\n */\nexport function credentialDaysLeft(\n issuedAt: string,\n maxAgeDays: number,\n now: Date = new Date(),\n): number {\n const ageDays = (now.getTime() - Date.parse(issuedAt)) / 86_400_000;\n return Math.ceil(maxAgeDays - ageDays);\n}\n"],"mappings":";;;;;AAIA,MAAa,gBAAgB;;AAG7B,SAAgB,cACd,MACA,iBACS;CACT,IAAI,MAAM,UAAU,QAAQ,CAAC,KAAK,gBAAgB,CAAC,iBAAiB,OAAO;CAC3E,MAAM,WAAW,KAAK,MAAM,KAAK,YAAY;CAC7C,MAAM,WAAW,KAAK,MAAM,eAAe;CAC3C,OAAO,OAAO,SAAS,QAAQ,KAAK,OAAO,SAAS,QAAQ,KAAK,WAAW;AAC9E;;;;;AAMA,SAAgB,mBACd,UACA,YACA,sBAAY,IAAI,KAAK,GACb;CACR,MAAM,WAAW,IAAI,QAAQ,IAAI,KAAK,MAAM,QAAQ,KAAK;CACzD,OAAO,KAAK,KAAK,aAAa,OAAO;AACvC"}
1
+ {"version":3,"file":"design-health.mjs","names":[],"sources":["../../src/models/design-health.ts"],"sourcesContent":["/** Design cache freshness, staleness and credential-age rules (provider-neutral). */\nimport type { DesignNode } from \"../schema/design.ts\";\n\n/** Cached metadata younger than this is served without a refetch. */\nexport const DESIGN_TTL_MS = 15 * 60_000;\n\n/** True when the design changed after the baseline it is compared with was approved. */\nexport function isDesignStale(\n node: Pick<DesignNode, \"state\" | \"lastModified\"> | null | undefined,\n baselineVersion: string | null | undefined,\n): boolean {\n if (node?.state !== \"ok\" || !node.lastModified || !baselineVersion) return false;\n const modified = Date.parse(node.lastModified);\n const approved = Date.parse(baselineVersion);\n return Number.isFinite(modified) && Number.isFinite(approved) && modified > approved;\n}\n\n/** What happened to a design after its baseline was approved. */\nexport type DesignChangeKind = \"modified-after-approval\" | \"deleted\";\n\n/** A design change to tell people about, with the identity it is de-duplicated on. */\nexport interface DesignChange {\n change: DesignChangeKind;\n /** `<change>:<version>`: one notification per design version and kind of change. */\n key: string;\n /** The design's version when it changed (a deleted node keeps its last known one). */\n designVersion: string | null;\n}\n\n/**\n * The change worth a `design:changed` notification, or null. A deleted node always counts; a\n * modified one only when it moved after `baselineVersion` (see {@link isDesignStale}).\n */\nexport function designChange(\n node: Pick<DesignNode, \"state\" | \"version\" | \"lastModified\"> | null | undefined,\n baselineVersion: string | null | undefined,\n): DesignChange | null {\n if (!node) return null;\n const designVersion = node.version ?? node.lastModified;\n if (node.state === \"deleted\") {\n return { change: \"deleted\", key: `deleted:${designVersion ?? \"\"}`, designVersion };\n }\n if (!isDesignStale(node, baselineVersion)) return null;\n return {\n change: \"modified-after-approval\",\n key: `modified-after-approval:${designVersion ?? \"\"}`,\n designVersion,\n };\n}\n\n/**\n * Days until a credential passes its provider's maximum lifetime (negative once\n * past). `issuedAt` is when the credential was set or replaced — never a status write.\n */\nexport function credentialDaysLeft(\n issuedAt: string,\n maxAgeDays: number,\n now: Date = new Date(),\n): number {\n const ageDays = (now.getTime() - Date.parse(issuedAt)) / 86_400_000;\n return Math.ceil(maxAgeDays - ageDays);\n}\n"],"mappings":";;;;;AAIA,MAAa,gBAAgB;;AAG7B,SAAgB,cACd,MACA,iBACS;CACT,IAAI,MAAM,UAAU,QAAQ,CAAC,KAAK,gBAAgB,CAAC,iBAAiB,OAAO;CAC3E,MAAM,WAAW,KAAK,MAAM,KAAK,YAAY;CAC7C,MAAM,WAAW,KAAK,MAAM,eAAe;CAC3C,OAAO,OAAO,SAAS,QAAQ,KAAK,OAAO,SAAS,QAAQ,KAAK,WAAW;AAC9E;;;;;AAkBA,SAAgB,aACd,MACA,iBACqB;CACrB,IAAI,CAAC,MAAM,OAAO;CAClB,MAAM,gBAAgB,KAAK,WAAW,KAAK;CAC3C,IAAI,KAAK,UAAU,WACjB,OAAO;EAAE,QAAQ;EAAW,KAAK,WAAW,iBAAiB;EAAM;CAAc;CAEnF,IAAI,CAAC,cAAc,MAAM,eAAe,GAAG,OAAO;CAClD,OAAO;EACL,QAAQ;EACR,KAAK,2BAA2B,iBAAiB;EACjD;CACF;AACF;;;;;AAMA,SAAgB,mBACd,UACA,YACA,sBAAY,IAAI,KAAK,GACb;CACR,MAAM,WAAW,IAAI,QAAQ,IAAI,KAAK,MAAM,QAAQ,KAAK;CACzD,OAAO,KAAK,KAAK,aAAa,OAAO;AACvC"}
@@ -1,15 +1,15 @@
1
1
  import __tsdown_shims_path from 'node:path';
2
2
  import __tsdown_shims_url from 'node:url';
3
- import { DatabaseAdapter } from "../adapter/database.mjs";
4
3
  import { Logger } from "../logger.mjs";
4
+ import { DatabaseAdapter } from "../adapter/database.mjs";
5
5
  import { DesignProvider } from "../adapter/design.mjs";
6
6
  import { StorageAdapter } from "../adapter/storage.mjs";
7
- import { ConnectionModel } from "./connections.mjs";
8
7
  import { DesignNode } from "../schema/design.mjs";
8
+ import { ConnectionModel } from "./connections.mjs";
9
9
  import { DesignCredentialModel } from "./design-credentials.mjs";
10
+ import { DesignChange } from "./design-health.mjs";
10
11
  import { Table } from "drizzle-orm";
11
12
  import { Buffer } from "node:buffer";
12
- //#region src/models/design-nodes.d.ts
13
13
  /** Tables required by {@link DesignNodeModel}. */
14
14
  interface DesignNodeTables {
15
15
  designNodes: Table;
@@ -47,6 +47,14 @@ declare class DesignNodeModel {
47
47
  * absent). A row fetched with someone's own connection is only visible to that viewer.
48
48
  */
49
49
  cachedFor(projectId: string, urls: readonly string[], viewerId?: string): Promise<Map<string, DesignNode>>;
50
+ /**
51
+ * Claim the right to announce `change` for a node: true for exactly one caller per
52
+ * `change.key`, even when health checks run concurrently. The claim is a compare-and-set of
53
+ * `notified_version` from the value read here to a fresh `<key>#<token>`; a caller that finds
54
+ * the key already claimed, or loses the swap, gets false. Shared (project-credential) rows only:
55
+ * a member's own-connection row never announces, so one design is never announced twice.
56
+ */
57
+ claimChange(node: DesignNode, change: DesignChange): Promise<boolean>;
50
58
  /** Refresh one link (see {@link refreshMany}); null for unsupported URLs or nothing cached. */
51
59
  refresh(projectId: string, url: string, now?: Date, viewerId?: string): Promise<DesignNode | null>;
52
60
  /**
@@ -78,6 +86,5 @@ declare class DesignNodeModel {
78
86
  */
79
87
  private store;
80
88
  }
81
- //#endregion
82
89
  export { DesignNodeModel, DesignNodeTables, RefreshOptions };
83
90
  //# sourceMappingURL=design-nodes.d.mts.map
@@ -8,8 +8,8 @@ import { mapLimit } from "../utils/map-limit.mjs";
8
8
  import "./design-credentials.mjs";
9
9
  import "./design-health.mjs";
10
10
  import { DesignRenderStore } from "./design-renders.mjs";
11
- import { and, eq } from "drizzle-orm";
12
- //#region src/models/design-nodes.ts
11
+ import { and, eq, isNull } from "drizzle-orm";
12
+ // src/models/design-nodes.ts
13
13
  /** Design metadata + render cache: TTL refresh, batched per file, bounded concurrency, abortable. */
14
14
  /** Most files refreshed in parallel by one {@link DesignNodeModel.refreshMany} call. */
15
15
  const FILE_CONCURRENCY = 3;
@@ -51,6 +51,21 @@ var DesignNodeModel = class {
51
51
  }));
52
52
  return new Map(rows.flatMap(([url, row]) => row ? [[url, row]] : []));
53
53
  }
54
+ /**
55
+ * Claim the right to announce `change` for a node: true for exactly one caller per
56
+ * `change.key`, even when health checks run concurrently. The claim is a compare-and-set of
57
+ * `notified_version` from the value read here to a fresh `<key>#<token>`; a caller that finds
58
+ * the key already claimed, or loses the swap, gets false. Shared (project-credential) rows only:
59
+ * a member's own-connection row never announces, so one design is never announced twice.
60
+ */
61
+ async claimChange(node, change) {
62
+ if (node.viewerId !== "") return false;
63
+ const column = col(this.tables.designNodes, "notifiedVersion");
64
+ const current = await this.db.get(this.tables.designNodes, node.id);
65
+ if (!current || current.notifiedVersion?.startsWith(`${change.key}#`) === true) return false;
66
+ const token = `${change.key}#${ulid()}`;
67
+ return (await this.db.updateWhere(this.tables.designNodes, node.id, { notifiedVersion: token }, current.notifiedVersion === null ? isNull(column) : eq(column, current.notifiedVersion)))?.notifiedVersion === token;
68
+ }
54
69
  /** Refresh one link (see {@link refreshMany}); null for unsupported URLs or nothing cached. */
55
70
  async refresh(projectId, url, now, viewerId) {
56
71
  return (await this.refreshMany(projectId, [url], {
@@ -181,6 +196,7 @@ var DesignNodeModel = class {
181
196
  nodeId: ref.nodeId ?? "",
182
197
  renderPath: null,
183
198
  renderVersion: null,
199
+ notifiedVersion: null,
184
200
  createdAt: stamp,
185
201
  ...values
186
202
  });
@@ -216,7 +232,6 @@ async function memo(cache, key, load) {
216
232
  cache.set(key, created);
217
233
  return await created;
218
234
  }
219
- //#endregion
220
235
  export { DesignNodeModel };
221
236
 
222
237
  //# sourceMappingURL=design-nodes.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"design-nodes.mjs","names":[],"sources":["../../src/models/design-nodes.ts"],"sourcesContent":["/** Design metadata + render cache: TTL refresh, batched per file, bounded concurrency, abortable. */\nimport { and, eq } from \"drizzle-orm\";\nimport type { Table } from \"drizzle-orm\";\nimport type { Buffer } from \"node:buffer\";\nimport type { DatabaseAdapter } from \"../adapters/database.ts\";\nimport type {\n DesignCallOptions,\n DesignClient,\n DesignMeta,\n DesignProvider,\n DesignRef,\n} from \"../adapters/design.ts\";\nimport { parseDesignUrl } from \"../adapters/design.ts\";\nimport type { StorageAdapter } from \"../adapters/storage.ts\";\nimport type { Logger } from \"../logger.ts\";\nimport type { DesignNode } from \"../schema/design.ts\";\nimport { mapLimit } from \"../utils/map-limit.ts\";\nimport { ulid } from \"../utils/ulid.ts\";\nimport { col } from \"./columns.ts\";\nimport type { ConnectionModel } from \"./connections.ts\";\nimport { PROJECT_SCOPE, type DesignCredentialModel } from \"./design-credentials.ts\";\nimport { DESIGN_TTL_MS } from \"./design-health.ts\";\nimport { DesignRenderStore } from \"./design-renders.ts\";\nimport type { DesignCredentialStatus } from \"./design-resolve.ts\";\n\n/** Most files refreshed in parallel by one {@link DesignNodeModel.refreshMany} call. */\nconst FILE_CONCURRENCY = 3;\n\n/** Tables required by {@link DesignNodeModel}. */\nexport interface DesignNodeTables {\n designNodes: Table;\n}\n\n/** Options of {@link DesignNodeModel.refreshMany}. */\nexport interface RefreshOptions {\n now?: Date;\n /** Cancels in-flight provider calls (e.g. a page-render budget). */\n signal?: AbortSignal;\n /** The visiting user: their own connection is used when the project has no credential. */\n viewerId?: string;\n}\n\ninterface Due {\n url: string;\n provider: DesignProvider;\n ref: DesignRef;\n cached: DesignNode | null;\n /** Whose credential fetches this link: {@link PROJECT_SCOPE} or the viewer's user id. */\n scope: string;\n}\n\n/** Cache of design file/node metadata and node renders with TTL refresh. */\nexport class DesignNodeModel {\n private readonly tables: DesignNodeTables;\n private readonly renders: DesignRenderStore;\n private readonly logger: Logger | undefined;\n private readonly connections: ConnectionModel | undefined;\n private readonly db: DatabaseAdapter;\n private readonly credentials: DesignCredentialModel;\n private readonly providers: readonly DesignProvider[];\n\n constructor(\n db: DatabaseAdapter,\n credentials: DesignCredentialModel,\n providers: readonly DesignProvider[],\n options: {\n tables?: DesignNodeTables;\n logger?: Logger;\n storage?: StorageAdapter;\n /** Members' own connections, used when the project has no credential. */\n connections?: ConnectionModel;\n } = {},\n ) {\n this.db = db;\n this.credentials = credentials;\n this.providers = providers;\n this.tables = options.tables ?? { designNodes: db.tables.designNodes };\n this.renders = new DesignRenderStore(\n db,\n this.tables.designNodes,\n options.storage,\n options.logger,\n );\n this.logger = options.logger;\n this.connections = options.connections;\n }\n\n /** Cached row by id, scoped to the project, or null. */\n async get(projectId: string, id: string): Promise<DesignNode | null> {\n const row = (await this.db.get(this.tables.designNodes, id)) as unknown as DesignNode | null;\n return row?.projectId === projectId ? row : null;\n }\n\n /** Cached render bytes of a node, or null when it has none (or storage is not wired). */\n async readRender(node: DesignNode): Promise<Buffer | null> {\n return await this.renders.read(node);\n }\n\n /**\n * Cached rows for the given design URLs, keyed by URL (unparseable or uncached URLs are\n * absent). A row fetched with someone's own connection is only visible to that viewer.\n */\n async cachedFor(\n projectId: string,\n urls: readonly string[],\n viewerId?: string,\n ): Promise<Map<string, DesignNode>> {\n const rows = await Promise.all(\n [...new Set(urls)].map(async (url) => {\n const parsed = parseDesignUrl(this.providers, url);\n const row = parsed\n ? await this.visible(projectId, parsed.provider.metadata.kind, parsed.ref, viewerId)\n : null;\n return [url, row] as const;\n }),\n );\n return new Map(rows.flatMap(([url, row]) => (row ? [[url, row] as const] : [])));\n }\n\n /** Refresh one link (see {@link refreshMany}); null for unsupported URLs or nothing cached. */\n async refresh(\n projectId: string,\n url: string,\n now?: Date,\n viewerId?: string,\n ): Promise<DesignNode | null> {\n const results = await this.refreshMany(projectId, [url], {\n ...(now ? { now } : {}),\n ...(viewerId ? { viewerId } : {}),\n });\n return results.get(url) ?? null;\n }\n\n /**\n * Refresh the given links' metadata and renders when missing or past the TTL.\n * Links of one file share one metadata call and one render call, and files run\n * with bounded concurrency. Transient failures keep the previous cache; auth\n * failures update the credential's health. Never throws.\n */\n async refreshMany(\n projectId: string,\n urls: readonly string[],\n options: RefreshOptions = {},\n ): Promise<Map<string, DesignNode | null>> {\n const now = options.now ?? new Date();\n const scopes = new Map<string, Promise<string>>();\n const lookups = await Promise.all(\n [...new Set(urls)].map(async (url) => {\n const parsed = parseDesignUrl(this.providers, url);\n if (!parsed) return null;\n const kind = parsed.provider.metadata.kind;\n const scope = await memo(scopes, kind, async () => {\n return await this.scopeFor(projectId, kind, options.viewerId);\n });\n const cached = await this.lookup(projectId, kind, parsed.ref, scope);\n return { url, provider: parsed.provider, ref: parsed.ref, cached, scope };\n }),\n );\n const known = lookups.filter((item) => item !== null);\n const results = new Map<string, DesignNode | null>(\n known.map((item) => [item.url, item.cached]),\n );\n const due = known.filter((item) => isDue(item.cached, now));\n if (due.length === 0) return results;\n const fresh = await this.refreshDue(projectId, due, now, callOptions(this.logger, options));\n for (const [url, node] of fresh) results.set(url, node);\n return results;\n }\n\n /** Refresh due links grouped by provider and file, files in parallel (bounded). */\n private async refreshDue(\n projectId: string,\n due: Due[],\n now: Date,\n call: DesignCallOptions,\n ): Promise<Map<string, DesignNode>> {\n const byFile = [\n ...Map.groupBy(due, (item) => `${item.provider.metadata.kind}\\u0000${item.ref.fileKey}`),\n ];\n const batches = await mapLimit(byFile, FILE_CONCURRENCY, async ([, items]) => {\n return await this.refreshFile(projectId, items, now, call);\n });\n const merged = new Map<string, DesignNode>();\n for (const batch of batches) {\n for (const [url, node] of batch) merged.set(url, node);\n }\n return merged;\n }\n\n private async refreshFile(\n projectId: string,\n items: Due[],\n now: Date,\n call: DesignCallOptions,\n ): Promise<Map<string, DesignNode>> {\n const client = await this.clientFor(projectId, items);\n const first = items[0];\n if (!client || !first) return new Map();\n const { provider, ref } = first;\n const metas = await client.fetchMetas(\n ref.fileKey,\n items.map((item) => item.ref),\n call,\n );\n const answered = [...metas.values()].find((meta) => meta.state !== \"error\");\n if (answered && first.scope === PROJECT_SCOPE) {\n await this.credentials.recordStatus(\n projectId,\n provider.metadata.kind,\n statusFor(answered.state),\n );\n }\n const stored = await this.storeAll(projectId, items, metas, now);\n return await this.renders.ensure(projectId, client, ref.fileKey, stored, call);\n }\n\n /** Upsert a row per item that got a usable answer (`error` keeps its cached row), keyed by URL. */\n private async storeAll(\n projectId: string,\n items: Due[],\n metas: Map<string, DesignMeta>,\n now: Date,\n ): Promise<Map<string, DesignNode>> {\n const rows = await Promise.all(\n items.map(async (item) => {\n const meta = metas.get(item.ref.nodeId ?? \"\");\n const row =\n meta && meta.state !== \"error\" ? await this.store(projectId, item, meta, now) : null;\n return row ? ([item.url, row] as const) : null;\n }),\n );\n return new Map(rows.filter((entry) => entry !== null));\n }\n\n /** A client for the provider of `items` bound to the items' credential scope, or null without one. */\n private async clientFor(projectId: string, items: Due[]): Promise<DesignClient | null> {\n const first = items[0];\n if (!first) return null;\n const { provider, scope } = first;\n const kind = provider.metadata.kind;\n const own = scope !== PROJECT_SCOPE;\n const credential = own\n ? await this.connections?.accessToken(scope, kind, provider.oauth).catch(() => null)\n : await this.credentials.get(projectId, kind).catch(() => null);\n if (!credential) return null;\n return provider.create({\n credential,\n authType: own ? \"oauth\" : \"token\",\n ...(this.logger ? { logger: this.logger } : {}),\n });\n }\n\n /**\n * Whose credential serves a provider: the project's when set (it is shared and\n * wins), else the viewer's own connection, else the project scope (nothing to fetch with).\n */\n private async scopeFor(projectId: string, provider: string, viewerId?: string): Promise<string> {\n if (!viewerId || !this.connections || (await this.credentials.info(projectId, provider))) {\n return PROJECT_SCOPE;\n }\n return (await this.connections.info(viewerId, provider)) ? viewerId : PROJECT_SCOPE;\n }\n\n /** The shared row, else the viewer's own (a connected member's entries are never served to others). */\n private async visible(\n projectId: string,\n provider: string,\n ref: DesignRef,\n viewerId?: string,\n ): Promise<DesignNode | null> {\n const shared = await this.lookup(projectId, provider, ref, PROJECT_SCOPE);\n if (shared || !viewerId) return shared;\n return await this.lookup(projectId, provider, ref, viewerId);\n }\n\n private async lookup(\n projectId: string,\n provider: string,\n ref: DesignRef,\n scope: string,\n ): Promise<DesignNode | null> {\n const rows = (await this.db.list(this.tables.designNodes, {\n where: and(\n eq(col(this.tables.designNodes, \"projectId\"), projectId),\n eq(col(this.tables.designNodes, \"provider\"), provider),\n eq(col(this.tables.designNodes, \"viewerId\"), scope),\n eq(col(this.tables.designNodes, \"fileKey\"), ref.fileKey),\n eq(col(this.tables.designNodes, \"nodeId\"), ref.nodeId ?? \"\"),\n ),\n limit: 1,\n })) as unknown as DesignNode[];\n return rows[0] ?? null;\n }\n\n /**\n * Upsert the row. Non-ok states keep the last good name/version/modified time\n * (an auth failure must not erase the label); a deleted node also drops its\n * cached render, so a design that no longer exists is never shown as reference.\n */\n private async store(\n projectId: string,\n item: Due,\n meta: DesignMeta,\n now: Date,\n ): Promise<DesignNode> {\n const { ref, cached } = item;\n const stamp = now.toISOString();\n const keep = meta.state !== \"ok\" && cached;\n const values = {\n name: keep ? cached.name : meta.name,\n version: keep ? cached.version : meta.version,\n lastModified: keep ? cached.lastModified : meta.lastModified,\n state: meta.state,\n fetchedAt: stamp,\n updatedAt: stamp,\n };\n if (!cached) {\n return (await this.db.insert(this.tables.designNodes, {\n id: ulid(),\n projectId,\n provider: item.provider.metadata.kind,\n viewerId: item.scope,\n fileKey: ref.fileKey,\n nodeId: ref.nodeId ?? \"\",\n renderPath: null,\n renderVersion: null,\n createdAt: stamp,\n ...values,\n })) as unknown as DesignNode;\n }\n const dropRender = meta.state === \"deleted\" && cached.renderPath !== null;\n if (dropRender) await this.renders.remove(cached);\n return (await this.db.update(this.tables.designNodes, cached.id, {\n ...values,\n ...(dropRender ? { renderPath: null, renderVersion: null } : {}),\n })) as unknown as DesignNode;\n }\n}\n\n/** Never fetched, or cached longer ago than the TTL. */\nfunction isDue(cached: DesignNode | null, now: Date): boolean {\n return !cached || now.getTime() - Date.parse(cached.fetchedAt) >= DESIGN_TTL_MS;\n}\n\nfunction callOptions(logger: Logger | undefined, options: RefreshOptions): DesignCallOptions {\n return {\n ...(logger ? { logger } : {}),\n ...(options.signal ? { signal: options.signal } : {}),\n };\n}\n\nfunction statusFor(state: DesignMeta[\"state\"]): DesignCredentialStatus {\n return state === \"unauthorized\" || state === \"forbidden\" ? state : \"ok\";\n}\n\n/** Run `load` once per key, sharing the in-flight promise between concurrent callers. */\nasync function memo<T>(\n cache: Map<string, Promise<T>>,\n key: string,\n load: () => Promise<T>,\n): Promise<T> {\n const hit = cache.get(key);\n if (hit) return await hit;\n const created = load();\n cache.set(key, created);\n return await created;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"design-nodes.mjs","names":[],"sources":["../../src/models/design-nodes.ts"],"sourcesContent":["/** Design metadata + render cache: TTL refresh, batched per file, bounded concurrency, abortable. */\nimport { and, eq, isNull } from \"drizzle-orm\";\nimport type { Table } from \"drizzle-orm\";\nimport type { Buffer } from \"node:buffer\";\nimport type { DatabaseAdapter } from \"../adapters/database.ts\";\nimport type {\n DesignCallOptions,\n DesignClient,\n DesignMeta,\n DesignProvider,\n DesignRef,\n} from \"../adapters/design.ts\";\nimport { parseDesignUrl } from \"../adapters/design.ts\";\nimport type { StorageAdapter } from \"../adapters/storage.ts\";\nimport type { Logger } from \"../logger.ts\";\nimport type { DesignNode } from \"../schema/design.ts\";\nimport { mapLimit } from \"../utils/map-limit.ts\";\nimport { ulid } from \"../utils/ulid.ts\";\nimport { col } from \"./columns.ts\";\nimport type { ConnectionModel } from \"./connections.ts\";\nimport { PROJECT_SCOPE, type DesignCredentialModel } from \"./design-credentials.ts\";\nimport { DESIGN_TTL_MS, type DesignChange } from \"./design-health.ts\";\nimport { DesignRenderStore } from \"./design-renders.ts\";\nimport type { DesignCredentialStatus } from \"./design-resolve.ts\";\n\n/** Most files refreshed in parallel by one {@link DesignNodeModel.refreshMany} call. */\nconst FILE_CONCURRENCY = 3;\n\n/** Tables required by {@link DesignNodeModel}. */\nexport interface DesignNodeTables {\n designNodes: Table;\n}\n\n/** Options of {@link DesignNodeModel.refreshMany}. */\nexport interface RefreshOptions {\n now?: Date;\n /** Cancels in-flight provider calls (e.g. a page-render budget). */\n signal?: AbortSignal;\n /** The visiting user: their own connection is used when the project has no credential. */\n viewerId?: string;\n}\n\ninterface Due {\n url: string;\n provider: DesignProvider;\n ref: DesignRef;\n cached: DesignNode | null;\n /** Whose credential fetches this link: {@link PROJECT_SCOPE} or the viewer's user id. */\n scope: string;\n}\n\n/** Cache of design file/node metadata and node renders with TTL refresh. */\nexport class DesignNodeModel {\n private readonly tables: DesignNodeTables;\n private readonly renders: DesignRenderStore;\n private readonly logger: Logger | undefined;\n private readonly connections: ConnectionModel | undefined;\n private readonly db: DatabaseAdapter;\n private readonly credentials: DesignCredentialModel;\n private readonly providers: readonly DesignProvider[];\n\n constructor(\n db: DatabaseAdapter,\n credentials: DesignCredentialModel,\n providers: readonly DesignProvider[],\n options: {\n tables?: DesignNodeTables;\n logger?: Logger;\n storage?: StorageAdapter;\n /** Members' own connections, used when the project has no credential. */\n connections?: ConnectionModel;\n } = {},\n ) {\n this.db = db;\n this.credentials = credentials;\n this.providers = providers;\n this.tables = options.tables ?? { designNodes: db.tables.designNodes };\n this.renders = new DesignRenderStore(\n db,\n this.tables.designNodes,\n options.storage,\n options.logger,\n );\n this.logger = options.logger;\n this.connections = options.connections;\n }\n\n /** Cached row by id, scoped to the project, or null. */\n async get(projectId: string, id: string): Promise<DesignNode | null> {\n const row = (await this.db.get(this.tables.designNodes, id)) as unknown as DesignNode | null;\n return row?.projectId === projectId ? row : null;\n }\n\n /** Cached render bytes of a node, or null when it has none (or storage is not wired). */\n async readRender(node: DesignNode): Promise<Buffer | null> {\n return await this.renders.read(node);\n }\n\n /**\n * Cached rows for the given design URLs, keyed by URL (unparseable or uncached URLs are\n * absent). A row fetched with someone's own connection is only visible to that viewer.\n */\n async cachedFor(\n projectId: string,\n urls: readonly string[],\n viewerId?: string,\n ): Promise<Map<string, DesignNode>> {\n const rows = await Promise.all(\n [...new Set(urls)].map(async (url) => {\n const parsed = parseDesignUrl(this.providers, url);\n const row = parsed\n ? await this.visible(projectId, parsed.provider.metadata.kind, parsed.ref, viewerId)\n : null;\n return [url, row] as const;\n }),\n );\n return new Map(rows.flatMap(([url, row]) => (row ? [[url, row] as const] : [])));\n }\n\n /**\n * Claim the right to announce `change` for a node: true for exactly one caller per\n * `change.key`, even when health checks run concurrently. The claim is a compare-and-set of\n * `notified_version` from the value read here to a fresh `<key>#<token>`; a caller that finds\n * the key already claimed, or loses the swap, gets false. Shared (project-credential) rows only:\n * a member's own-connection row never announces, so one design is never announced twice.\n */\n async claimChange(node: DesignNode, change: DesignChange): Promise<boolean> {\n if (node.viewerId !== PROJECT_SCOPE) return false;\n const column = col(this.tables.designNodes, \"notifiedVersion\");\n const current = (await this.db.get(\n this.tables.designNodes,\n node.id,\n )) as unknown as DesignNode | null;\n if (!current || current.notifiedVersion?.startsWith(`${change.key}#`) === true) return false;\n const token = `${change.key}#${ulid()}`;\n const won = (await this.db.updateWhere(\n this.tables.designNodes,\n node.id,\n { notifiedVersion: token },\n current.notifiedVersion === null ? isNull(column) : eq(column, current.notifiedVersion),\n )) as unknown as DesignNode | null;\n return won?.notifiedVersion === token;\n }\n\n /** Refresh one link (see {@link refreshMany}); null for unsupported URLs or nothing cached. */\n async refresh(\n projectId: string,\n url: string,\n now?: Date,\n viewerId?: string,\n ): Promise<DesignNode | null> {\n const results = await this.refreshMany(projectId, [url], {\n ...(now ? { now } : {}),\n ...(viewerId ? { viewerId } : {}),\n });\n return results.get(url) ?? null;\n }\n\n /**\n * Refresh the given links' metadata and renders when missing or past the TTL.\n * Links of one file share one metadata call and one render call, and files run\n * with bounded concurrency. Transient failures keep the previous cache; auth\n * failures update the credential's health. Never throws.\n */\n async refreshMany(\n projectId: string,\n urls: readonly string[],\n options: RefreshOptions = {},\n ): Promise<Map<string, DesignNode | null>> {\n const now = options.now ?? new Date();\n const scopes = new Map<string, Promise<string>>();\n const lookups = await Promise.all(\n [...new Set(urls)].map(async (url) => {\n const parsed = parseDesignUrl(this.providers, url);\n if (!parsed) return null;\n const kind = parsed.provider.metadata.kind;\n const scope = await memo(scopes, kind, async () => {\n return await this.scopeFor(projectId, kind, options.viewerId);\n });\n const cached = await this.lookup(projectId, kind, parsed.ref, scope);\n return { url, provider: parsed.provider, ref: parsed.ref, cached, scope };\n }),\n );\n const known = lookups.filter((item) => item !== null);\n const results = new Map<string, DesignNode | null>(\n known.map((item) => [item.url, item.cached]),\n );\n const due = known.filter((item) => isDue(item.cached, now));\n if (due.length === 0) return results;\n const fresh = await this.refreshDue(projectId, due, now, callOptions(this.logger, options));\n for (const [url, node] of fresh) results.set(url, node);\n return results;\n }\n\n /** Refresh due links grouped by provider and file, files in parallel (bounded). */\n private async refreshDue(\n projectId: string,\n due: Due[],\n now: Date,\n call: DesignCallOptions,\n ): Promise<Map<string, DesignNode>> {\n const byFile = [\n ...Map.groupBy(due, (item) => `${item.provider.metadata.kind}\\u0000${item.ref.fileKey}`),\n ];\n const batches = await mapLimit(byFile, FILE_CONCURRENCY, async ([, items]) => {\n return await this.refreshFile(projectId, items, now, call);\n });\n const merged = new Map<string, DesignNode>();\n for (const batch of batches) {\n for (const [url, node] of batch) merged.set(url, node);\n }\n return merged;\n }\n\n private async refreshFile(\n projectId: string,\n items: Due[],\n now: Date,\n call: DesignCallOptions,\n ): Promise<Map<string, DesignNode>> {\n const client = await this.clientFor(projectId, items);\n const first = items[0];\n if (!client || !first) return new Map();\n const { provider, ref } = first;\n const metas = await client.fetchMetas(\n ref.fileKey,\n items.map((item) => item.ref),\n call,\n );\n const answered = [...metas.values()].find((meta) => meta.state !== \"error\");\n if (answered && first.scope === PROJECT_SCOPE) {\n await this.credentials.recordStatus(\n projectId,\n provider.metadata.kind,\n statusFor(answered.state),\n );\n }\n const stored = await this.storeAll(projectId, items, metas, now);\n return await this.renders.ensure(projectId, client, ref.fileKey, stored, call);\n }\n\n /** Upsert a row per item that got a usable answer (`error` keeps its cached row), keyed by URL. */\n private async storeAll(\n projectId: string,\n items: Due[],\n metas: Map<string, DesignMeta>,\n now: Date,\n ): Promise<Map<string, DesignNode>> {\n const rows = await Promise.all(\n items.map(async (item) => {\n const meta = metas.get(item.ref.nodeId ?? \"\");\n const row =\n meta && meta.state !== \"error\" ? await this.store(projectId, item, meta, now) : null;\n return row ? ([item.url, row] as const) : null;\n }),\n );\n return new Map(rows.filter((entry) => entry !== null));\n }\n\n /** A client for the provider of `items` bound to the items' credential scope, or null without one. */\n private async clientFor(projectId: string, items: Due[]): Promise<DesignClient | null> {\n const first = items[0];\n if (!first) return null;\n const { provider, scope } = first;\n const kind = provider.metadata.kind;\n const own = scope !== PROJECT_SCOPE;\n const credential = own\n ? await this.connections?.accessToken(scope, kind, provider.oauth).catch(() => null)\n : await this.credentials.get(projectId, kind).catch(() => null);\n if (!credential) return null;\n return provider.create({\n credential,\n authType: own ? \"oauth\" : \"token\",\n ...(this.logger ? { logger: this.logger } : {}),\n });\n }\n\n /**\n * Whose credential serves a provider: the project's when set (it is shared and\n * wins), else the viewer's own connection, else the project scope (nothing to fetch with).\n */\n private async scopeFor(projectId: string, provider: string, viewerId?: string): Promise<string> {\n if (!viewerId || !this.connections || (await this.credentials.info(projectId, provider))) {\n return PROJECT_SCOPE;\n }\n return (await this.connections.info(viewerId, provider)) ? viewerId : PROJECT_SCOPE;\n }\n\n /** The shared row, else the viewer's own (a connected member's entries are never served to others). */\n private async visible(\n projectId: string,\n provider: string,\n ref: DesignRef,\n viewerId?: string,\n ): Promise<DesignNode | null> {\n const shared = await this.lookup(projectId, provider, ref, PROJECT_SCOPE);\n if (shared || !viewerId) return shared;\n return await this.lookup(projectId, provider, ref, viewerId);\n }\n\n private async lookup(\n projectId: string,\n provider: string,\n ref: DesignRef,\n scope: string,\n ): Promise<DesignNode | null> {\n const rows = (await this.db.list(this.tables.designNodes, {\n where: and(\n eq(col(this.tables.designNodes, \"projectId\"), projectId),\n eq(col(this.tables.designNodes, \"provider\"), provider),\n eq(col(this.tables.designNodes, \"viewerId\"), scope),\n eq(col(this.tables.designNodes, \"fileKey\"), ref.fileKey),\n eq(col(this.tables.designNodes, \"nodeId\"), ref.nodeId ?? \"\"),\n ),\n limit: 1,\n })) as unknown as DesignNode[];\n return rows[0] ?? null;\n }\n\n /**\n * Upsert the row. Non-ok states keep the last good name/version/modified time\n * (an auth failure must not erase the label); a deleted node also drops its\n * cached render, so a design that no longer exists is never shown as reference.\n */\n private async store(\n projectId: string,\n item: Due,\n meta: DesignMeta,\n now: Date,\n ): Promise<DesignNode> {\n const { ref, cached } = item;\n const stamp = now.toISOString();\n const keep = meta.state !== \"ok\" && cached;\n const values = {\n name: keep ? cached.name : meta.name,\n version: keep ? cached.version : meta.version,\n lastModified: keep ? cached.lastModified : meta.lastModified,\n state: meta.state,\n fetchedAt: stamp,\n updatedAt: stamp,\n };\n if (!cached) {\n return (await this.db.insert(this.tables.designNodes, {\n id: ulid(),\n projectId,\n provider: item.provider.metadata.kind,\n viewerId: item.scope,\n fileKey: ref.fileKey,\n nodeId: ref.nodeId ?? \"\",\n renderPath: null,\n renderVersion: null,\n notifiedVersion: null,\n createdAt: stamp,\n ...values,\n })) as unknown as DesignNode;\n }\n const dropRender = meta.state === \"deleted\" && cached.renderPath !== null;\n if (dropRender) await this.renders.remove(cached);\n return (await this.db.update(this.tables.designNodes, cached.id, {\n ...values,\n ...(dropRender ? { renderPath: null, renderVersion: null } : {}),\n })) as unknown as DesignNode;\n }\n}\n\n/** Never fetched, or cached longer ago than the TTL. */\nfunction isDue(cached: DesignNode | null, now: Date): boolean {\n return !cached || now.getTime() - Date.parse(cached.fetchedAt) >= DESIGN_TTL_MS;\n}\n\nfunction callOptions(logger: Logger | undefined, options: RefreshOptions): DesignCallOptions {\n return {\n ...(logger ? { logger } : {}),\n ...(options.signal ? { signal: options.signal } : {}),\n };\n}\n\nfunction statusFor(state: DesignMeta[\"state\"]): DesignCredentialStatus {\n return state === \"unauthorized\" || state === \"forbidden\" ? state : \"ok\";\n}\n\n/** Run `load` once per key, sharing the in-flight promise between concurrent callers. */\nasync function memo<T>(\n cache: Map<string, Promise<T>>,\n key: string,\n load: () => Promise<T>,\n): Promise<T> {\n const hit = cache.get(key);\n if (hit) return await hit;\n const created = load();\n cache.set(key, created);\n return await created;\n}\n"],"mappings":"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"}
@@ -2,7 +2,7 @@ import "node:path";
2
2
  import "node:url";
3
3
  import.meta.url;
4
4
  import { designRenderPath } from "../paths.mjs";
5
- //#region src/models/design-renders.ts
5
+ // src/models/design-renders.ts
6
6
  /**
7
7
  * Renders live at `{projectId}/design/{provider}/{fileKey}/{node}.png` (a member's own
8
8
  * connection: `…/{provider}/user-{id}/{fileKey}/…`), one per
@@ -68,7 +68,6 @@ var DesignRenderStore = class {
68
68
  function needsRender(node) {
69
69
  return node.state === "ok" && node.nodeId !== "" && node.version !== null && node.renderVersion !== node.version;
70
70
  }
71
- //#endregion
72
71
  export { DesignRenderStore };
73
72
 
74
73
  //# sourceMappingURL=design-renders.mjs.map
@@ -1,7 +1,6 @@
1
1
  import __tsdown_shims_path from 'node:path';
2
2
  import __tsdown_shims_url from 'node:url';
3
3
  import { DesignLink } from "../schema/design.mjs";
4
- //#region src/models/design-resolve.d.ts
5
4
  /** Last observed health of a design credential. */
6
5
  type DesignCredentialStatus = "unknown" | "ok" | "unauthorized" | "forbidden";
7
6
  /** Where a component's resolved design link came from. */
@@ -24,6 +23,5 @@ declare function resolveDesigns(manual: readonly Pick<DesignLink, "importPath" |
24
23
  storyImportPath: string | null;
25
24
  designUrl?: string | null;
26
25
  }[]): Map<string, DesignResolution>;
27
- //#endregion
28
26
  export { DesignCredentialStatus, DesignResolution, DesignSource, normalizeImportPath, resolveDesigns };
29
27
  //# sourceMappingURL=design-resolve.d.mts.map
@@ -1,7 +1,7 @@
1
1
  import "node:path";
2
2
  import "node:url";
3
3
  import.meta.url;
4
- //#region src/models/design-resolve.ts
4
+ // src/models/design-resolve.ts
5
5
  /** Story-file key: path without a leading `./`, so index and manual entries compare equal. */
6
6
  function normalizeImportPath(path) {
7
7
  return path.trim().replace(/^(?:\.\/)+/u, "");
@@ -35,7 +35,6 @@ function storyDesigns(snapshots) {
35
35
  }
36
36
  return resolved;
37
37
  }
38
- //#endregion
39
38
  export { normalizeImportPath, resolveDesigns };
40
39
 
41
40
  //# sourceMappingURL=design-resolve.mjs.map
@@ -4,7 +4,6 @@ import { DatabaseAdapter } from "../adapter/database.mjs";
4
4
  import { DesignProvider } from "../adapter/design.mjs";
5
5
  import { DesignLink } from "../schema/design.mjs";
6
6
  import { Table } from "drizzle-orm";
7
- //#region src/models/design.d.ts
8
7
  /** Tables required by {@link DesignModel}. */
9
8
  interface DesignTables {
10
9
  designLinks: Table;
@@ -34,6 +33,5 @@ declare class DesignModel {
34
33
  private links;
35
34
  private insertLink;
36
35
  }
37
- //#endregion
38
36
  export { DesignModel, DesignTables };
39
37
  //# sourceMappingURL=design.d.mts.map
@@ -6,7 +6,7 @@ import { ulid } from "../utils/ulid.mjs";
6
6
  import { col } from "./columns.mjs";
7
7
  import { normalizeImportPath } from "./design-resolve.mjs";
8
8
  import { eq } from "drizzle-orm";
9
- //#region src/models/design.ts
9
+ // src/models/design.ts
10
10
  /** Design-source links: project-level design files and per-component (story file) mappings. */
11
11
  /** Data operations for design links; URLs are validated by the configured providers. */
12
12
  var DesignModel = class {
@@ -84,7 +84,6 @@ var DesignModel = class {
84
84
  });
85
85
  }
86
86
  };
87
- //#endregion
88
87
  export { DesignModel };
89
88
 
90
89
  //# sourceMappingURL=design.mjs.map
@@ -1,7 +1,6 @@
1
1
  import __tsdown_shims_path from 'node:path';
2
2
  import __tsdown_shims_url from 'node:url';
3
3
  import { Build } from "../schema/build.mjs";
4
- //#region src/models/inherited-ratio.d.ts
5
4
  /** Builds the average looks at by default (newest first). */
6
5
  declare const RECENT_BUILDS_FOR_INHERITED_RATIO = 20;
7
6
  /** The average inherited ratio of a project's recent builds. */
@@ -16,6 +15,5 @@ interface InheritedRatio {
16
15
  * does (no data is not a 0 % skip rate). A build with no snapshots has no ratio and is skipped.
17
16
  */
18
17
  declare function averageInheritedRatio(builds: readonly Pick<Build, "createdAt" | "snapshotCount" | "inheritedCount">[], limit?: number): InheritedRatio | null;
19
- //#endregion
20
18
  export { InheritedRatio, RECENT_BUILDS_FOR_INHERITED_RATIO, averageInheritedRatio };
21
19
  //# sourceMappingURL=inherited-ratio.d.mts.map
@@ -1,7 +1,7 @@
1
1
  import "node:path";
2
2
  import "node:url";
3
3
  import.meta.url;
4
- //#region src/models/inherited-ratio.ts
4
+ // src/models/inherited-ratio.ts
5
5
  /** Builds the average looks at by default (newest first). */
6
6
  const RECENT_BUILDS_FOR_INHERITED_RATIO = 20;
7
7
  /**
@@ -16,7 +16,6 @@ function averageInheritedRatio(builds, limit = 20) {
16
16
  builds: recent.length
17
17
  };
18
18
  }
19
- //#endregion
20
19
  export { RECENT_BUILDS_FOR_INHERITED_RATIO, averageInheritedRatio };
21
20
 
22
21
  //# sourceMappingURL=inherited-ratio.mjs.map