quiver-cli 1.3.1 → 2.0.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 (407) hide show
  1. package/README.md +197 -239
  2. package/dist/cli.js +3163 -2040
  3. package/package.json +10 -11
  4. package/template/.agents/AGENTS.md +0 -26
  5. package/template/.agents/commands/cp.md +0 -108
  6. package/template/.agents/commands/next-setup.md +0 -1064
  7. package/template/.agents/commands/review.md +0 -68
  8. package/template/.agents/commands/terraform-readme.md +0 -38
  9. package/template/.agents/config.json +0 -80
  10. package/template/.agents/plugins/opencode/rtk.ts +0 -34
  11. package/template/.agents/skills/agent-browser/SKILL.md +0 -52
  12. package/template/.agents/skills/apps/skybridge/SKILL.md +0 -52
  13. package/template/.agents/skills/code/cleanup/SKILL.md +0 -26
  14. package/template/.agents/skills/code/improve/SKILL.md +0 -122
  15. package/template/.agents/skills/code/improve/references/audit-playbook.md +0 -130
  16. package/template/.agents/skills/code/improve/references/closing-the-loop.md +0 -96
  17. package/template/.agents/skills/code/improve/references/plan-template.md +0 -197
  18. package/template/.agents/skills/code/vercel-react-best-practices/AGENTS.md +0 -3810
  19. package/template/.agents/skills/code/vercel-react-best-practices/README.md +0 -123
  20. package/template/.agents/skills/code/vercel-react-best-practices/SKILL.md +0 -149
  21. package/template/.agents/skills/code/vercel-react-best-practices/metadata.json +0 -15
  22. package/template/.agents/skills/code/vercel-react-best-practices/rules/_sections.md +0 -46
  23. package/template/.agents/skills/code/vercel-react-best-practices/rules/_template.md +0 -28
  24. package/template/.agents/skills/code/vercel-react-best-practices/rules/advanced-effect-event-deps.md +0 -56
  25. package/template/.agents/skills/code/vercel-react-best-practices/rules/advanced-event-handler-refs.md +0 -55
  26. package/template/.agents/skills/code/vercel-react-best-practices/rules/advanced-init-once.md +0 -42
  27. package/template/.agents/skills/code/vercel-react-best-practices/rules/advanced-use-latest.md +0 -39
  28. package/template/.agents/skills/code/vercel-react-best-practices/rules/async-api-routes.md +0 -38
  29. package/template/.agents/skills/code/vercel-react-best-practices/rules/async-cheap-condition-before-await.md +0 -37
  30. package/template/.agents/skills/code/vercel-react-best-practices/rules/async-defer-await.md +0 -82
  31. package/template/.agents/skills/code/vercel-react-best-practices/rules/async-dependencies.md +0 -51
  32. package/template/.agents/skills/code/vercel-react-best-practices/rules/async-parallel.md +0 -28
  33. package/template/.agents/skills/code/vercel-react-best-practices/rules/async-suspense-boundaries.md +0 -99
  34. package/template/.agents/skills/code/vercel-react-best-practices/rules/bundle-analyzable-paths.md +0 -63
  35. package/template/.agents/skills/code/vercel-react-best-practices/rules/bundle-barrel-imports.md +0 -60
  36. package/template/.agents/skills/code/vercel-react-best-practices/rules/bundle-conditional.md +0 -31
  37. package/template/.agents/skills/code/vercel-react-best-practices/rules/bundle-defer-third-party.md +0 -49
  38. package/template/.agents/skills/code/vercel-react-best-practices/rules/bundle-dynamic-imports.md +0 -35
  39. package/template/.agents/skills/code/vercel-react-best-practices/rules/bundle-preload.md +0 -50
  40. package/template/.agents/skills/code/vercel-react-best-practices/rules/client-event-listeners.md +0 -74
  41. package/template/.agents/skills/code/vercel-react-best-practices/rules/client-localstorage-schema.md +0 -71
  42. package/template/.agents/skills/code/vercel-react-best-practices/rules/client-passive-event-listeners.md +0 -48
  43. package/template/.agents/skills/code/vercel-react-best-practices/rules/client-swr-dedup.md +0 -56
  44. package/template/.agents/skills/code/vercel-react-best-practices/rules/js-batch-dom-css.md +0 -107
  45. package/template/.agents/skills/code/vercel-react-best-practices/rules/js-cache-function-results.md +0 -80
  46. package/template/.agents/skills/code/vercel-react-best-practices/rules/js-cache-property-access.md +0 -28
  47. package/template/.agents/skills/code/vercel-react-best-practices/rules/js-cache-storage.md +0 -70
  48. package/template/.agents/skills/code/vercel-react-best-practices/rules/js-combine-iterations.md +0 -32
  49. package/template/.agents/skills/code/vercel-react-best-practices/rules/js-early-exit.md +0 -50
  50. package/template/.agents/skills/code/vercel-react-best-practices/rules/js-flatmap-filter.md +0 -60
  51. package/template/.agents/skills/code/vercel-react-best-practices/rules/js-hoist-regexp.md +0 -45
  52. package/template/.agents/skills/code/vercel-react-best-practices/rules/js-index-maps.md +0 -37
  53. package/template/.agents/skills/code/vercel-react-best-practices/rules/js-length-check-first.md +0 -49
  54. package/template/.agents/skills/code/vercel-react-best-practices/rules/js-min-max-loop.md +0 -82
  55. package/template/.agents/skills/code/vercel-react-best-practices/rules/js-request-idle-callback.md +0 -105
  56. package/template/.agents/skills/code/vercel-react-best-practices/rules/js-set-map-lookups.md +0 -24
  57. package/template/.agents/skills/code/vercel-react-best-practices/rules/js-tosorted-immutable.md +0 -57
  58. package/template/.agents/skills/code/vercel-react-best-practices/rules/rendering-activity.md +0 -26
  59. package/template/.agents/skills/code/vercel-react-best-practices/rules/rendering-animate-svg-wrapper.md +0 -47
  60. package/template/.agents/skills/code/vercel-react-best-practices/rules/rendering-conditional-render.md +0 -40
  61. package/template/.agents/skills/code/vercel-react-best-practices/rules/rendering-content-visibility.md +0 -38
  62. package/template/.agents/skills/code/vercel-react-best-practices/rules/rendering-hoist-jsx.md +0 -46
  63. package/template/.agents/skills/code/vercel-react-best-practices/rules/rendering-hydration-no-flicker.md +0 -82
  64. package/template/.agents/skills/code/vercel-react-best-practices/rules/rendering-hydration-suppress-warning.md +0 -30
  65. package/template/.agents/skills/code/vercel-react-best-practices/rules/rendering-resource-hints.md +0 -85
  66. package/template/.agents/skills/code/vercel-react-best-practices/rules/rendering-script-defer-async.md +0 -68
  67. package/template/.agents/skills/code/vercel-react-best-practices/rules/rendering-svg-precision.md +0 -28
  68. package/template/.agents/skills/code/vercel-react-best-practices/rules/rendering-usetransition-loading.md +0 -75
  69. package/template/.agents/skills/code/vercel-react-best-practices/rules/rerender-defer-reads.md +0 -39
  70. package/template/.agents/skills/code/vercel-react-best-practices/rules/rerender-dependencies.md +0 -45
  71. package/template/.agents/skills/code/vercel-react-best-practices/rules/rerender-derived-state-no-effect.md +0 -40
  72. package/template/.agents/skills/code/vercel-react-best-practices/rules/rerender-derived-state.md +0 -29
  73. package/template/.agents/skills/code/vercel-react-best-practices/rules/rerender-functional-setstate.md +0 -74
  74. package/template/.agents/skills/code/vercel-react-best-practices/rules/rerender-lazy-state-init.md +0 -58
  75. package/template/.agents/skills/code/vercel-react-best-practices/rules/rerender-memo-with-default-value.md +0 -38
  76. package/template/.agents/skills/code/vercel-react-best-practices/rules/rerender-memo.md +0 -44
  77. package/template/.agents/skills/code/vercel-react-best-practices/rules/rerender-move-effect-to-event.md +0 -45
  78. package/template/.agents/skills/code/vercel-react-best-practices/rules/rerender-no-inline-components.md +0 -82
  79. package/template/.agents/skills/code/vercel-react-best-practices/rules/rerender-simple-expression-in-memo.md +0 -35
  80. package/template/.agents/skills/code/vercel-react-best-practices/rules/rerender-split-combined-hooks.md +0 -64
  81. package/template/.agents/skills/code/vercel-react-best-practices/rules/rerender-transitions.md +0 -40
  82. package/template/.agents/skills/code/vercel-react-best-practices/rules/rerender-use-deferred-value.md +0 -59
  83. package/template/.agents/skills/code/vercel-react-best-practices/rules/rerender-use-ref-transient-values.md +0 -73
  84. package/template/.agents/skills/code/vercel-react-best-practices/rules/server-after-nonblocking.md +0 -73
  85. package/template/.agents/skills/code/vercel-react-best-practices/rules/server-auth-actions.md +0 -96
  86. package/template/.agents/skills/code/vercel-react-best-practices/rules/server-cache-lru.md +0 -41
  87. package/template/.agents/skills/code/vercel-react-best-practices/rules/server-cache-react.md +0 -76
  88. package/template/.agents/skills/code/vercel-react-best-practices/rules/server-dedup-props.md +0 -65
  89. package/template/.agents/skills/code/vercel-react-best-practices/rules/server-hoist-static-io.md +0 -149
  90. package/template/.agents/skills/code/vercel-react-best-practices/rules/server-no-shared-module-state.md +0 -50
  91. package/template/.agents/skills/code/vercel-react-best-practices/rules/server-parallel-fetching.md +0 -83
  92. package/template/.agents/skills/code/vercel-react-best-practices/rules/server-parallel-nested-fetching.md +0 -34
  93. package/template/.agents/skills/code/vercel-react-best-practices/rules/server-serialization.md +0 -38
  94. package/template/.agents/skills/data/prisma-cli/SKILL.md +0 -265
  95. package/template/.agents/skills/data/prisma-cli/references/agent-safety.md +0 -27
  96. package/template/.agents/skills/data/prisma-cli/references/complete.md +0 -22
  97. package/template/.agents/skills/data/prisma-cli/references/db-execute.md +0 -78
  98. package/template/.agents/skills/data/prisma-cli/references/db-pull.md +0 -185
  99. package/template/.agents/skills/data/prisma-cli/references/db-push.md +0 -150
  100. package/template/.agents/skills/data/prisma-cli/references/db-seed.md +0 -188
  101. package/template/.agents/skills/data/prisma-cli/references/debug.md +0 -46
  102. package/template/.agents/skills/data/prisma-cli/references/dev.md +0 -157
  103. package/template/.agents/skills/data/prisma-cli/references/format.md +0 -48
  104. package/template/.agents/skills/data/prisma-cli/references/generate.md +0 -173
  105. package/template/.agents/skills/data/prisma-cli/references/init.md +0 -139
  106. package/template/.agents/skills/data/prisma-cli/references/mcp.md +0 -39
  107. package/template/.agents/skills/data/prisma-cli/references/migrate-deploy.md +0 -127
  108. package/template/.agents/skills/data/prisma-cli/references/migrate-dev.md +0 -145
  109. package/template/.agents/skills/data/prisma-cli/references/migrate-diff.md +0 -89
  110. package/template/.agents/skills/data/prisma-cli/references/migrate-reset.md +0 -80
  111. package/template/.agents/skills/data/prisma-cli/references/migrate-resolve.md +0 -57
  112. package/template/.agents/skills/data/prisma-cli/references/migrate-status.md +0 -65
  113. package/template/.agents/skills/data/prisma-cli/references/studio.md +0 -137
  114. package/template/.agents/skills/data/prisma-cli/references/validate.md +0 -53
  115. package/template/.agents/skills/data/prisma-client-api/SKILL.md +0 -216
  116. package/template/.agents/skills/data/prisma-client-api/references/client-methods.md +0 -223
  117. package/template/.agents/skills/data/prisma-client-api/references/constructor.md +0 -221
  118. package/template/.agents/skills/data/prisma-client-api/references/filters.md +0 -256
  119. package/template/.agents/skills/data/prisma-client-api/references/model-queries.md +0 -281
  120. package/template/.agents/skills/data/prisma-client-api/references/query-options.md +0 -276
  121. package/template/.agents/skills/data/prisma-client-api/references/raw-queries.md +0 -198
  122. package/template/.agents/skills/data/prisma-client-api/references/relations.md +0 -308
  123. package/template/.agents/skills/data/prisma-client-api/references/transactions.md +0 -184
  124. package/template/.agents/skills/design/impeccable/SKILL.md +0 -84
  125. package/template/.agents/skills/design/impeccable/reference/adapt.md +0 -312
  126. package/template/.agents/skills/design/impeccable/reference/adapt.native.md +0 -58
  127. package/template/.agents/skills/design/impeccable/reference/android.md +0 -46
  128. package/template/.agents/skills/design/impeccable/reference/animate.md +0 -89
  129. package/template/.agents/skills/design/impeccable/reference/audit.md +0 -136
  130. package/template/.agents/skills/design/impeccable/reference/audit.native.md +0 -139
  131. package/template/.agents/skills/design/impeccable/reference/bolder.md +0 -33
  132. package/template/.agents/skills/design/impeccable/reference/clarify.md +0 -94
  133. package/template/.agents/skills/design/impeccable/reference/colorize.md +0 -86
  134. package/template/.agents/skills/design/impeccable/reference/craft-floor.md +0 -44
  135. package/template/.agents/skills/design/impeccable/reference/craft.md +0 -5
  136. package/template/.agents/skills/design/impeccable/reference/critique.md +0 -806
  137. package/template/.agents/skills/design/impeccable/reference/degraded/asset-producer.md +0 -37
  138. package/template/.agents/skills/design/impeccable/reference/degraded/documenter.md +0 -24
  139. package/template/.agents/skills/design/impeccable/reference/degraded/finish-reviewer.md +0 -38
  140. package/template/.agents/skills/design/impeccable/reference/degraded/manual-edit-applier.md +0 -92
  141. package/template/.agents/skills/design/impeccable/reference/delight.md +0 -70
  142. package/template/.agents/skills/design/impeccable/reference/distill.md +0 -111
  143. package/template/.agents/skills/design/impeccable/reference/doctor.md +0 -54
  144. package/template/.agents/skills/design/impeccable/reference/document.md +0 -416
  145. package/template/.agents/skills/design/impeccable/reference/extract.md +0 -69
  146. package/template/.agents/skills/design/impeccable/reference/harden.md +0 -336
  147. package/template/.agents/skills/design/impeccable/reference/hooks.md +0 -111
  148. package/template/.agents/skills/design/impeccable/reference/init.md +0 -131
  149. package/template/.agents/skills/design/impeccable/reference/ios.md +0 -51
  150. package/template/.agents/skills/design/impeccable/reference/layout.md +0 -84
  151. package/template/.agents/skills/design/impeccable/reference/live-setup.md +0 -102
  152. package/template/.agents/skills/design/impeccable/reference/live.md +0 -323
  153. package/template/.agents/skills/design/impeccable/reference/new-work.md +0 -145
  154. package/template/.agents/skills/design/impeccable/reference/onboard.md +0 -234
  155. package/template/.agents/skills/design/impeccable/reference/operate.md +0 -61
  156. package/template/.agents/skills/design/impeccable/reference/optimize.md +0 -258
  157. package/template/.agents/skills/design/impeccable/reference/overdrive.md +0 -127
  158. package/template/.agents/skills/design/impeccable/reference/polish.md +0 -105
  159. package/template/.agents/skills/design/impeccable/reference/quieter.md +0 -99
  160. package/template/.agents/skills/design/impeccable/reference/routing.md +0 -18
  161. package/template/.agents/skills/design/impeccable/reference/shape.md +0 -59
  162. package/template/.agents/skills/design/impeccable/reference/typeset.md +0 -80
  163. package/template/.agents/skills/design/impeccable/reference/visualize.md +0 -46
  164. package/template/.agents/skills/design/impeccable/scripts/build-phase.mjs +0 -1022
  165. package/template/.agents/skills/design/impeccable/scripts/command-metadata.json +0 -94
  166. package/template/.agents/skills/design/impeccable/scripts/comp-diff.mjs +0 -391
  167. package/template/.agents/skills/design/impeccable/scripts/comp-spec.mjs +0 -513
  168. package/template/.agents/skills/design/impeccable/scripts/concept-seed.mjs +0 -814
  169. package/template/.agents/skills/design/impeccable/scripts/context-signals.mjs +0 -325
  170. package/template/.agents/skills/design/impeccable/scripts/context.mjs +0 -1565
  171. package/template/.agents/skills/design/impeccable/scripts/critique-storage.mjs +0 -473
  172. package/template/.agents/skills/design/impeccable/scripts/data/font-index-failures.json +0 -121
  173. package/template/.agents/skills/design/impeccable/scripts/data/font-index.json +0 -1
  174. package/template/.agents/skills/design/impeccable/scripts/detect-csp.mjs +0 -198
  175. package/template/.agents/skills/design/impeccable/scripts/detect.mjs +0 -30
  176. package/template/.agents/skills/design/impeccable/scripts/detector/browser/injected/index.mjs +0 -2204
  177. package/template/.agents/skills/design/impeccable/scripts/detector/cli/main.mjs +0 -432
  178. package/template/.agents/skills/design/impeccable/scripts/detector/design-system.mjs +0 -1311
  179. package/template/.agents/skills/design/impeccable/scripts/detector/detect-antipatterns-browser.js +0 -9043
  180. package/template/.agents/skills/design/impeccable/scripts/detector/detect-antipatterns.mjs +0 -51
  181. package/template/.agents/skills/design/impeccable/scripts/detector/engines/browser/detect-url.mjs +0 -434
  182. package/template/.agents/skills/design/impeccable/scripts/detector/engines/regex/detect-text.mjs +0 -1319
  183. package/template/.agents/skills/design/impeccable/scripts/detector/engines/static-html/css-cascade.mjs +0 -1241
  184. package/template/.agents/skills/design/impeccable/scripts/detector/engines/static-html/detect-html.mjs +0 -288
  185. package/template/.agents/skills/design/impeccable/scripts/detector/engines/visual/screenshot-contrast.mjs +0 -189
  186. package/template/.agents/skills/design/impeccable/scripts/detector/findings.mjs +0 -18
  187. package/template/.agents/skills/design/impeccable/scripts/detector/node/file-system.mjs +0 -213
  188. package/template/.agents/skills/design/impeccable/scripts/detector/profile/profiler.mjs +0 -166
  189. package/template/.agents/skills/design/impeccable/scripts/detector/registry/antipatterns.mjs +0 -635
  190. package/template/.agents/skills/design/impeccable/scripts/detector/rules/checks.mjs +0 -5681
  191. package/template/.agents/skills/design/impeccable/scripts/detector/shared/color.mjs +0 -596
  192. package/template/.agents/skills/design/impeccable/scripts/detector/shared/constants.mjs +0 -127
  193. package/template/.agents/skills/design/impeccable/scripts/detector/shared/fonts.mjs +0 -30
  194. package/template/.agents/skills/design/impeccable/scripts/detector/shared/inline-ignores.mjs +0 -148
  195. package/template/.agents/skills/design/impeccable/scripts/detector/shared/page.mjs +0 -7
  196. package/template/.agents/skills/design/impeccable/scripts/doctor.mjs +0 -329
  197. package/template/.agents/skills/design/impeccable/scripts/embed-prompt.mjs +0 -166
  198. package/template/.agents/skills/design/impeccable/scripts/font-match.mjs +0 -457
  199. package/template/.agents/skills/design/impeccable/scripts/generate-image.mjs +0 -447
  200. package/template/.agents/skills/design/impeccable/scripts/hook-admin.mjs +0 -819
  201. package/template/.agents/skills/design/impeccable/scripts/hook-before-edit.mjs +0 -538
  202. package/template/.agents/skills/design/impeccable/scripts/hook-lib.mjs +0 -2490
  203. package/template/.agents/skills/design/impeccable/scripts/hook.mjs +0 -79
  204. package/template/.agents/skills/design/impeccable/scripts/lib/artifact-schema.mjs +0 -93
  205. package/template/.agents/skills/design/impeccable/scripts/lib/composition-catalog.mjs +0 -200
  206. package/template/.agents/skills/design/impeccable/scripts/lib/concept-catalog.mjs +0 -396
  207. package/template/.agents/skills/design/impeccable/scripts/lib/design-parser.mjs +0 -880
  208. package/template/.agents/skills/design/impeccable/scripts/lib/font-fingerprint.mjs +0 -564
  209. package/template/.agents/skills/design/impeccable/scripts/lib/font-index.mjs +0 -130
  210. package/template/.agents/skills/design/impeccable/scripts/lib/hero-checks.mjs +0 -246
  211. package/template/.agents/skills/design/impeccable/scripts/lib/image-metrics.mjs +0 -306
  212. package/template/.agents/skills/design/impeccable/scripts/lib/impeccable-config.mjs +0 -640
  213. package/template/.agents/skills/design/impeccable/scripts/lib/impeccable-paths.mjs +0 -137
  214. package/template/.agents/skills/design/impeccable/scripts/lib/is-generated.mjs +0 -72
  215. package/template/.agents/skills/design/impeccable/scripts/lib/live-path-globs.mjs +0 -37
  216. package/template/.agents/skills/design/impeccable/scripts/lib/open-system-browser.mjs +0 -26
  217. package/template/.agents/skills/design/impeccable/scripts/lib/png.mjs +0 -281
  218. package/template/.agents/skills/design/impeccable/scripts/lib/provider.mjs +0 -5
  219. package/template/.agents/skills/design/impeccable/scripts/lib/raster.mjs +0 -194
  220. package/template/.agents/skills/design/impeccable/scripts/lib/roll-selection.mjs +0 -369
  221. package/template/.agents/skills/design/impeccable/scripts/lib/staleness-deep.mjs +0 -485
  222. package/template/.agents/skills/design/impeccable/scripts/lib/staleness-notice.mjs +0 -169
  223. package/template/.agents/skills/design/impeccable/scripts/lib/staleness.mjs +0 -533
  224. package/template/.agents/skills/design/impeccable/scripts/lib/surface-briefs.mjs +0 -149
  225. package/template/.agents/skills/design/impeccable/scripts/lib/target-args.mjs +0 -42
  226. package/template/.agents/skills/design/impeccable/scripts/lib/target-slug.mjs +0 -33
  227. package/template/.agents/skills/design/impeccable/scripts/lib/template-extensions.mjs +0 -146
  228. package/template/.agents/skills/design/impeccable/scripts/live/accept-css.mjs +0 -617
  229. package/template/.agents/skills/design/impeccable/scripts/live/accept-verify.mjs +0 -60
  230. package/template/.agents/skills/design/impeccable/scripts/live/browser-script-parts.mjs +0 -84
  231. package/template/.agents/skills/design/impeccable/scripts/live/completion.mjs +0 -28
  232. package/template/.agents/skills/design/impeccable/scripts/live/event-validation.mjs +0 -199
  233. package/template/.agents/skills/design/impeccable/scripts/live/frameworks/astro.mjs +0 -47
  234. package/template/.agents/skills/design/impeccable/scripts/live/frameworks/detect-utils.mjs +0 -73
  235. package/template/.agents/skills/design/impeccable/scripts/live/frameworks/index.mjs +0 -143
  236. package/template/.agents/skills/design/impeccable/scripts/live/frameworks/journal.mjs +0 -197
  237. package/template/.agents/skills/design/impeccable/scripts/live/frameworks/nextjs.mjs +0 -49
  238. package/template/.agents/skills/design/impeccable/scripts/live/frameworks/nuxt.mjs +0 -161
  239. package/template/.agents/skills/design/impeccable/scripts/live/frameworks/script-src.mjs +0 -17
  240. package/template/.agents/skills/design/impeccable/scripts/live/frameworks/static-html.mjs +0 -26
  241. package/template/.agents/skills/design/impeccable/scripts/live/frameworks/sveltekit.mjs +0 -71
  242. package/template/.agents/skills/design/impeccable/scripts/live/frameworks/tag-strategy.mjs +0 -247
  243. package/template/.agents/skills/design/impeccable/scripts/live/frameworks/tanstack-start.mjs +0 -70
  244. package/template/.agents/skills/design/impeccable/scripts/live/frameworks/vite-generic.mjs +0 -42
  245. package/template/.agents/skills/design/impeccable/scripts/live/generation-preflight.mjs +0 -149
  246. package/template/.agents/skills/design/impeccable/scripts/live/insert-ui.mjs +0 -458
  247. package/template/.agents/skills/design/impeccable/scripts/live/instructions.mjs +0 -142
  248. package/template/.agents/skills/design/impeccable/scripts/live/manual-apply.mjs +0 -939
  249. package/template/.agents/skills/design/impeccable/scripts/live/manual-edit-routes.mjs +0 -357
  250. package/template/.agents/skills/design/impeccable/scripts/live/manual-edits-buffer.mjs +0 -152
  251. package/template/.agents/skills/design/impeccable/scripts/live/poll-lanes.mjs +0 -14
  252. package/template/.agents/skills/design/impeccable/scripts/live/project-ignores.mjs +0 -139
  253. package/template/.agents/skills/design/impeccable/scripts/live/roots.mjs +0 -508
  254. package/template/.agents/skills/design/impeccable/scripts/live/session-store.mjs +0 -563
  255. package/template/.agents/skills/design/impeccable/scripts/live/source-lock.mjs +0 -105
  256. package/template/.agents/skills/design/impeccable/scripts/live/source-search.mjs +0 -105
  257. package/template/.agents/skills/design/impeccable/scripts/live/svelte-ast.mjs +0 -969
  258. package/template/.agents/skills/design/impeccable/scripts/live/svelte-component.mjs +0 -1366
  259. package/template/.agents/skills/design/impeccable/scripts/live/sveltekit-adapter.mjs +0 -304
  260. package/template/.agents/skills/design/impeccable/scripts/live/tanstack-adapter.mjs +0 -259
  261. package/template/.agents/skills/design/impeccable/scripts/live/ui-surfaces.mjs +0 -75
  262. package/template/.agents/skills/design/impeccable/scripts/live/vocabulary.mjs +0 -171
  263. package/template/.agents/skills/design/impeccable/scripts/live-accept.mjs +0 -938
  264. package/template/.agents/skills/design/impeccable/scripts/live-browser-dom.js +0 -146
  265. package/template/.agents/skills/design/impeccable/scripts/live-browser-ignores.js +0 -242
  266. package/template/.agents/skills/design/impeccable/scripts/live-browser-session.js +0 -123
  267. package/template/.agents/skills/design/impeccable/scripts/live-browser.js +0 -12551
  268. package/template/.agents/skills/design/impeccable/scripts/live-commit-manual-edits.mjs +0 -1200
  269. package/template/.agents/skills/design/impeccable/scripts/live-complete.mjs +0 -107
  270. package/template/.agents/skills/design/impeccable/scripts/live-copy-edit-agent.mjs +0 -800
  271. package/template/.agents/skills/design/impeccable/scripts/live-discard-manual-edits.mjs +0 -51
  272. package/template/.agents/skills/design/impeccable/scripts/live-inject.mjs +0 -463
  273. package/template/.agents/skills/design/impeccable/scripts/live-insert.mjs +0 -292
  274. package/template/.agents/skills/design/impeccable/scripts/live-manual-edit-evidence.mjs +0 -368
  275. package/template/.agents/skills/design/impeccable/scripts/live-poll.mjs +0 -430
  276. package/template/.agents/skills/design/impeccable/scripts/live-resume.mjs +0 -123
  277. package/template/.agents/skills/design/impeccable/scripts/live-server.mjs +0 -1698
  278. package/template/.agents/skills/design/impeccable/scripts/live-status.mjs +0 -71
  279. package/template/.agents/skills/design/impeccable/scripts/live-target.mjs +0 -30
  280. package/template/.agents/skills/design/impeccable/scripts/live-wrap.mjs +0 -927
  281. package/template/.agents/skills/design/impeccable/scripts/live.mjs +0 -334
  282. package/template/.agents/skills/design/impeccable/scripts/modern-screenshot.umd.js +0 -14
  283. package/template/.agents/skills/design/impeccable/scripts/palette.mjs +0 -628
  284. package/template/.agents/skills/design/impeccable/scripts/pin.mjs +0 -224
  285. package/template/.agents/skills/design/impeccable/scripts/serve-question.mjs +0 -1783
  286. package/template/.agents/skills/design/impeccable/scripts/surface-brief.mjs +0 -74
  287. package/template/.agents/skills/design/shadcn/SKILL.md +0 -277
  288. package/template/.agents/skills/design/shadcn/agents/openai.yml +0 -5
  289. package/template/.agents/skills/design/shadcn/assets/shadcn-small.png +0 -0
  290. package/template/.agents/skills/design/shadcn/assets/shadcn.png +0 -0
  291. package/template/.agents/skills/design/shadcn/cli.md +0 -290
  292. package/template/.agents/skills/design/shadcn/customization.md +0 -209
  293. package/template/.agents/skills/design/shadcn/evals/evals.json +0 -77
  294. package/template/.agents/skills/design/shadcn/mcp.md +0 -105
  295. package/template/.agents/skills/design/shadcn/registry.md +0 -277
  296. package/template/.agents/skills/design/shadcn/rules/base-vs-radix.md +0 -306
  297. package/template/.agents/skills/design/shadcn/rules/chat.md +0 -250
  298. package/template/.agents/skills/design/shadcn/rules/composition.md +0 -213
  299. package/template/.agents/skills/design/shadcn/rules/forms.md +0 -192
  300. package/template/.agents/skills/design/shadcn/rules/icons.md +0 -101
  301. package/template/.agents/skills/design/shadcn/rules/styling.md +0 -185
  302. package/template/.agents/skills/find-skills/SKILL.md +0 -141
  303. package/template/.agents/skills/hono/SKILL.md +0 -593
  304. package/template/.agents/skills/integrations/langfuse/SKILL.md +0 -150
  305. package/template/.agents/skills/integrations/langfuse/references/ci-cd.md +0 -41
  306. package/template/.agents/skills/integrations/langfuse/references/cli.md +0 -60
  307. package/template/.agents/skills/integrations/langfuse/references/create-dataset.md +0 -35
  308. package/template/.agents/skills/integrations/langfuse/references/error-analysis.md +0 -87
  309. package/template/.agents/skills/integrations/langfuse/references/instrumentation.md +0 -121
  310. package/template/.agents/skills/integrations/langfuse/references/judge-calibration.md +0 -293
  311. package/template/.agents/skills/integrations/langfuse/references/prompt-engineering.md +0 -35
  312. package/template/.agents/skills/integrations/langfuse/references/prompt-migration.md +0 -79
  313. package/template/.agents/skills/integrations/langfuse/references/sdk-upgrade.md +0 -34
  314. package/template/.agents/skills/integrations/langfuse/references/setting-up-evals.md +0 -65
  315. package/template/.agents/skills/integrations/langfuse/references/skill-feedback.md +0 -35
  316. package/template/.agents/skills/integrations/langfuse/references/trace-evaluator-upgrade.md +0 -76
  317. package/template/.agents/skills/integrations/langfuse/references/user-feedback.md +0 -66
  318. package/template/.agents/skills/integrations/langfuse/references/v4-project-migration.md +0 -75
  319. package/template/.agents/skills/integrations/posthog/SKILL.md +0 -102
  320. package/template/.agents/skills/integrations/posthog/references/error-tracking-alerts.md +0 -63
  321. package/template/.agents/skills/integrations/posthog/references/error-tracking-assigning-issues.md +0 -77
  322. package/template/.agents/skills/integrations/posthog/references/error-tracking-fingerprints.md +0 -57
  323. package/template/.agents/skills/integrations/posthog/references/error-tracking-monitoring.md +0 -140
  324. package/template/.agents/skills/integrations/posthog/references/error-tracking-nextjs.md +0 -490
  325. package/template/.agents/skills/integrations/posthog/references/error-tracking-source-maps.md +0 -45
  326. package/template/.agents/skills/integrations/posthog/references/feature-flags-best-practices.md +0 -139
  327. package/template/.agents/skills/integrations/posthog/references/feature-flags-react.md +0 -302
  328. package/template/.agents/skills/integrations/posthog/references/identify-users.md +0 -202
  329. package/template/.agents/skills/integrations/posthog/references/integration-example.md +0 -706
  330. package/template/.agents/skills/integrations/posthog/references/integration-nextjs.md +0 -385
  331. package/template/.agents/skills/integrations/posthog/references/integration-step-1-begin.md +0 -43
  332. package/template/.agents/skills/integrations/posthog/references/integration-step-2-edit.md +0 -37
  333. package/template/.agents/skills/integrations/posthog/references/integration-step-3-revise.md +0 -22
  334. package/template/.agents/skills/integrations/posthog/references/integration-step-4-conclude.md +0 -38
  335. package/template/.agents/skills/integrations/posthog/references/llm-analytics-anthropic.md +0 -200
  336. package/template/.agents/skills/integrations/posthog/references/llm-analytics-basics.md +0 -62
  337. package/template/.agents/skills/integrations/posthog/references/llm-analytics-costs.md +0 -197
  338. package/template/.agents/skills/integrations/posthog/references/llm-analytics-manual-capture.md +0 -397
  339. package/template/.agents/skills/integrations/posthog/references/llm-analytics-traces.md +0 -98
  340. package/template/.agents/skills/integrations/posthog/references/llm-analytics-vercel-ai.md +0 -120
  341. package/template/.agents/skills/repo/repo-ci/SKILL.md +0 -265
  342. package/template/.agents/skills/repo/repo-init-next-js/SKILL.md +0 -129
  343. package/template/.agents/skills/repo/repo-init-next-js/references/file-contents.md +0 -800
  344. package/template/.agents/skills/repo/repo-init-next-js/scripts/setup.sh +0 -47
  345. package/template/.agents/skills/repo/repo-init-node/SKILL.md +0 -196
  346. package/template/.agents/skills/skill-creator/LICENSE.txt +0 -202
  347. package/template/.agents/skills/skill-creator/SKILL.md +0 -485
  348. package/template/.agents/skills/skill-creator/agents/analyzer.md +0 -274
  349. package/template/.agents/skills/skill-creator/agents/comparator.md +0 -202
  350. package/template/.agents/skills/skill-creator/agents/grader.md +0 -223
  351. package/template/.agents/skills/skill-creator/assets/eval_review.html +0 -146
  352. package/template/.agents/skills/skill-creator/eval-viewer/generate_review.py +0 -471
  353. package/template/.agents/skills/skill-creator/eval-viewer/viewer.html +0 -1325
  354. package/template/.agents/skills/skill-creator/references/schemas.md +0 -430
  355. package/template/.agents/skills/skill-creator/scripts/__init__.py +0 -0
  356. package/template/.agents/skills/skill-creator/scripts/aggregate_benchmark.py +0 -401
  357. package/template/.agents/skills/skill-creator/scripts/generate_report.py +0 -326
  358. package/template/.agents/skills/skill-creator/scripts/improve_description.py +0 -247
  359. package/template/.agents/skills/skill-creator/scripts/package_skill.py +0 -136
  360. package/template/.agents/skills/skill-creator/scripts/quick_validate.py +0 -103
  361. package/template/.agents/skills/skill-creator/scripts/run_eval.py +0 -310
  362. package/template/.agents/skills/skill-creator/scripts/run_loop.py +0 -328
  363. package/template/.agents/skills/skill-creator/scripts/utils.py +0 -47
  364. package/template/.agents/skills/supabase/CHANGELOG.md +0 -78
  365. package/template/.agents/skills/supabase/SKILL.md +0 -149
  366. package/template/.agents/skills/supabase/assets/feedback-issue-template.md +0 -17
  367. package/template/.agents/skills/supabase/references/skill-feedback.md +0 -17
  368. package/template/.agents/skills/supabase-postgres-best-practices/CHANGELOG.md +0 -73
  369. package/template/.agents/skills/supabase-postgres-best-practices/SKILL.md +0 -64
  370. package/template/.agents/skills/supabase-postgres-best-practices/references/_contributing.md +0 -170
  371. package/template/.agents/skills/supabase-postgres-best-practices/references/_sections.md +0 -39
  372. package/template/.agents/skills/supabase-postgres-best-practices/references/_template.md +0 -34
  373. package/template/.agents/skills/supabase-postgres-best-practices/references/advanced-full-text-search.md +0 -55
  374. package/template/.agents/skills/supabase-postgres-best-practices/references/advanced-jsonb-indexing.md +0 -49
  375. package/template/.agents/skills/supabase-postgres-best-practices/references/conn-idle-timeout.md +0 -46
  376. package/template/.agents/skills/supabase-postgres-best-practices/references/conn-limits.md +0 -44
  377. package/template/.agents/skills/supabase-postgres-best-practices/references/conn-pooling.md +0 -41
  378. package/template/.agents/skills/supabase-postgres-best-practices/references/conn-prepared-statements.md +0 -46
  379. package/template/.agents/skills/supabase-postgres-best-practices/references/data-batch-inserts.md +0 -54
  380. package/template/.agents/skills/supabase-postgres-best-practices/references/data-n-plus-one.md +0 -53
  381. package/template/.agents/skills/supabase-postgres-best-practices/references/data-pagination.md +0 -50
  382. package/template/.agents/skills/supabase-postgres-best-practices/references/data-upsert.md +0 -50
  383. package/template/.agents/skills/supabase-postgres-best-practices/references/lock-advisory.md +0 -56
  384. package/template/.agents/skills/supabase-postgres-best-practices/references/lock-deadlock-prevention.md +0 -68
  385. package/template/.agents/skills/supabase-postgres-best-practices/references/lock-short-transactions.md +0 -50
  386. package/template/.agents/skills/supabase-postgres-best-practices/references/lock-skip-locked.md +0 -54
  387. package/template/.agents/skills/supabase-postgres-best-practices/references/monitor-explain-analyze.md +0 -45
  388. package/template/.agents/skills/supabase-postgres-best-practices/references/monitor-pg-stat-statements.md +0 -55
  389. package/template/.agents/skills/supabase-postgres-best-practices/references/monitor-vacuum-analyze.md +0 -55
  390. package/template/.agents/skills/supabase-postgres-best-practices/references/query-composite-indexes.md +0 -44
  391. package/template/.agents/skills/supabase-postgres-best-practices/references/query-covering-indexes.md +0 -40
  392. package/template/.agents/skills/supabase-postgres-best-practices/references/query-index-types.md +0 -48
  393. package/template/.agents/skills/supabase-postgres-best-practices/references/query-missing-indexes.md +0 -43
  394. package/template/.agents/skills/supabase-postgres-best-practices/references/query-partial-indexes.md +0 -45
  395. package/template/.agents/skills/supabase-postgres-best-practices/references/schema-constraints.md +0 -80
  396. package/template/.agents/skills/supabase-postgres-best-practices/references/schema-data-types.md +0 -46
  397. package/template/.agents/skills/supabase-postgres-best-practices/references/schema-foreign-key-indexes.md +0 -59
  398. package/template/.agents/skills/supabase-postgres-best-practices/references/schema-lowercase-identifiers.md +0 -55
  399. package/template/.agents/skills/supabase-postgres-best-practices/references/schema-partitioning.md +0 -55
  400. package/template/.agents/skills/supabase-postgres-best-practices/references/schema-primary-keys.md +0 -61
  401. package/template/.agents/skills/supabase-postgres-best-practices/references/security-privileges.md +0 -54
  402. package/template/.agents/skills/supabase-postgres-best-practices/references/security-rls-basics.md +0 -50
  403. package/template/.agents/skills/supabase-postgres-best-practices/references/security-rls-performance.md +0 -63
  404. package/template/.agents/skills/writing/humanizer/LICENSE +0 -21
  405. package/template/.agents/skills/writing/humanizer/README.md +0 -209
  406. package/template/.agents/skills/writing/humanizer/SKILL.md +0 -456
  407. package/template/.agents/upstreams.json +0 -115
@@ -1,969 +0,0 @@
1
- /**
2
- * AST-based Svelte scaffolding for live component previews.
3
- *
4
- * The scaffolder turns the selected block of a route's markup into a detached
5
- * preview component whose dynamic values arrive as props. The old
6
- * implementation matched `{...}` with a regex, which flattened control-flow
7
- * blocks ({#each}, {#if}) into scalar text props and shipped structurally
8
- * wrong previews. This module uses the app's own svelte compiler
9
- * (parse with modern: true) and replaces only expressions that are FREE,
10
- * i.e. reference identifiers not bound by an enclosing template scope:
11
- *
12
- * {#each stages as stage, i} stages -> collection prop (array)
13
- * <span>{stage.label}</span> bound -> left verbatim
14
- * {/each}
15
- * <p>{footerNote}</p> free -> text prop (string)
16
- *
17
- * Constructs that cannot work in a detached component (component tags whose
18
- * imports live in the route file, bind:/use: directives, await blocks,
19
- * render tags) mark the analysis unsupported; the caller falls back to
20
- * source-preview mode, which keeps the markup inside the route file where
21
- * those references still resolve. A wrong preview is worse than a plain one.
22
- *
23
- * The compiler is resolved from the APP's node_modules, never bundled: the
24
- * preview must be parsed by the same svelte version that will compile it.
25
- */
26
-
27
- import { createRequire } from 'node:module';
28
- import path from 'node:path';
29
-
30
- const HANDLER_ATTR_RE = /^on[a-z]/;
31
-
32
- /**
33
- * Resolve the app's svelte compiler synchronously (svelte 5 ships a CJS
34
- * compiler build, so createRequire works and the accept/scaffold pipeline
35
- * stays synchronous). Returns { parse, compile, VERSION } or null.
36
- */
37
- export function loadSvelteCompiler(appRoot) {
38
- try {
39
- const req = createRequire(path.join(appRoot, 'package.json'));
40
- const mod = req('svelte/compiler');
41
- if (typeof mod.parse !== 'function') return null;
42
- const major = parseInt(String(mod.VERSION || '0'), 10);
43
- if (major < 5) return null; // detached mount() previews are svelte 5 only
44
- return { parse: mod.parse, compile: mod.compile, VERSION: mod.VERSION };
45
- } catch {
46
- return null;
47
- }
48
- }
49
-
50
- // ---------------------------------------------------------------------------
51
- // ESTree helpers
52
- // ---------------------------------------------------------------------------
53
-
54
- /**
55
- * Collect the root identifiers an ESTree expression reads. Walks generically;
56
- * skips non-computed member properties and non-computed/non-shorthand object
57
- * keys, which are names, not references.
58
- */
59
- export function collectRootIdentifiers(node, out = new Set()) {
60
- if (!node || typeof node !== 'object') return out;
61
- if (Array.isArray(node)) {
62
- for (const item of node) collectRootIdentifiers(item, out);
63
- return out;
64
- }
65
- switch (node.type) {
66
- case 'Identifier':
67
- out.add(node.name);
68
- return out;
69
- case 'MemberExpression':
70
- collectRootIdentifiers(node.object, out);
71
- if (node.computed) collectRootIdentifiers(node.property, out);
72
- return out;
73
- case 'Property':
74
- if (node.computed) collectRootIdentifiers(node.key, out);
75
- collectRootIdentifiers(node.value, out);
76
- return out;
77
- case 'ArrowFunctionExpression':
78
- case 'FunctionExpression': {
79
- // Params shadow outer names inside the body.
80
- const bound = new Set();
81
- for (const param of node.params || []) collectPatternNames(param, bound);
82
- const inner = collectRootIdentifiers(node.body, new Set());
83
- for (const name of inner) if (!bound.has(name)) out.add(name);
84
- return out;
85
- }
86
- default: {
87
- for (const key of Object.keys(node)) {
88
- if (key === 'type' || key === 'start' || key === 'end' || key === 'loc' || key === 'range' || key === 'parent') continue;
89
- collectRootIdentifiers(node[key], out);
90
- }
91
- return out;
92
- }
93
- }
94
- }
95
-
96
- /** Collect names bound by a destructuring pattern (each contexts, const tags). */
97
- export function collectPatternNames(pattern, out = new Set()) {
98
- if (!pattern || typeof pattern !== 'object') return out;
99
- switch (pattern.type) {
100
- case 'Identifier':
101
- out.add(pattern.name);
102
- return out;
103
- case 'ObjectPattern':
104
- for (const prop of pattern.properties || []) {
105
- if (prop.type === 'RestElement') collectPatternNames(prop.argument, out);
106
- else collectPatternNames(prop.value, out);
107
- }
108
- return out;
109
- case 'ArrayPattern':
110
- for (const el of pattern.elements || []) if (el) collectPatternNames(el, out);
111
- return out;
112
- case 'AssignmentPattern':
113
- collectPatternNames(pattern.left, out);
114
- return out;
115
- case 'RestElement':
116
- collectPatternNames(pattern.argument, out);
117
- return out;
118
- default:
119
- return out;
120
- }
121
- }
122
-
123
- // ---------------------------------------------------------------------------
124
- // Template analysis
125
- // ---------------------------------------------------------------------------
126
-
127
- class Analysis {
128
- constructor(source) {
129
- this.source = source;
130
- this.replacements = []; // { start, end, prop } source ranges to swap
131
- this.contract = []; // [{ prop, expr, kind, ... }]
132
- this.byExpr = new Map(); // expr text -> contract entry
133
- this.usedNames = new Set();
134
- this.unsupported = null;
135
- }
136
-
137
- fail(reason) {
138
- if (!this.unsupported) this.unsupported = reason;
139
- }
140
-
141
- propFor(exprText, kind, extra = {}) {
142
- const existing = this.byExpr.get(exprText);
143
- if (existing) return existing;
144
- const base = derivePropName(exprText);
145
- let name = base;
146
- let n = 2;
147
- while (this.usedNames.has(name)) name = `${base}${n++}`;
148
- this.usedNames.add(name);
149
- const entry = { prop: name, expr: exprText, kind, ...extra };
150
- this.byExpr.set(exprText, entry);
151
- this.contract.push(entry);
152
- return entry;
153
- }
154
- }
155
-
156
- // A derived prop name lands in `let { <name> } = $props()`; a reserved word
157
- // there is a syntax error the session only hits at import time.
158
- const RESERVED_PROP_NAMES = new Set([
159
- 'await', 'break', 'case', 'catch', 'class', 'const', 'continue', 'debugger',
160
- 'default', 'delete', 'do', 'else', 'enum', 'export', 'extends', 'false',
161
- 'finally', 'for', 'function', 'if', 'implements', 'import', 'in',
162
- 'instanceof', 'interface', 'let', 'new', 'null', 'package', 'private',
163
- 'protected', 'public', 'return', 'static', 'super', 'switch', 'this',
164
- 'throw', 'true', 'try', 'typeof', 'undefined', 'var', 'void', 'while',
165
- 'with', 'yield',
166
- ]);
167
-
168
- export function derivePropName(expr) {
169
- const tail = String(expr).match(/(?:\.|\[["']?)([A-Za-z_$][\w$]*)["']?\]?\s*$/);
170
- const candidate = (tail && tail[1])
171
- || (String(expr).match(/^([A-Za-z_$][\w$]*)$/) || [])[1]
172
- || 'value';
173
- return RESERVED_PROP_NAMES.has(candidate) ? `${candidate}Value` : candidate;
174
- }
175
-
176
- function exprText(source, node) {
177
- return source.slice(node.start, node.end);
178
- }
179
-
180
- // Identifiers that resolve in ANY module scope. They are neither hydratable
181
- // props nor evidence of route coupling, so they count as neither free nor
182
- // bound: `{Math.round(x)}` must not mint a prop named `round`, and
183
- // `{fmt(stage.label)}` must not pass as global-only.
184
- const GLOBAL_IDENTIFIERS = new Set([
185
- 'Math', 'JSON', 'Date', 'Intl', 'Number', 'String', 'Boolean', 'Array',
186
- 'Object', 'Map', 'Set', 'Promise', 'RegExp', 'NaN', 'Infinity', 'undefined',
187
- 'isNaN', 'isFinite', 'parseInt', 'parseFloat', 'encodeURIComponent',
188
- 'decodeURIComponent', 'console', 'window', 'document', 'navigator',
189
- 'location', 'structuredClone', 'crypto',
190
- ]);
191
-
192
- function classifyRoots(node, scopes) {
193
- const roots = collectRootIdentifiers(node);
194
- let bound = 0;
195
- let free = 0;
196
- for (const name of roots) {
197
- if (GLOBAL_IDENTIFIERS.has(name)) continue;
198
- if (scopes.some((scope) => scope.has(name))) bound++;
199
- else free++;
200
- }
201
- return { bound, free };
202
- }
203
-
204
- function isFree(node, scopes) {
205
- const { bound, free } = classifyRoots(node, scopes);
206
- return free > 0 && bound === 0;
207
- }
208
-
209
- /**
210
- * An expression mixing loop-bound and outer free identifiers (e.g.
211
- * `{fmt(stage.label)}` where `fmt` lives in the route script) can neither
212
- * become a prop (the bound part varies per item) nor survive detachment
213
- * verbatim (the free name is undeclared in the preview and throws at mount,
214
- * past the compile gate, because globals make it legal to the compiler).
215
- * Source-preview mode is the only correct home for it.
216
- */
217
- function failOnMixedExpression(node, scopes, analysis, source) {
218
- const { bound, free } = classifyRoots(node, scopes);
219
- if (bound > 0 && free > 0) {
220
- analysis.fail(`expression mixing loop and outer identifiers ({${exprText(source, node).slice(0, 60)}}) requires source-preview mode`);
221
- return true;
222
- }
223
- return false;
224
- }
225
-
226
- /**
227
- * Analyze a parsed template fragment. `scopes` is a stack of Sets of bound
228
- * names; the outermost call passes an empty stack.
229
- */
230
- function analyzeFragment(fragment, analysis, scopes) {
231
- if (!fragment || !Array.isArray(fragment.nodes)) return;
232
- // ConstTag declarations bind for the whole fragment.
233
- const fragmentScope = new Set();
234
- const nextScopes = [...scopes, fragmentScope];
235
- for (const node of fragment.nodes) {
236
- if (node.type === 'ConstTag' && node.declaration) {
237
- for (const decl of node.declaration.declarations || []) {
238
- collectPatternNames(decl.id, fragmentScope);
239
- }
240
- }
241
- }
242
- for (const node of fragment.nodes) analyzeNode(node, analysis, nextScopes);
243
- }
244
-
245
- function analyzeNode(node, analysis, scopes) {
246
- if (!node || analysis.unsupported) return;
247
- switch (node.type) {
248
- case 'Text':
249
- case 'Comment':
250
- return;
251
- case 'ExpressionTag': {
252
- if (failOnMixedExpression(node.expression, scopes, analysis, analysis.source)) return;
253
- if (isFree(node.expression, scopes)) {
254
- const text = exprText(analysis.source, node.expression);
255
- const entry = analysis.propFor(text, 'text');
256
- // node.start/end include the braces; keep them, swap the inside.
257
- analysis.replacements.push({ start: node.expression.start, end: node.expression.end, prop: entry.prop });
258
- }
259
- return;
260
- }
261
- case 'HtmlTag': {
262
- if (failOnMixedExpression(node.expression, scopes, analysis, analysis.source)) return;
263
- if (isFree(node.expression, scopes)) {
264
- const text = exprText(analysis.source, node.expression);
265
- const entry = analysis.propFor(text, 'raw');
266
- analysis.replacements.push({ start: node.expression.start, end: node.expression.end, prop: entry.prop });
267
- }
268
- return;
269
- }
270
- case 'ConstTag': {
271
- // Its expression may read free names; leave them: the declaration
272
- // travels with the markup and stays valid only if its inputs do.
273
- if (node.declaration) {
274
- for (const decl of node.declaration.declarations || []) {
275
- if (decl.init && failOnMixedExpression(decl.init, scopes, analysis, analysis.source)) return;
276
- if (decl.init && isFree(decl.init, scopes)) {
277
- const text = exprText(analysis.source, decl.init);
278
- const entry = analysis.propFor(text, 'text');
279
- analysis.replacements.push({ start: decl.init.start, end: decl.init.end, prop: entry.prop });
280
- }
281
- }
282
- }
283
- return;
284
- }
285
- case 'EachBlock': {
286
- if (failOnMixedExpression(node.expression, scopes, analysis, analysis.source)) return;
287
- if (isFree(node.expression, scopes)) {
288
- const text = exprText(analysis.source, node.expression);
289
- const item = describeEachItem(node, analysis.source);
290
- // Keyed each: the key must evaluate to a distinct value per hydrated
291
- // item or Svelte throws each_key_duplicate at mount. A key that is a
292
- // plain member of the item (the common `(item.id)` shape) gets a
293
- // synthetic per-index value injected by the browser (keyField).
294
- // Anything else cannot be hydrated safely; source-preview mode keeps
295
- // it correct.
296
- if (node.key) {
297
- const keyInfo = classifyEachKey(node);
298
- if (keyInfo.unsupported) {
299
- analysis.fail(keyInfo.unsupported);
300
- return;
301
- }
302
- if (keyInfo.keyField) {
303
- if (item.textSlots.some((slot) => slot.key === keyInfo.keyField)) {
304
- // The key doubles as a displayed slot; a synthetic value would
305
- // change visible text, and the displayed text may not be
306
- // unique. Not previewable in a detached component.
307
- analysis.fail('each key that is also a displayed field requires source-preview mode');
308
- return;
309
- }
310
- item.keyField = keyInfo.keyField;
311
- }
312
- }
313
- const entry = analysis.propFor(text, 'collection', { item });
314
- analysis.replacements.push({ start: node.expression.start, end: node.expression.end, prop: entry.prop });
315
- }
316
- const bound = new Set();
317
- if (node.context) collectPatternNames(node.context, bound);
318
- if (node.index) bound.add(node.index);
319
- analyzeFragment(node.body, analysis, [...scopes, bound]);
320
- if (node.fallback) analyzeFragment(node.fallback, analysis, scopes);
321
- return;
322
- }
323
- case 'IfBlock': {
324
- if (failOnMixedExpression(node.test, scopes, analysis, analysis.source)) return;
325
- if (isFree(node.test, scopes)) {
326
- const text = exprText(analysis.source, node.test);
327
- // The browser hydrates a free condition from what the live page
328
- // currently shows: when the consequent's root element is present
329
- // under the picked element, the condition is on.
330
- const entry = analysis.propFor(text, 'condition', {
331
- probe: describeElementProbe(node.consequent),
332
- });
333
- analysis.replacements.push({ start: node.test.start, end: node.test.end, prop: entry.prop });
334
- }
335
- analyzeFragment(node.consequent, analysis, scopes);
336
- if (node.alternate) analyzeFragment(node.alternate, analysis, scopes);
337
- return;
338
- }
339
- case 'KeyBlock': {
340
- if (failOnMixedExpression(node.expression, scopes, analysis, analysis.source)) return;
341
- if (isFree(node.expression, scopes)) {
342
- const text = exprText(analysis.source, node.expression);
343
- const entry = analysis.propFor(text, 'text');
344
- analysis.replacements.push({ start: node.expression.start, end: node.expression.end, prop: entry.prop });
345
- }
346
- analyzeFragment(node.fragment, analysis, scopes);
347
- return;
348
- }
349
- case 'SnippetBlock': {
350
- const bound = new Set();
351
- for (const param of node.parameters || []) collectPatternNames(param, bound);
352
- // The snippet's own name becomes available to render tags in this file.
353
- analyzeFragment(node.body, analysis, [...scopes, bound]);
354
- return;
355
- }
356
- case 'RegularElement':
357
- case 'SlotElement':
358
- case 'TitleElement': {
359
- if (node.name === 'script') {
360
- // An inline script inside the selected block carries route-scoped
361
- // code; running it a second time from a detached preview is wrong.
362
- analysis.fail('inline script element requires source-preview mode');
363
- return;
364
- }
365
- analyzeAttributes(node, analysis, scopes);
366
- if (!analysis.unsupported) analyzeFragment(node.fragment, analysis, scopes);
367
- return;
368
- }
369
- case 'SvelteElement':
370
- case 'SvelteFragment':
371
- case 'SvelteBoundary': {
372
- analyzeAttributes(node, analysis, scopes);
373
- if (!analysis.unsupported) analyzeFragment(node.fragment, analysis, scopes);
374
- return;
375
- }
376
- case 'Component':
377
- case 'SvelteComponent':
378
- case 'SvelteSelf':
379
- // The component's import lives in the route file; a detached preview
380
- // cannot resolve it. Source-preview mode keeps it working.
381
- analysis.fail(`component tag <${node.name || 'Component'}> requires source-preview mode`);
382
- return;
383
- case 'RenderTag':
384
- analysis.fail('render tag requires source-preview mode');
385
- return;
386
- case 'AwaitBlock':
387
- analysis.fail('await block requires source-preview mode');
388
- return;
389
- case 'SvelteHead':
390
- case 'SvelteWindow':
391
- case 'SvelteDocument':
392
- case 'SvelteBody':
393
- analysis.fail(`${node.type} requires source-preview mode`);
394
- return;
395
- default: {
396
- if (node.fragment) analyzeFragment(node.fragment, analysis, scopes);
397
- return;
398
- }
399
- }
400
- }
401
-
402
- function analyzeAttributes(node, analysis, scopes) {
403
- for (const attr of node.attributes || []) {
404
- switch (attr.type) {
405
- case 'Attribute': {
406
- if (attr.value === true) break;
407
- const parts = Array.isArray(attr.value) ? attr.value : [attr.value];
408
- for (const part of parts) {
409
- if (!part || part.type !== 'ExpressionTag') continue;
410
- if (failOnMixedExpression(part.expression, scopes, analysis, analysis.source)) return;
411
- if (!isFree(part.expression, scopes)) continue;
412
- const text = exprText(analysis.source, part.expression);
413
- const kind = HANDLER_ATTR_RE.test(attr.name) ? 'handler' : 'text';
414
- const entry = analysis.propFor(text, kind);
415
- analysis.replacements.push({ start: part.expression.start, end: part.expression.end, prop: entry.prop });
416
- }
417
- break;
418
- }
419
- case 'ClassDirective': {
420
- const expr = attr.expression;
421
- if (expr && failOnMixedExpression(expr, scopes, analysis, analysis.source)) return;
422
- if (expr && isFree(expr, scopes)) {
423
- const text = exprText(analysis.source, expr);
424
- // The directive's class name is literal, so the live DOM answers
425
- // the condition directly: the class is either present or not.
426
- const entry = analysis.propFor(text, 'condition', {
427
- probe: { className: attr.name },
428
- });
429
- analysis.replacements.push({ start: expr.start, end: expr.end, prop: entry.prop });
430
- }
431
- break;
432
- }
433
- case 'StyleDirective': {
434
- // Unlike ClassDirective, a style directive stores its value in
435
- // attribute shape: `true` for the shorthand, else an array of parts.
436
- const parts = attr.value === true ? [] : (Array.isArray(attr.value) ? attr.value : [attr.value]);
437
- for (const part of parts) {
438
- if (part?.type === 'ExpressionTag'
439
- && failOnMixedExpression(part.expression, scopes, analysis, analysis.source)) {
440
- return;
441
- }
442
- }
443
- const dynamic = parts.some((part) => part?.type === 'ExpressionTag' && isFree(part.expression, scopes));
444
- const shorthandFree = attr.value === true && isFree({ type: 'Identifier', name: attr.name }, scopes);
445
- if (dynamic || shorthandFree) {
446
- // style:opacity={x} carries a css VALUE, not a boolean, and the
447
- // computed value on the live element is not reliably recoverable in
448
- // the shape the expression produced. A falsified style is worse
449
- // than an HMR-resetting preview.
450
- analysis.fail(`style:${attr.name} with a dynamic value requires source-preview mode`);
451
- }
452
- break;
453
- }
454
- case 'BindDirective':
455
- analysis.fail(`bind:${attr.name} requires source-preview mode`);
456
- return;
457
- case 'UseDirective':
458
- analysis.fail(`use:${attr.name} requires source-preview mode`);
459
- return;
460
- case 'AnimateDirective':
461
- case 'TransitionDirective':
462
- // Motion directives reference route-scoped or svelte/transition
463
- // imports; a detached preview cannot resolve them.
464
- analysis.fail(`${attr.type} requires source-preview mode`);
465
- return;
466
- case 'OnDirective': {
467
- // Legacy on:click syntax; treat like handler attributes.
468
- const expr = attr.expression;
469
- if (expr && failOnMixedExpression(expr, scopes, analysis, analysis.source)) return;
470
- if (expr && isFree(expr, scopes)) {
471
- const text = exprText(analysis.source, expr);
472
- const entry = analysis.propFor(text, 'handler');
473
- analysis.replacements.push({ start: expr.start, end: expr.end, prop: entry.prop });
474
- }
475
- break;
476
- }
477
- case 'SpreadAttribute':
478
- analysis.fail('spread attribute requires source-preview mode');
479
- return;
480
- default:
481
- break;
482
- }
483
- }
484
- }
485
-
486
- /**
487
- * Describe the repeating item of an each block for browser-side hydration:
488
- * the item's root element (tag + static classes, used to count live
489
- * iterations) and the ordered text slots that reference loop bindings.
490
- */
491
- function describeEachItem(node, source) {
492
- const body = node.body;
493
- const rootEl = (body?.nodes || []).find((n) => n.type === 'RegularElement');
494
-
495
- const textSlots = [];
496
- const staticTexts = [];
497
- let nestedUnsupported = false;
498
- const collectStatics = (fragment) => {
499
- for (const child of fragment?.nodes || []) {
500
- if (child.type === 'Text') {
501
- const trimmed = String(child.data || '').trim();
502
- if (trimmed) staticTexts.push(trimmed);
503
- } else if (child.type === 'IfBlock') {
504
- collectStatics(child.consequent);
505
- if (child.alternate) collectStatics(child.alternate);
506
- } else if (child.type === 'EachBlock') {
507
- collectStatics(child.body);
508
- } else if (child.fragment) {
509
- collectStatics(child.fragment);
510
- }
511
- }
512
- };
513
- collectStatics(body);
514
- const attrSlots = [];
515
- // The hydration item is a SHALLOW object whose string fields are the exact
516
- // property names the markup accesses, filled from the rendered page. That
517
- // model supports one item access per slot, optionally wrapped in a global
518
- // transform ({Math.round(r.score)} hydrates `score`). Shapes it cannot
519
- // represent split two ways: CRASHY ones would throw at mount time against a
520
- // shallow item (deep paths like r.meta.label, method calls like r.format())
521
- // and force the source-preview fallback; LOSSY ones render wrong but safe
522
- // (bare {r}, multi-access expressions that would double their text) and
523
- // also fall back in text position, where the damage is visible.
524
- const boundAs = (name, scopeInfos) => {
525
- for (let i = scopeInfos.length - 1; i >= 0; i--) {
526
- const info = scopeInfos[i];
527
- if (info.indexName === name) return 'index';
528
- if (info.itemName === name) return 'item';
529
- if (info.names.has(name)) return 'field';
530
- }
531
- return null;
532
- };
533
- const slotKeysOf = (expression, scopeInfos) => {
534
- const keys = new Set();
535
- let crashy = false;
536
- let lossy = false;
537
- let touches = false;
538
- const visit = (node, ctx) => {
539
- if (!node || typeof node !== 'object' || crashy) return;
540
- if (Array.isArray(node)) {
541
- for (const item of node) visit(item, {});
542
- return;
543
- }
544
- switch (node.type) {
545
- case 'Identifier': {
546
- const kind = boundAs(node.name, scopeInfos);
547
- if (!kind) return;
548
- touches = true;
549
- if (kind === 'index') return; // the runtime each provides it
550
- if (kind === 'item') { lossy = true; return; } // bare item reference
551
- if (ctx.callee) { crashy = true; return; } // field() on a hydrated string
552
- keys.add(node.name); // destructured context field
553
- return;
554
- }
555
- case 'MemberExpression': {
556
- if (
557
- !node.computed
558
- && node.object?.type === 'Identifier'
559
- && boundAs(node.object.name, scopeInfos) === 'item'
560
- && node.property?.type === 'Identifier'
561
- ) {
562
- touches = true;
563
- // item.a.b or item.method(): a shallow string field throws here.
564
- if (ctx.memberObject || ctx.callee) { crashy = true; return; }
565
- keys.add(node.property.name);
566
- return;
567
- }
568
- visit(node.object, { memberObject: true });
569
- if (node.computed) visit(node.property, {});
570
- return;
571
- }
572
- case 'CallExpression':
573
- visit(node.callee, { callee: true });
574
- for (const arg of node.arguments || []) visit(arg, {});
575
- return;
576
- case 'ArrowFunctionExpression':
577
- case 'FunctionExpression': {
578
- // Closures cannot hydrate; only lossy when they capture the item.
579
- const roots = collectRootIdentifiers(node);
580
- if ([...roots].some((name) => boundAs(name, scopeInfos))) { touches = true; lossy = true; }
581
- return;
582
- }
583
- case 'Property':
584
- if (node.computed) visit(node.key, {});
585
- visit(node.value, {});
586
- return;
587
- default: {
588
- for (const key of Object.keys(node)) {
589
- if (key === 'type' || key === 'start' || key === 'end' || key === 'loc' || key === 'range' || key === 'parent') continue;
590
- visit(node[key], {});
591
- }
592
- }
593
- }
594
- };
595
- visit(expression, {});
596
- if (crashy) return { crashy: true };
597
- if (lossy || keys.size > 1) return { lossy: true };
598
- if (!touches || keys.size === 0) return { skip: true };
599
- return { key: [...keys][0] };
600
- };
601
- const staticClassesOf = (el) => {
602
- const classes = [];
603
- for (const attr of el?.attributes || []) {
604
- if (attr.type === 'Attribute' && attr.name === 'class' && Array.isArray(attr.value)) {
605
- for (const part of attr.value) {
606
- if (part.type === 'Text') classes.push(...part.data.split(/\s+/).filter(Boolean));
607
- }
608
- }
609
- }
610
- return classes;
611
- };
612
- const scopeInfoOf = (eachNode) => {
613
- const names = new Set();
614
- if (eachNode.context) collectPatternNames(eachNode.context, names);
615
- return {
616
- names,
617
- itemName: eachNode.context?.type === 'Identifier' ? eachNode.context.name : null,
618
- indexName: eachNode.index || null,
619
- };
620
- };
621
- const walkForSlots = (fragment, scopeInfos) => {
622
- for (const child of fragment?.nodes || []) {
623
- if (child.type === 'ExpressionTag') {
624
- const slot = slotKeysOf(child.expression, scopeInfos);
625
- if (slot.crashy || slot.lossy) { nestedUnsupported = true; continue; }
626
- if (slot.skip) continue;
627
- textSlots.push({ key: slot.key, expr: exprText(source, child.expression) });
628
- } else if (child.type === 'RegularElement' || child.type === 'SvelteElement') {
629
- // Bound values in ATTRIBUTES (href={link.href}, src={item.img}) are
630
- // part of the item too: the browser reads the rendered attribute off
631
- // the live element, so the preview does not mount with empty links.
632
- // Only a single-expression attribute hydrates exactly; a mixed value
633
- // ("card {r.status}") stays unhydrated because the rendered attribute
634
- // is not separable into its parts, which was the prior behavior.
635
- for (const attr of child.attributes || []) {
636
- if (attr.type !== 'Attribute' || attr.value === true) continue;
637
- if (HANDLER_ATTR_RE.test(attr.name)) continue; // functions cannot hydrate
638
- const parts = Array.isArray(attr.value) ? attr.value : [attr.value];
639
- const exprParts = parts.filter((part) => part?.type === 'ExpressionTag');
640
- for (const part of exprParts) {
641
- const slot = slotKeysOf(part.expression, scopeInfos);
642
- if (slot.crashy) { nestedUnsupported = true; continue; }
643
- if (slot.skip || slot.lossy) continue;
644
- if (parts.length !== 1) continue; // mixed static+dynamic value
645
- attrSlots.push({
646
- key: slot.key,
647
- expr: exprText(source, part.expression),
648
- attr: attr.name,
649
- tag: child.name || null,
650
- classes: staticClassesOf(child),
651
- });
652
- }
653
- }
654
- walkForSlots(child.fragment, scopeInfos);
655
- continue;
656
- } else if (child.type === 'EachBlock') {
657
- const roots = collectRootIdentifiers(child.expression);
658
- const boundNested = [...roots].some((name) => boundAs(name, scopeInfos));
659
- if (boundNested) nestedUnsupported = true; // nested per-item arrays: no hydration plan yet
660
- walkForSlots(child.body, [...scopeInfos, scopeInfoOf(child)]);
661
- } else if (child.type === 'IfBlock') {
662
- walkForSlots(child.consequent, scopeInfos);
663
- if (child.alternate) walkForSlots(child.alternate, scopeInfos);
664
- } else if (child.fragment) {
665
- walkForSlots(child.fragment, scopeInfos);
666
- }
667
- }
668
- };
669
- walkForSlots(body, [scopeInfoOf(node)]);
670
-
671
- const staticClasses = [];
672
- for (const attr of rootEl?.attributes || []) {
673
- if (attr.type === 'Attribute' && attr.name === 'class' && Array.isArray(attr.value)) {
674
- for (const part of attr.value) {
675
- if (part.type === 'Text') staticClasses.push(...part.data.split(/\s+/).filter(Boolean));
676
- }
677
- }
678
- }
679
-
680
- return {
681
- rootTag: rootEl?.name || null,
682
- rootClasses: staticClasses,
683
- textSlots,
684
- attrSlots,
685
- staticTexts,
686
- nestedUnsupported,
687
- };
688
- }
689
-
690
- /**
691
- * Classify a keyed each block's key expression:
692
- * { keyField } member of the loop item (e.g. `(expense.id)` when the
693
- * context binds `expense`): browser injects a unique
694
- * per-index value under that field.
695
- * {} key is the whole loop item or the index: already
696
- * distinct per iteration, nothing to inject.
697
- * { unsupported } free or complex keys: cannot hydrate distinct values.
698
- */
699
- function classifyEachKey(node) {
700
- const bound = new Set();
701
- if (node.context) collectPatternNames(node.context, bound);
702
- if (node.index) bound.add(node.index);
703
- const key = node.key;
704
- const roots = collectRootIdentifiers(key);
705
- const usesLoopBinding = [...roots].some((name) => bound.has(name));
706
- if (!usesLoopBinding) {
707
- // A key that ignores the loop item is constant across iterations:
708
- // guaranteed duplicate keys at mount.
709
- return { unsupported: 'each key not derived from the loop item requires source-preview mode' };
710
- }
711
- if (key.type === 'Identifier' && bound.has(key.name)) return {};
712
- if (
713
- key.type === 'MemberExpression'
714
- && !key.computed
715
- && key.object?.type === 'Identifier'
716
- && bound.has(key.object.name)
717
- && key.property?.type === 'Identifier'
718
- ) {
719
- return { keyField: key.property.name };
720
- }
721
- return { unsupported: 'complex each key requires source-preview mode' };
722
- }
723
-
724
- /**
725
- * Describe a fragment's root element for browser presence probing:
726
- * { tag, classes } of the first RegularElement, or null for text-only
727
- * fragments (which cannot be probed reliably).
728
- */
729
- function describeElementProbe(fragment) {
730
- const rootEl = (fragment?.nodes || []).find((n) => n.type === 'RegularElement');
731
- if (!rootEl) return null;
732
- const classes = [];
733
- for (const attr of rootEl.attributes || []) {
734
- if (attr.type === 'Attribute' && attr.name === 'class' && Array.isArray(attr.value)) {
735
- for (const part of attr.value) {
736
- if (part.type === 'Text') classes.push(...part.data.split(/\s+/).filter(Boolean));
737
- }
738
- }
739
- }
740
- return { tag: rootEl.name, classes };
741
- }
742
-
743
- // ---------------------------------------------------------------------------
744
- // Public API
745
- // ---------------------------------------------------------------------------
746
-
747
- /**
748
- * Analyze a markup block and produce the prop-substituted scaffold markup and
749
- * the v2 prop contract. Returns { ok: false, reason } when the block needs
750
- * source-preview mode (parse failure or unsupported construct).
751
- */
752
- export function analyzeSvelteMarkup(markup, parse) {
753
- const source = String(markup || '');
754
- let ast;
755
- try {
756
- ast = parse(source, { modern: true });
757
- } catch (err) {
758
- return { ok: false, reason: `svelte parse failed: ${err.message}` };
759
- }
760
- if (ast.instance || ast.module) {
761
- return { ok: false, reason: 'selected block contains a script tag' };
762
- }
763
- const analysis = new Analysis(source);
764
- analyzeFragment(ast.fragment, analysis, []);
765
- if (analysis.unsupported) {
766
- return { ok: false, reason: analysis.unsupported };
767
- }
768
- for (const entry of analysis.contract) {
769
- if (entry.kind === 'collection' && entry.item?.nestedUnsupported) {
770
- return { ok: false, reason: 'per-item content (nested blocks or expressions) this preview cannot hydrate requires source-preview mode' };
771
- }
772
- }
773
-
774
- const markupWithProps = applyReplacements(source, analysis.replacements);
775
- return {
776
- ok: true,
777
- markupWithProps,
778
- contract: analysis.contract.map((entry) => ({
779
- prop: entry.prop,
780
- expr: entry.expr,
781
- kind: entry.kind,
782
- // Kept for backward compatibility with v1 consumers (fake e2e agent,
783
- // text-only restore paths).
784
- placeholder: `{${entry.expr}}`,
785
- ...(entry.item ? { item: entry.item } : {}),
786
- ...(entry.probe ? { probe: entry.probe } : {}),
787
- })),
788
- };
789
- }
790
-
791
- function applyReplacements(source, replacements) {
792
- const sorted = [...replacements].sort((a, b) => b.start - a.start);
793
- let out = source;
794
- for (const { start, end, prop } of sorted) {
795
- out = out.slice(0, start) + prop + out.slice(end);
796
- }
797
- return out;
798
- }
799
-
800
- /**
801
- * Restore a variant's markup back to route-source form: every free
802
- * identifier that matches a contract prop is replaced by its original
803
- * expression. AST-based so `{#each stages as stage}` restores to
804
- * `{#each data.stages as stage}` even though the prop appears without braces.
805
- */
806
- export function restoreSvelteMarkup(markup, contract, parse) {
807
- const source = String(markup || '');
808
- const byProp = new Map();
809
- for (const entry of contract || []) byProp.set(entry.prop, entry.expr);
810
- if (byProp.size === 0) return { ok: true, markup: source };
811
-
812
- let ast;
813
- try {
814
- ast = parse(source, { modern: true });
815
- } catch (err) {
816
- return { ok: false, reason: `variant parse failed: ${err.message}` };
817
- }
818
-
819
- const replacements = [];
820
- const visitExpr = (expression, scopes) => {
821
- if (!expression) return;
822
- collectFreeIdentifierRanges(expression, scopes, (name, start, end) => {
823
- const original = byProp.get(name);
824
- if (original != null && original !== name) replacements.push({ start, end, prop: original });
825
- });
826
- };
827
-
828
- const walk = (fragment, scopes) => {
829
- const fragmentScope = new Set();
830
- const nextScopes = [...scopes, fragmentScope];
831
- for (const node of fragment?.nodes || []) {
832
- if (node.type === 'ConstTag' && node.declaration) {
833
- for (const decl of node.declaration.declarations || []) collectPatternNames(decl.id, fragmentScope);
834
- }
835
- }
836
- for (const node of fragment?.nodes || []) {
837
- switch (node?.type) {
838
- case 'ExpressionTag':
839
- case 'HtmlTag':
840
- visitExpr(node.expression, nextScopes);
841
- break;
842
- case 'ConstTag':
843
- for (const decl of node.declaration?.declarations || []) visitExpr(decl.init, nextScopes);
844
- break;
845
- case 'EachBlock': {
846
- visitExpr(node.expression, nextScopes);
847
- const bound = new Set();
848
- if (node.context) collectPatternNames(node.context, bound);
849
- if (node.index) bound.add(node.index);
850
- // The key evaluates per item, so the loop context and index are in
851
- // scope there. Visiting it with outer scopes only let a contract
852
- // prop that shares a loop binding's name rewrite the key.
853
- if (node.key) visitExpr(node.key, [...nextScopes, bound]);
854
- walk(node.body, [...nextScopes, bound]);
855
- if (node.fallback) walk(node.fallback, nextScopes);
856
- break;
857
- }
858
- case 'IfBlock':
859
- visitExpr(node.test, nextScopes);
860
- walk(node.consequent, nextScopes);
861
- if (node.alternate) walk(node.alternate, nextScopes);
862
- break;
863
- case 'KeyBlock':
864
- visitExpr(node.expression, nextScopes);
865
- walk(node.fragment, nextScopes);
866
- break;
867
- case 'SnippetBlock': {
868
- const bound = new Set();
869
- for (const param of node.parameters || []) collectPatternNames(param, bound);
870
- walk(node.body, [...nextScopes, bound]);
871
- break;
872
- }
873
- default: {
874
- for (const attr of node?.attributes || []) {
875
- if (attr.type === 'Attribute' && Array.isArray(attr.value)) {
876
- for (const part of attr.value) {
877
- if (part?.type === 'ExpressionTag') visitExpr(part.expression, nextScopes);
878
- }
879
- } else if (attr.expression) {
880
- visitExpr(attr.expression, nextScopes);
881
- }
882
- }
883
- if (node?.fragment) walk(node.fragment, nextScopes);
884
- }
885
- }
886
- }
887
- };
888
- walk(ast.fragment, []);
889
-
890
- return { ok: true, markup: applyReplacements(source, replacements) };
891
- }
892
-
893
- /**
894
- * Report [name, start, end] for every free root identifier READ in an
895
- * expression (skips member properties, object keys, shadowed names).
896
- */
897
- function collectFreeIdentifierRanges(node, scopes, emit) {
898
- const visit = (n, localBound) => {
899
- if (!n || typeof n !== 'object') return;
900
- if (Array.isArray(n)) { for (const item of n) visit(item, localBound); return; }
901
- switch (n.type) {
902
- case 'Identifier': {
903
- const bound = localBound.has(n.name) || scopes.some((s) => s.has(n.name));
904
- if (!bound) emit(n.name, n.start, n.end);
905
- return;
906
- }
907
- case 'MemberExpression':
908
- visit(n.object, localBound);
909
- if (n.computed) visit(n.property, localBound);
910
- return;
911
- case 'Property':
912
- if (n.computed) visit(n.key, localBound);
913
- visit(n.value, localBound);
914
- return;
915
- case 'ArrowFunctionExpression':
916
- case 'FunctionExpression': {
917
- const inner = new Set(localBound);
918
- for (const param of n.params || []) collectPatternNames(param, inner);
919
- visit(n.body, inner);
920
- return;
921
- }
922
- default:
923
- for (const key of Object.keys(n)) {
924
- if (key === 'type' || key === 'start' || key === 'end' || key === 'loc' || key === 'range' || key === 'parent') continue;
925
- visit(n[key], localBound);
926
- }
927
- }
928
- };
929
- visit(node, new Set());
930
- }
931
-
932
- /**
933
- * Build the preview component's script block from a v2 contract, with
934
- * defaults that keep an unhydrated mount rendering instead of crashing.
935
- */
936
- // `/** @type {...} */` directly before a destructuring declaration is also
937
- // JSDoc's cast syntax, and Svelte 5.50+ re-emits the annotation in cast form
938
- // onto the template's own declaration: `var /** @type {...} */ (h1) = root()`.
939
- // That is a syntax error, so the browser's dynamic import of the variant dies
940
- // with "Unexpected token '('" and nothing renders. `@typedef` carries the same
941
- // shape without being a cast. Keep it a typedef;
942
- // tests/live-svelte-props-script.test.mjs compiles what these builders emit
943
- // and parses the result.
944
- export function buildPropsScriptV2(contract) {
945
- if (!contract || contract.length === 0) {
946
- return '<script>\n /** @typedef {Record<string, never>} Props */\n let {} = $props();\n</script>\n';
947
- }
948
- const defaults = {
949
- text: "''",
950
- raw: "''",
951
- condition: 'false',
952
- collection: '[]',
953
- handler: '() => {}',
954
- };
955
- const types = {
956
- text: 'string',
957
- raw: 'string',
958
- condition: 'boolean',
959
- collection: 'Array<Record<string, unknown>>',
960
- handler: '() => void',
961
- };
962
- const names = contract
963
- .map((c) => `${c.prop} = ${defaults[c.kind] ?? "''"}`)
964
- .join(', ');
965
- const typeFields = contract
966
- .map((c) => ` ${c.prop}?: ${types[c.kind] ?? 'string'};`)
967
- .join('\n');
968
- return `<script>\n /** @typedef {{\n${typeFields}\n }} Props */\n let { ${names} } = $props();\n</script>\n`;
969
- }