@bettercms-ai/kit 1.0.0 → 2.0.0-next.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (619) hide show
  1. package/README.md +33 -0
  2. package/dist/adapters/dock-values.d.ts +28 -0
  3. package/dist/adapters/dock-values.js +41 -0
  4. package/dist/adapters/dock-values.js.map +1 -0
  5. package/{adapters → dist/adapters}/icons.js +2 -2
  6. package/dist/adapters/icons.js.map +1 -0
  7. package/{adapters → dist/adapters}/lossy.d.ts +94 -31
  8. package/dist/adapters/lossy.js +419 -0
  9. package/dist/adapters/lossy.js.map +1 -0
  10. package/{adapters → dist/adapters}/prod-types.d.ts +91 -26
  11. package/{adapters → dist/adapters}/prod-types.js +82 -13
  12. package/dist/adapters/prod-types.js.map +1 -0
  13. package/{adapters → dist/adapters}/schema.d.ts +40 -1
  14. package/{adapters → dist/adapters}/schema.js +1038 -204
  15. package/dist/adapters/schema.js.map +1 -0
  16. package/{adapters → dist/adapters}/values.d.ts +26 -5
  17. package/{adapters → dist/adapters}/values.js +87 -17
  18. package/dist/adapters/values.js.map +1 -0
  19. package/dist/brand/logo.js +96 -0
  20. package/dist/brand/logo.js.map +1 -0
  21. package/{context.d.ts → dist/context.d.ts} +131 -2
  22. package/{context.js → dist/context.js} +44 -2
  23. package/dist/context.js.map +1 -0
  24. package/{controls → dist/controls}/button.d.ts +19 -5
  25. package/{controls → dist/controls}/button.js +125 -54
  26. package/dist/controls/button.js.map +1 -0
  27. package/{controls → dist/controls}/c1.js +1 -1
  28. package/dist/controls/c1.js.map +1 -0
  29. package/{controls → dist/controls}/c2.js +1 -1
  30. package/dist/controls/c2.js.map +1 -0
  31. package/{controls → dist/controls}/choice.d.ts +7 -2
  32. package/{controls → dist/controls}/choice.js +71 -18
  33. package/dist/controls/choice.js.map +1 -0
  34. package/{controls → dist/controls}/code-register.js +1 -1
  35. package/dist/controls/code-register.js.map +1 -0
  36. package/{controls → dist/controls}/code.js +10 -9
  37. package/dist/controls/code.js.map +1 -0
  38. package/{controls → dist/controls}/color.js +8 -8
  39. package/dist/controls/color.js.map +1 -0
  40. package/{controls → dist/controls}/component-services.d.ts +42 -0
  41. package/dist/controls/component-services.js +7 -0
  42. package/dist/controls/component-services.js.map +1 -0
  43. package/{controls → dist/controls}/date-register.js +1 -1
  44. package/dist/controls/date-register.js.map +1 -0
  45. package/{controls → dist/controls}/date-zone.js +8 -2
  46. package/dist/controls/date-zone.js.map +1 -0
  47. package/{controls → dist/controls}/date.js +32 -24
  48. package/dist/controls/date.js.map +1 -0
  49. package/dist/controls/form-picker-register.d.ts +7 -0
  50. package/{controls → dist/controls}/form-picker-register.js +2 -6
  51. package/dist/controls/form-picker-register.js.map +1 -0
  52. package/{controls → dist/controls}/form-picker.js +11 -9
  53. package/dist/controls/form-picker.js.map +1 -0
  54. package/{controls → dist/controls}/icon-register.js +1 -1
  55. package/dist/controls/icon-register.js.map +1 -0
  56. package/{controls → dist/controls}/icon.d.ts +4 -2
  57. package/{controls → dist/controls}/icon.js +32 -27
  58. package/dist/controls/icon.js.map +1 -0
  59. package/{controls → dist/controls}/index.d.ts +1 -1
  60. package/{controls → dist/controls}/index.js +4 -3
  61. package/dist/controls/index.js.map +1 -0
  62. package/{controls → dist/controls}/json-register.js +1 -1
  63. package/dist/controls/json-register.js.map +1 -0
  64. package/{controls → dist/controls}/json.js +13 -3
  65. package/dist/controls/json.js.map +1 -0
  66. package/{controls → dist/controls}/link.js +116 -200
  67. package/dist/controls/link.js.map +1 -0
  68. package/{controls → dist/controls}/location-register.js +1 -1
  69. package/dist/controls/location-register.js.map +1 -0
  70. package/{controls → dist/controls}/location.js +59 -40
  71. package/dist/controls/location.js.map +1 -0
  72. package/{controls → dist/controls}/media-register.js +1 -1
  73. package/dist/controls/media-register.js.map +1 -0
  74. package/{controls → dist/controls}/media-services.d.ts +6 -0
  75. package/dist/controls/media-services.js +0 -0
  76. package/{controls → dist/controls}/media.d.ts +21 -4
  77. package/{controls → dist/controls}/media.js +407 -130
  78. package/dist/controls/media.js.map +1 -0
  79. package/{controls → dist/controls}/number.js +10 -9
  80. package/dist/controls/number.js.map +1 -0
  81. package/{controls → dist/controls}/reference-register.js +1 -1
  82. package/dist/controls/reference-register.js.map +1 -0
  83. package/{controls → dist/controls}/reference-services.d.ts +4 -0
  84. package/dist/controls/reference-services.js +0 -0
  85. package/{controls → dist/controls}/reference.d.ts +1 -1
  86. package/{controls → dist/controls}/reference.js +93 -77
  87. package/dist/controls/reference.js.map +1 -0
  88. package/dist/controls/rich-text/article-shell.d.ts +145 -0
  89. package/dist/controls/rich-text/article-shell.js +510 -0
  90. package/dist/controls/rich-text/article-shell.js.map +1 -0
  91. package/dist/controls/rich-text/author.d.ts +25 -0
  92. package/dist/controls/rich-text/author.js +104 -0
  93. package/dist/controls/rich-text/author.js.map +1 -0
  94. package/dist/controls/rich-text/blocks.d.ts +60 -0
  95. package/{controls → dist/controls}/rich-text/blocks.js +164 -389
  96. package/dist/controls/rich-text/blocks.js.map +1 -0
  97. package/dist/controls/rich-text/button.d.ts +23 -0
  98. package/dist/controls/rich-text/button.js +85 -0
  99. package/dist/controls/rich-text/button.js.map +1 -0
  100. package/{controls → dist/controls}/rich-text/catalog.d.ts +7 -4
  101. package/{controls → dist/controls}/rich-text/catalog.js +94 -68
  102. package/dist/controls/rich-text/catalog.js.map +1 -0
  103. package/dist/controls/rich-text/checklist.d.ts +24 -0
  104. package/dist/controls/rich-text/checklist.js +115 -0
  105. package/dist/controls/rich-text/checklist.js.map +1 -0
  106. package/{controls → dist/controls}/rich-text/codec.d.ts +9 -1
  107. package/{controls → dist/controls}/rich-text/codec.js +7 -2
  108. package/dist/controls/rich-text/codec.js.map +1 -0
  109. package/dist/controls/rich-text/columns.d.ts +41 -0
  110. package/dist/controls/rich-text/columns.js +181 -0
  111. package/dist/controls/rich-text/columns.js.map +1 -0
  112. package/{controls → dist/controls}/rich-text/component-card.d.ts +1 -1
  113. package/{controls → dist/controls}/rich-text/component-card.js +417 -99
  114. package/dist/controls/rich-text/component-card.js.map +1 -0
  115. package/dist/controls/rich-text/component-panel.d.ts +47 -0
  116. package/dist/controls/rich-text/component-panel.js +141 -0
  117. package/dist/controls/rich-text/component-panel.js.map +1 -0
  118. package/{controls → dist/controls}/rich-text/control.d.ts +1 -2
  119. package/{controls → dist/controls}/rich-text/control.js +84 -42
  120. package/dist/controls/rich-text/control.js.map +1 -0
  121. package/{controls → dist/controls}/rich-text/dom.d.ts +53 -9
  122. package/{controls → dist/controls}/rich-text/dom.js +388 -50
  123. package/dist/controls/rich-text/dom.js.map +1 -0
  124. package/{controls → dist/controls}/rich-text/editor.d.ts +7 -0
  125. package/{controls → dist/controls}/rich-text/editor.js +226 -44
  126. package/dist/controls/rich-text/editor.js.map +1 -0
  127. package/dist/controls/rich-text/embed.d.ts +28 -0
  128. package/dist/controls/rich-text/embed.js +210 -0
  129. package/dist/controls/rich-text/embed.js.map +1 -0
  130. package/{controls → dist/controls}/rich-text/floating.d.ts +6 -1
  131. package/{controls → dist/controls}/rich-text/floating.js +35 -18
  132. package/dist/controls/rich-text/floating.js.map +1 -0
  133. package/dist/controls/rich-text/footnote.d.ts +41 -0
  134. package/dist/controls/rich-text/footnote.js +185 -0
  135. package/dist/controls/rich-text/footnote.js.map +1 -0
  136. package/dist/controls/rich-text/gallery.d.ts +28 -0
  137. package/dist/controls/rich-text/gallery.js +167 -0
  138. package/dist/controls/rich-text/gallery.js.map +1 -0
  139. package/{controls → dist/controls}/rich-text/index.js +1 -1
  140. package/dist/controls/rich-text/index.js.map +1 -0
  141. package/{controls → dist/controls}/rich-text/link-form.d.ts +3 -1
  142. package/dist/controls/rich-text/link-form.js +147 -0
  143. package/dist/controls/rich-text/link-form.js.map +1 -0
  144. package/{controls → dist/controls}/rich-text/markdown.js +4 -2
  145. package/dist/controls/rich-text/markdown.js.map +1 -0
  146. package/{controls → dist/controls}/rich-text/ops.js +17 -15
  147. package/dist/controls/rich-text/ops.js.map +1 -0
  148. package/dist/controls/rich-text/plain.d.ts +17 -0
  149. package/dist/controls/rich-text/plain.js +44 -0
  150. package/dist/controls/rich-text/plain.js.map +1 -0
  151. package/dist/controls/rich-text/related.d.ts +36 -0
  152. package/dist/controls/rich-text/related.js +213 -0
  153. package/dist/controls/rich-text/related.js.map +1 -0
  154. package/dist/controls/rich-text/toc.d.ts +46 -0
  155. package/dist/controls/rich-text/toc.js +161 -0
  156. package/dist/controls/rich-text/toc.js.map +1 -0
  157. package/{controls → dist/controls}/rich-text/toolbar.d.ts +6 -2
  158. package/{controls → dist/controls}/rich-text/toolbar.js +12 -7
  159. package/dist/controls/rich-text/toolbar.js.map +1 -0
  160. package/{controls → dist/controls}/scale.js +5 -5
  161. package/dist/controls/scale.js.map +1 -0
  162. package/dist/controls/slot-register.d.ts +6 -0
  163. package/{controls → dist/controls}/slot-register.js +2 -5
  164. package/dist/controls/slot-register.js.map +1 -0
  165. package/{controls → dist/controls}/slot-services.d.ts +25 -0
  166. package/dist/controls/slot-services.js +155 -0
  167. package/dist/controls/slot-services.js.map +1 -0
  168. package/{controls → dist/controls}/slot.js +42 -24
  169. package/dist/controls/slot.js.map +1 -0
  170. package/{controls → dist/controls}/table/index.js +1 -1
  171. package/dist/controls/table/index.js.map +1 -0
  172. package/{controls → dist/controls}/table/prod.js +3 -3
  173. package/dist/controls/table/prod.js.map +1 -0
  174. package/{controls → dist/controls}/table/refs.js +13 -2
  175. package/dist/controls/table/refs.js.map +1 -0
  176. package/{controls → dist/controls}/table/stored.js +1 -1
  177. package/dist/controls/table/stored.js.map +1 -0
  178. package/{controls → dist/controls}/table/table-editor.js +51 -48
  179. package/dist/controls/table/table-editor.js.map +1 -0
  180. package/{controls → dist/controls}/table/table-link.js +3 -3
  181. package/dist/controls/table/table-link.js.map +1 -0
  182. package/{controls → dist/controls}/table/table.js +3 -3
  183. package/dist/controls/table/table.js.map +1 -0
  184. package/{controls → dist/controls}/text.d.ts +2 -2
  185. package/{controls → dist/controls}/text.js +78 -43
  186. package/dist/controls/text.js.map +1 -0
  187. package/dist/controls/unknown-value.d.ts +17 -0
  188. package/dist/controls/unknown-value.js +66 -0
  189. package/dist/controls/unknown-value.js.map +1 -0
  190. package/dist/core/core.d.ts +70 -0
  191. package/dist/core/core.js +187 -0
  192. package/dist/core/core.js.map +1 -0
  193. package/dist/core/facets.d.ts +355 -0
  194. package/dist/core/facets.js +342 -0
  195. package/dist/core/facets.js.map +1 -0
  196. package/dist/core/index.d.ts +3 -0
  197. package/dist/core/index.js +3 -0
  198. package/{engine → dist/engine}/array.js +34 -21
  199. package/dist/engine/array.js.map +1 -0
  200. package/{engine → dist/engine}/bits.js +1 -1
  201. package/dist/engine/bits.js.map +1 -0
  202. package/{engine → dist/engine}/breadcrumb.d.ts +3 -3
  203. package/{engine → dist/engine}/breadcrumb.js +12 -36
  204. package/dist/engine/breadcrumb.js.map +1 -0
  205. package/{engine → dist/engine}/expand.d.ts +2 -4
  206. package/{engine → dist/engine}/expand.js +5 -22
  207. package/dist/engine/expand.js.map +1 -0
  208. package/{engine → dist/engine}/field-list.js +1 -1
  209. package/dist/engine/field-list.js.map +1 -0
  210. package/{engine → dist/engine}/field-shell.js +249 -121
  211. package/dist/engine/field-shell.js.map +1 -0
  212. package/{engine → dist/engine}/frame.d.ts +3 -1
  213. package/{engine → dist/engine}/frame.js +16 -14
  214. package/dist/engine/frame.js.map +1 -0
  215. package/{engine → dist/engine}/group-summary.d.ts +2 -1
  216. package/{engine → dist/engine}/group-summary.js +7 -3
  217. package/dist/engine/group-summary.js.map +1 -0
  218. package/{engine → dist/engine}/grouping.d.ts +19 -5
  219. package/{engine → dist/engine}/grouping.js +193 -21
  220. package/dist/engine/grouping.js.map +1 -0
  221. package/{engine → dist/engine}/item-list.d.ts +11 -1
  222. package/{engine → dist/engine}/item-list.js +35 -13
  223. package/dist/engine/item-list.js.map +1 -0
  224. package/{engine → dist/engine}/kit-field.js +45 -10
  225. package/dist/engine/kit-field.js.map +1 -0
  226. package/{engine → dist/engine}/schema-form.d.ts +27 -2
  227. package/{engine → dist/engine}/schema-form.js +182 -40
  228. package/dist/engine/schema-form.js.map +1 -0
  229. package/{engine → dist/engine}/slot.js +5 -15
  230. package/dist/engine/slot.js.map +1 -0
  231. package/dist/index.js +58 -0
  232. package/dist/index.js.map +1 -0
  233. package/{kit.css → dist/kit.css} +19 -0
  234. package/{lib → dist/lib}/cn.js +7 -4
  235. package/dist/lib/cn.js.map +1 -0
  236. package/{lib → dist/lib}/columns.js +4 -1
  237. package/dist/lib/columns.js.map +1 -0
  238. package/{lib → dist/lib}/frames.js +2 -2
  239. package/dist/lib/frames.js.map +1 -0
  240. package/dist/lib/icon-image.d.ts +10 -0
  241. package/dist/lib/icon-image.js +8 -0
  242. package/dist/lib/icon-image.js.map +1 -0
  243. package/{lib → dist/lib}/icons.js +4 -3
  244. package/dist/lib/icons.js.map +1 -0
  245. package/dist/lib/input-modality.d.ts +11 -0
  246. package/dist/lib/input-modality.js +47 -0
  247. package/dist/lib/input-modality.js.map +1 -0
  248. package/dist/lib/key-event.d.ts +11 -0
  249. package/dist/lib/key-event.js +16 -0
  250. package/dist/lib/key-event.js.map +1 -0
  251. package/{lib → dist/lib}/path.js +4 -2
  252. package/dist/lib/path.js.map +1 -0
  253. package/{lib → dist/lib}/schema.d.ts +34 -3
  254. package/{lib → dist/lib}/schema.js +110 -13
  255. package/dist/lib/schema.js.map +1 -0
  256. package/{lib → dist/lib}/utils.js +1 -1
  257. package/dist/lib/utils.js.map +1 -0
  258. package/{lib → dist/lib}/validate.d.ts +11 -1
  259. package/{lib → dist/lib}/validate.js +85 -11
  260. package/dist/lib/validate.js.map +1 -0
  261. package/dist/library/analysis.d.ts +23 -0
  262. package/dist/library/analysis.js +160 -0
  263. package/dist/library/analysis.js.map +1 -0
  264. package/dist/library/catalog.d.ts +57 -0
  265. package/dist/library/catalog.js +307 -0
  266. package/dist/library/catalog.js.map +1 -0
  267. package/dist/library/coreNotes.d.ts +8 -0
  268. package/dist/library/coreNotes.js +111 -0
  269. package/dist/library/coreNotes.js.map +1 -0
  270. package/dist/library/coverage.d.ts +40 -0
  271. package/dist/library/coverage.js +59 -0
  272. package/dist/library/coverage.js.map +1 -0
  273. package/dist/library/entries/advanced.entries.d.ts +2 -0
  274. package/dist/library/entries/advanced.entries.js +6579 -0
  275. package/dist/library/entries/advanced.entries.js.map +1 -0
  276. package/dist/library/entries/commerce.entries.d.ts +2 -0
  277. package/dist/library/entries/commerce.entries.js +4140 -0
  278. package/dist/library/entries/commerce.entries.js.map +1 -0
  279. package/dist/library/entries/company.entries.d.ts +2 -0
  280. package/dist/library/entries/company.entries.js +2731 -0
  281. package/dist/library/entries/company.entries.js.map +1 -0
  282. package/dist/library/entries/conversion.entries.d.ts +2 -0
  283. package/dist/library/entries/conversion.entries.js +3707 -0
  284. package/dist/library/entries/conversion.entries.js.map +1 -0
  285. package/dist/library/entries/data.entries.d.ts +7 -0
  286. package/dist/library/entries/data.entries.js +4613 -0
  287. package/dist/library/entries/data.entries.js.map +1 -0
  288. package/dist/library/entries/events.entries.d.ts +2 -0
  289. package/dist/library/entries/events.entries.js +3428 -0
  290. package/dist/library/entries/events.entries.js.map +1 -0
  291. package/dist/library/entries/features.entries.d.ts +2 -0
  292. package/dist/library/entries/features.entries.js +3157 -0
  293. package/dist/library/entries/features.entries.js.map +1 -0
  294. package/dist/library/entries/heroes.entries.d.ts +2 -0
  295. package/dist/library/entries/heroes.entries.js +3401 -0
  296. package/dist/library/entries/heroes.entries.js.map +1 -0
  297. package/dist/library/entries/index.d.ts +3 -0
  298. package/dist/library/entries/index.js +35 -0
  299. package/dist/library/entries/index.js.map +1 -0
  300. package/dist/library/entries/listings.entries.d.ts +2 -0
  301. package/dist/library/entries/listings.entries.js +2976 -0
  302. package/dist/library/entries/listings.entries.js.map +1 -0
  303. package/dist/library/entries/longform.entries.d.ts +2 -0
  304. package/dist/library/entries/longform.entries.js +4227 -0
  305. package/dist/library/entries/longform.entries.js.map +1 -0
  306. package/dist/library/entries/media.entries.d.ts +2 -0
  307. package/dist/library/entries/media.entries.js +2781 -0
  308. package/dist/library/entries/media.entries.js.map +1 -0
  309. package/dist/library/entries/navigation.entries.d.ts +2 -0
  310. package/dist/library/entries/navigation.entries.js +4011 -0
  311. package/dist/library/entries/navigation.entries.js.map +1 -0
  312. package/dist/library/entries/pricing.entries.d.ts +2 -0
  313. package/dist/library/entries/pricing.entries.js +2383 -0
  314. package/dist/library/entries/pricing.entries.js.map +1 -0
  315. package/dist/library/entries/social-proof.entries.d.ts +2 -0
  316. package/dist/library/entries/social-proof.entries.js +2297 -0
  317. package/dist/library/entries/social-proof.entries.js.map +1 -0
  318. package/dist/library/examples/bento.d.ts +92 -0
  319. package/dist/library/examples/bento.js +201 -0
  320. package/dist/library/examples/bento.js.map +1 -0
  321. package/dist/library/examples/blocks.d.ts +14 -0
  322. package/dist/library/examples/blocks.js +313 -0
  323. package/dist/library/examples/blocks.js.map +1 -0
  324. package/dist/library/examples/faq.d.ts +28 -0
  325. package/dist/library/examples/faq.js +230 -0
  326. package/dist/library/examples/faq.js.map +1 -0
  327. package/dist/library/examples/hero.d.ts +43 -0
  328. package/dist/library/examples/hero.js +222 -0
  329. package/dist/library/examples/hero.js.map +1 -0
  330. package/dist/library/examples/index.d.ts +36 -0
  331. package/dist/library/examples/index.js +109 -0
  332. package/dist/library/examples/index.js.map +1 -0
  333. package/dist/library/examples/journalStory.d.ts +93 -0
  334. package/dist/library/examples/journalStory.js +338 -0
  335. package/dist/library/examples/journalStory.js.map +1 -0
  336. package/dist/library/examples/megaMenu.d.ts +134 -0
  337. package/dist/library/examples/megaMenu.js +442 -0
  338. package/dist/library/examples/megaMenu.js.map +1 -0
  339. package/dist/library/examples/pricing.d.ts +103 -0
  340. package/dist/library/examples/pricing.js +479 -0
  341. package/dist/library/examples/pricing.js.map +1 -0
  342. package/dist/library/examples/tabs.d.ts +87 -0
  343. package/dist/library/examples/tabs.js +180 -0
  344. package/dist/library/examples/tabs.js.map +1 -0
  345. package/dist/library/export.d.ts +42 -0
  346. package/dist/library/export.js +80 -0
  347. package/dist/library/export.js.map +1 -0
  348. package/dist/library/index.d.ts +13 -0
  349. package/dist/library/index.js +6 -0
  350. package/dist/library/kit-catalog.json +1511 -0
  351. package/dist/library/types.d.ts +42 -0
  352. package/dist/library/types.js +0 -0
  353. package/{schema → dist/schema}/adapters.js +1 -2
  354. package/dist/schema/adapters.js.map +1 -0
  355. package/{schema → dist/schema}/help.d.ts +2 -0
  356. package/{schema → dist/schema}/help.js +6 -4
  357. package/dist/schema/help.js.map +1 -0
  358. package/{schema → dist/schema}/scale.js +1 -1
  359. package/dist/schema/scale.js.map +1 -0
  360. package/{schema → dist/schema}/sources.js +1 -1
  361. package/dist/schema/sources.js.map +1 -0
  362. package/{schema → dist/schema}/table.js +11 -10
  363. package/dist/schema/table.js.map +1 -0
  364. package/{schema → dist/schema}/types.d.ts +36 -1
  365. package/{schema → dist/schema}/types.js +1 -1
  366. package/dist/schema/types.js.map +1 -0
  367. package/dist/styles.css +2 -0
  368. package/dist/table/table-view.d.ts +14 -0
  369. package/dist/table/table-view.js +140 -0
  370. package/dist/table/table-view.js.map +1 -0
  371. package/dist/table/table.css +73 -0
  372. package/dist/testing/corpus.d.ts +63 -0
  373. package/dist/testing/corpus.js +129 -0
  374. package/dist/testing/corpus.js.map +1 -0
  375. package/dist/testing/test-form.d.ts +11 -0
  376. package/dist/testing/test-form.js +38 -0
  377. package/dist/testing/test-form.js.map +1 -0
  378. package/dist/theme.css +345 -0
  379. package/dist/theme.d.ts +22 -0
  380. package/dist/theme.js +43 -0
  381. package/dist/theme.js.map +1 -0
  382. package/dist/tokens.css +542 -0
  383. package/dist/types.js +0 -0
  384. package/{ui → dist/ui}/action-tray.d.ts +1 -1
  385. package/{ui → dist/ui}/action-tray.js +20 -19
  386. package/dist/ui/action-tray.js.map +1 -0
  387. package/dist/ui/autocomplete.d.ts +58 -0
  388. package/dist/ui/autocomplete.js +194 -0
  389. package/dist/ui/autocomplete.js.map +1 -0
  390. package/{ui → dist/ui}/button.d.ts +3 -3
  391. package/{ui → dist/ui}/button.js +8 -11
  392. package/dist/ui/button.js.map +1 -0
  393. package/{ui → dist/ui}/choice-switch.js +1 -1
  394. package/dist/ui/choice-switch.js.map +1 -0
  395. package/{ui → dist/ui}/controls.js +51 -39
  396. package/dist/ui/controls.js.map +1 -0
  397. package/{ui → dist/ui}/display.d.ts +10 -3
  398. package/{ui → dist/ui}/display.js +57 -46
  399. package/dist/ui/display.js.map +1 -0
  400. package/{ui → dist/ui}/expanded-editor-modal.d.ts +2 -2
  401. package/{ui → dist/ui}/expanded-editor-modal.js +6 -6
  402. package/dist/ui/expanded-editor-modal.js.map +1 -0
  403. package/{ui → dist/ui}/field-context.js +1 -1
  404. package/dist/ui/field-context.js.map +1 -0
  405. package/{ui → dist/ui}/floating-panel.d.ts +1 -1
  406. package/{ui → dist/ui}/floating-panel.js +28 -12
  407. package/dist/ui/floating-panel.js.map +1 -0
  408. package/{ui → dist/ui}/index.d.ts +4 -0
  409. package/dist/ui/index.js +18 -0
  410. package/{ui → dist/ui}/input.d.ts +1 -1
  411. package/{ui → dist/ui}/input.js +9 -9
  412. package/dist/ui/input.js.map +1 -0
  413. package/{ui → dist/ui}/mode-bar.js +3 -3
  414. package/dist/ui/mode-bar.js.map +1 -0
  415. package/{ui → dist/ui}/overlay.d.ts +77 -5
  416. package/dist/ui/overlay.js +697 -0
  417. package/dist/ui/overlay.js.map +1 -0
  418. package/{ui → dist/ui}/path-crumbs.js +37 -3
  419. package/dist/ui/path-crumbs.js.map +1 -0
  420. package/{ui → dist/ui}/presence.js +2 -2
  421. package/dist/ui/presence.js.map +1 -0
  422. package/{ui → dist/ui}/select.js +89 -87
  423. package/dist/ui/select.js.map +1 -0
  424. package/{ui → dist/ui}/shortcut-hint.js +1 -1
  425. package/dist/ui/shortcut-hint.js.map +1 -0
  426. package/dist/ui/spinner.d.ts +24 -0
  427. package/dist/ui/spinner.js +42 -0
  428. package/dist/ui/spinner.js.map +1 -0
  429. package/dist/ui/split-button.d.ts +32 -0
  430. package/dist/ui/split-button.js +60 -0
  431. package/dist/ui/split-button.js.map +1 -0
  432. package/{ui → dist/ui}/surfaces.js +1 -1
  433. package/dist/ui/surfaces.js.map +1 -0
  434. package/dist/ui/tab.d.ts +38 -0
  435. package/dist/ui/tab.js +87 -0
  436. package/dist/ui/tab.js.map +1 -0
  437. package/dist/ui/toast.d.ts +20 -0
  438. package/dist/ui/toast.js +115 -0
  439. package/dist/ui/toast.js.map +1 -0
  440. package/{ui → dist/ui}/tooltip-root.d.ts +4 -3
  441. package/{ui → dist/ui}/tooltip-root.js +9 -8
  442. package/dist/ui/tooltip-root.js.map +1 -0
  443. package/{ui → dist/ui}/variants.js +1 -1
  444. package/dist/ui/variants.js.map +1 -0
  445. package/package.json +83 -23
  446. package/adapters/icons.js.map +0 -1
  447. package/adapters/lossy.js +0 -233
  448. package/adapters/lossy.js.map +0 -1
  449. package/adapters/prod-types.js.map +0 -1
  450. package/adapters/schema.js.map +0 -1
  451. package/adapters/values.js.map +0 -1
  452. package/brand/logo.js +0 -9
  453. package/brand/logo.js.map +0 -1
  454. package/context.js.map +0 -1
  455. package/controls/button.js.map +0 -1
  456. package/controls/c1.js.map +0 -1
  457. package/controls/c2.js.map +0 -1
  458. package/controls/choice.js.map +0 -1
  459. package/controls/code-register.js.map +0 -1
  460. package/controls/code.js.map +0 -1
  461. package/controls/color.js.map +0 -1
  462. package/controls/date-register.js.map +0 -1
  463. package/controls/date-zone.js.map +0 -1
  464. package/controls/date.js.map +0 -1
  465. package/controls/form-picker-register.d.ts +0 -1
  466. package/controls/form-picker-register.js.map +0 -1
  467. package/controls/form-picker.js.map +0 -1
  468. package/controls/icon-register.js.map +0 -1
  469. package/controls/icon.js.map +0 -1
  470. package/controls/index.js.map +0 -1
  471. package/controls/json-register.js.map +0 -1
  472. package/controls/json.js.map +0 -1
  473. package/controls/link.js.map +0 -1
  474. package/controls/location-register.js.map +0 -1
  475. package/controls/location.js.map +0 -1
  476. package/controls/media-register.js.map +0 -1
  477. package/controls/media.js.map +0 -1
  478. package/controls/number.js.map +0 -1
  479. package/controls/reference-register.js.map +0 -1
  480. package/controls/reference.js.map +0 -1
  481. package/controls/rich-text/blocks.d.ts +0 -28
  482. package/controls/rich-text/blocks.js.map +0 -1
  483. package/controls/rich-text/catalog.js.map +0 -1
  484. package/controls/rich-text/codec.js.map +0 -1
  485. package/controls/rich-text/component-card.js.map +0 -1
  486. package/controls/rich-text/control.js.map +0 -1
  487. package/controls/rich-text/dom.js.map +0 -1
  488. package/controls/rich-text/editor.js.map +0 -1
  489. package/controls/rich-text/floating.js.map +0 -1
  490. package/controls/rich-text/index.js.map +0 -1
  491. package/controls/rich-text/link-form.js +0 -111
  492. package/controls/rich-text/link-form.js.map +0 -1
  493. package/controls/rich-text/markdown.js.map +0 -1
  494. package/controls/rich-text/ops.js.map +0 -1
  495. package/controls/rich-text/toolbar.js.map +0 -1
  496. package/controls/scale.js.map +0 -1
  497. package/controls/slot-register.d.ts +0 -1
  498. package/controls/slot-register.js.map +0 -1
  499. package/controls/slot-services.js +0 -38
  500. package/controls/slot-services.js.map +0 -1
  501. package/controls/slot.js.map +0 -1
  502. package/controls/table/index.js.map +0 -1
  503. package/controls/table/prod.js.map +0 -1
  504. package/controls/table/refs.js.map +0 -1
  505. package/controls/table/stored.js.map +0 -1
  506. package/controls/table/table-editor.js.map +0 -1
  507. package/controls/table/table-link.js.map +0 -1
  508. package/controls/table/table.js.map +0 -1
  509. package/controls/text.js.map +0 -1
  510. package/engine/array.js.map +0 -1
  511. package/engine/bits.js.map +0 -1
  512. package/engine/breadcrumb.js.map +0 -1
  513. package/engine/expand.js.map +0 -1
  514. package/engine/field-list.js.map +0 -1
  515. package/engine/field-shell.js.map +0 -1
  516. package/engine/frame.js.map +0 -1
  517. package/engine/group-summary.js.map +0 -1
  518. package/engine/grouping.js.map +0 -1
  519. package/engine/item-list.js.map +0 -1
  520. package/engine/kit-field.js.map +0 -1
  521. package/engine/schema-form.js.map +0 -1
  522. package/engine/slot.js.map +0 -1
  523. package/index.js +0 -55
  524. package/index.js.map +0 -1
  525. package/lib/cn.js.map +0 -1
  526. package/lib/columns.js.map +0 -1
  527. package/lib/frames.js.map +0 -1
  528. package/lib/icons.js.map +0 -1
  529. package/lib/path.js.map +0 -1
  530. package/lib/schema.js.map +0 -1
  531. package/lib/utils.js.map +0 -1
  532. package/lib/validate.js.map +0 -1
  533. package/schema/adapters.js.map +0 -1
  534. package/schema/help.js.map +0 -1
  535. package/schema/scale.js.map +0 -1
  536. package/schema/sources.js.map +0 -1
  537. package/schema/table.js.map +0 -1
  538. package/schema/types.js.map +0 -1
  539. package/ui/action-tray.js.map +0 -1
  540. package/ui/button.js.map +0 -1
  541. package/ui/choice-switch.js.map +0 -1
  542. package/ui/controls.js.map +0 -1
  543. package/ui/display.js.map +0 -1
  544. package/ui/expanded-editor-modal.js.map +0 -1
  545. package/ui/field-context.js.map +0 -1
  546. package/ui/floating-panel.js.map +0 -1
  547. package/ui/index.js +0 -13
  548. package/ui/input.js.map +0 -1
  549. package/ui/mode-bar.js.map +0 -1
  550. package/ui/overlay.js +0 -378
  551. package/ui/overlay.js.map +0 -1
  552. package/ui/path-crumbs.js.map +0 -1
  553. package/ui/presence.js.map +0 -1
  554. package/ui/select.js.map +0 -1
  555. package/ui/shortcut-hint.js.map +0 -1
  556. package/ui/surfaces.js.map +0 -1
  557. package/ui/tooltip-root.js.map +0 -1
  558. package/ui/variants.js.map +0 -1
  559. /package/{_virtual → dist/_virtual}/_rolldown/runtime.js +0 -0
  560. /package/{adapters → dist/adapters}/icons.d.ts +0 -0
  561. /package/{brand → dist/brand}/logo.d.ts +0 -0
  562. /package/{controls → dist/controls}/c1.d.ts +0 -0
  563. /package/{controls → dist/controls}/c2.d.ts +0 -0
  564. /package/{controls → dist/controls}/code-register.d.ts +0 -0
  565. /package/{controls → dist/controls}/code.d.ts +0 -0
  566. /package/{controls → dist/controls}/color.d.ts +0 -0
  567. /package/{controls → dist/controls}/date-register.d.ts +0 -0
  568. /package/{controls → dist/controls}/date-zone.d.ts +0 -0
  569. /package/{controls → dist/controls}/date.d.ts +0 -0
  570. /package/{controls → dist/controls}/form-picker.d.ts +0 -0
  571. /package/{controls → dist/controls}/icon-register.d.ts +0 -0
  572. /package/{controls → dist/controls}/json-register.d.ts +0 -0
  573. /package/{controls → dist/controls}/json.d.ts +0 -0
  574. /package/{controls → dist/controls}/link.d.ts +0 -0
  575. /package/{controls → dist/controls}/location-register.d.ts +0 -0
  576. /package/{controls → dist/controls}/location.d.ts +0 -0
  577. /package/{controls → dist/controls}/media-register.d.ts +0 -0
  578. /package/{controls → dist/controls}/number.d.ts +0 -0
  579. /package/{controls → dist/controls}/reference-register.d.ts +0 -0
  580. /package/{controls → dist/controls}/rich-text/index.d.ts +0 -0
  581. /package/{controls → dist/controls}/rich-text/markdown.d.ts +0 -0
  582. /package/{controls → dist/controls}/rich-text/ops.d.ts +0 -0
  583. /package/{controls → dist/controls}/scale.d.ts +0 -0
  584. /package/{controls → dist/controls}/slot.d.ts +0 -0
  585. /package/{controls → dist/controls}/table/index.d.ts +0 -0
  586. /package/{controls → dist/controls}/table/prod.d.ts +0 -0
  587. /package/{controls → dist/controls}/table/refs.d.ts +0 -0
  588. /package/{controls → dist/controls}/table/stored.d.ts +0 -0
  589. /package/{controls → dist/controls}/table/table-editor.d.ts +0 -0
  590. /package/{controls → dist/controls}/table/table-link.d.ts +0 -0
  591. /package/{controls → dist/controls}/table/table.d.ts +0 -0
  592. /package/{engine → dist/engine}/array.d.ts +0 -0
  593. /package/{engine → dist/engine}/bits.d.ts +0 -0
  594. /package/{engine → dist/engine}/field-list.d.ts +0 -0
  595. /package/{engine → dist/engine}/field-shell.d.ts +0 -0
  596. /package/{engine → dist/engine}/kit-field.d.ts +0 -0
  597. /package/{engine → dist/engine}/slot.d.ts +0 -0
  598. /package/{index.d.ts → dist/index.d.ts} +0 -0
  599. /package/{lib → dist/lib}/cn.d.ts +0 -0
  600. /package/{lib → dist/lib}/columns.d.ts +0 -0
  601. /package/{lib → dist/lib}/frames.d.ts +0 -0
  602. /package/{lib → dist/lib}/icons.d.ts +0 -0
  603. /package/{lib → dist/lib}/path.d.ts +0 -0
  604. /package/{lib → dist/lib}/utils.d.ts +0 -0
  605. /package/{schema → dist/schema}/adapters.d.ts +0 -0
  606. /package/{schema → dist/schema}/scale.d.ts +0 -0
  607. /package/{schema → dist/schema}/sources.d.ts +0 -0
  608. /package/{schema → dist/schema}/table.d.ts +0 -0
  609. /package/{types.d.ts → dist/types.d.ts} +0 -0
  610. /package/{ui → dist/ui}/choice-switch.d.ts +0 -0
  611. /package/{ui → dist/ui}/controls.d.ts +0 -0
  612. /package/{ui → dist/ui}/field-context.d.ts +0 -0
  613. /package/{ui → dist/ui}/mode-bar.d.ts +0 -0
  614. /package/{ui → dist/ui}/path-crumbs.d.ts +0 -0
  615. /package/{ui → dist/ui}/presence.d.ts +0 -0
  616. /package/{ui → dist/ui}/select.d.ts +0 -0
  617. /package/{ui → dist/ui}/shortcut-hint.d.ts +0 -0
  618. /package/{ui → dist/ui}/surfaces.d.ts +0 -0
  619. /package/{ui → dist/ui}/variants.d.ts +0 -0
@@ -1,9 +1,11 @@
1
- import { CONTROL_TYPES, HELP_MORE_MAX, ITEM_NAME_RE, UI_CONTROLS } from "./prod-types.js";
1
+ import { CATALOG, FEATURES, isHostConfigKey } from "./lossy.js";
2
+ import { CONTROL_TYPES, FIELDSET_DESCRIPTION_MAX, FIELD_HELP_MAX, HELP_MORE_MAX, ITEM_NAME_RE, MAX_FIELD_DEPTH, OPTION_DESCRIPTION_MAX, PROP_GROUP_DESCRIPTION_MAX, PROP_HELP_MAX, PROP_PLACEHOLDER_MAX, PROP_TAB_MAX, TAB_MAX as TAB_MAX$1, UI_CONTROLS, UI_PLACEHOLDER_MAX } from "./prod-types.js";
2
3
  import { dockIconOf, tablerIconOf } from "./icons.js";
3
- import { CATALOG, FEATURES } from "./lossy.js";
4
+ import { slotChildrenFromStored, slotChildrenToStored } from "../controls/slot-services.js";
4
5
  import { isDataTableProp, tableConfigFromOptions, tableOptionsFromConfig } from "../controls/table/prod.js";
5
- import { buttonFromProd, buttonToProd, datetimeFromProd, datetimeToProd, imageFromProd, imageToProd, linkFromProd, linkToProd, listFromProd, listToProd, ratingFromProd, ratingToProd, referenceFromProd, referenceToProd, scaleFromProd, scaleToProd, videoFromProd, videoToProd, zoneListFromProd, zoneListToProd } from "./values.js";
6
- //#region src/kit/adapters/schema.ts
6
+ import { buttonFromProd, buttonToProd, datetimeFromProd, datetimeToProd, imageFromProd, imageToProd, isCropRect, linkFromProd, linkToProd, listFromProd, listToProd, ratingFromProd, ratingToProd, referenceFromProd, referenceToProd, scaleFromProd, scaleToProd, typedRowsFromProd, typedRowsToProd, videoFromProd, videoToProd, zoneListFromProd, zoneListToProd } from "./values.js";
7
+ import { BLOCK_FIELD_TYPES_RENDERABLE_NOW, KEY_FORMAT, PRIMITIVE_ARRAY_RULE, RICHTEXT_FEATURES, isReservedKey, normaliseRichTextFeatures } from "@bettercms-ai/types";
8
+ //#region src/adapters/schema.ts
7
9
  var isObj = (v) => !!v && typeof v === "object" && !Array.isArray(v);
8
10
  function deepEqual(a, b) {
9
11
  if (a === b) return true;
@@ -14,6 +16,8 @@ function deepEqual(a, b) {
14
16
  var singular = (t) => t.endsWith("ies") ? `${t.slice(0, -3)}y` : t.endsWith("s") && !t.endsWith("ss") ? t.slice(0, -1) : t;
15
17
  var kebab = (s) => s.replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase().replace(/[^a-z0-9-]+/g, "-").replace(/^-+|-+$/g, "") || "schema";
16
18
  var camel = (s) => s.replace(/-([a-z0-9])/g, (_, c) => c.toUpperCase());
19
+ /** The slug a V2 schema name is stored under (a component's, when the host seeds a /kit block): `textBlock` → `text-block`. */
20
+ var schemaSlugOf = kebab;
17
21
  var capitalize = (s) => s.charAt(0).toUpperCase() + s.slice(1);
18
22
  var ZONE_NAMES = new Set(typeof Intl.supportedValuesOf === "function" ? [...Intl.supportedValuesOf("timeZone"), "UTC"] : ["UTC"]);
19
23
  /** An IANA zone name (isTimeZoneName): ICU's canonical list, or a current name Intl itself accepts (Asia/Kolkata). */
@@ -94,7 +98,7 @@ var PROP_TYPE = {
94
98
  code: "code"
95
99
  };
96
100
  /** Field types whose `config` the backend parses strictly (unknown keys stripped or refused). */
97
- var STRICT_CONFIG = new Set([
101
+ var STRICT_CONFIG = /* @__PURE__ */ new Set([
98
102
  "array",
99
103
  "reference",
100
104
  "multi-reference",
@@ -107,7 +111,9 @@ var STRICT_CONFIG = new Set([
107
111
  "tags",
108
112
  "rating",
109
113
  "code",
110
- "video"
114
+ "video",
115
+ "table",
116
+ "slot"
111
117
  ]);
112
118
  /** V2 list item types → prod `config.itemType` (ARRAY_ITEM_TYPES); a V2 `text` item reads back as `string`. */
113
119
  var PRIM_ITEM = {
@@ -125,6 +131,23 @@ var ENTRY_TARGET = (t) => ![
125
131
  "shopify",
126
132
  "collection"
127
133
  ].includes(t);
134
+ /** The non-entry targets a prop reference stores in config.targets (backend reference-target-config.ts REF_TARGET_KINDS). */
135
+ var REF_TARGET_KINDS = [
136
+ "page",
137
+ "form",
138
+ "collection"
139
+ ];
140
+ /**
141
+ * A prop reference whose only target is Shopify (backend reference-target-config.ts UNSUPPORTED_TARGETS refuses it by
142
+ * name) is stored as locked text holding the id (a list of them: a list of text), so the value stays the plain gid
143
+ * string V2 holds. This lock reason is the marker the read side turns back into V2's reference (allowed deviation
144
+ * "shopify reference").
145
+ */
146
+ var SHOPIFY_REF_LOCK = "A Shopify product id. No Shopify integration backs a reference in BetterCMS yet, so the id is kept as it is.";
147
+ var shopifyOnly = (r) => !!r?.to?.length && r.to.every((t) => t === "shopify");
148
+ /** backend REFERENCE_FILTER_MAX; parameters ($x) and the parent document (^) are refused there (reference_filter_invalid). */
149
+ var REFERENCE_FILTER_MAX = 500;
150
+ var filterKept = (f) => typeof f === "string" && f.trim() !== "" && f.length <= REFERENCE_FILTER_MAX && !/[$^]/.test(f);
128
151
  /** The old rating: a number with ui.control "rating" and these bounds read back as no `numeric` (read side only). */
129
152
  var RATING_CONFIG = {
130
153
  min: 0,
@@ -132,6 +155,12 @@ var RATING_CONFIG = {
132
155
  step: .5
133
156
  };
134
157
  /**
158
+ * A V2 rating sets no step and holds any number on its scale (a 4.8 average). Prod's unset step is 1, which refuses
159
+ * 4.8 (rating_value_invalid), so a rating with no step is written with step 0.1 (≤ RATING_MAX_STOPS stops on a 1–10
160
+ * scale) and that step reads back as none; a prod rating with no step reads back as its real step, 1.
161
+ */
162
+ var V2_RATING_STEP = .1;
163
+ /**
135
164
  * V2 block-picker category (KitCategory, and the core examples' own) → the nearest prod ComponentCategory. A category
136
165
  * with no row has no prod twin; the server defaults the component to "custom".
137
166
  */
@@ -154,33 +183,22 @@ var CATEGORY_TO_PROD = {
154
183
  Content: "content",
155
184
  Layout: "section"
156
185
  };
157
- /** The field types a kind:'block' model may hold (backend block-model-mapping.ts FIELD_PRIMITIVE). */
158
- var BLOCK_FIELD_TYPES = new Set([
159
- "text",
160
- "longtext",
161
- "richtext",
162
- "image",
163
- "link",
164
- "slug",
165
- "email",
166
- "phone",
167
- "color",
168
- "select",
169
- "number",
170
- "rating",
171
- "date",
172
- "datetime",
173
- "boolean"
174
- ]);
175
- /** A prop's config.minItems / maxItems cap (SLOT_ITEMS_CAP, componentPropDefSchema). */
186
+ /** The field types a kind:'block' model holds today: the ones a block primitive renders (types, staged to G7). */
187
+ var BLOCK_FIELD_TYPES = new Set(BLOCK_FIELD_TYPES_RENDERABLE_NOW);
188
+ /** A slot prop's config.minItems / maxItems cap (SLOT_ITEMS_CAP, componentPropDefSchema). */
176
189
  var SLOT_ITEMS_CAP = 50;
190
+ /** A table prop's rows and an array prop's items: their config.minItems / maxItems cap (LIST_ITEMS_CAP). */
191
+ var LIST_ITEMS_CAP = 500;
192
+ /** backend slotAllowIssues: a slot's config.allow holds 1–50 block names of 1–64 characters, no repeats. */
193
+ var SLOT_ALLOW_MAX = 50;
194
+ var SLOT_ALLOW_NAME_MAX = 64;
177
195
  /** Backend dock-vocabulary.ts SLIDER_MAX_STOPS: a slider's (max − min) / step. */
178
196
  var SLIDER_MAX_STOPS = 1e3;
179
197
  /**
180
198
  * The field types prod's unique flag accepts (uniqueFlagRules): a comparable scalar (UNIQUE_FIELD_TYPES, no two entries
181
199
  * share it; a reference only to one model) or a list (UNIQUE_ITEMS_FIELD_TYPES, no item repeats).
182
200
  */
183
- var UNIQUE_TYPES = new Set([
201
+ var UNIQUE_TYPES = /* @__PURE__ */ new Set([
184
202
  "text",
185
203
  "longtext",
186
204
  "slug",
@@ -197,14 +215,14 @@ var UNIQUE_TYPES = new Set([
197
215
  "multi-reference"
198
216
  ]);
199
217
  /** Prop types whose value is a list (UNIQUE_LIST_PROP_TYPES), and the sub-field types a table row compares (UNIQUE_ROW_PROP_TYPES). */
200
- var UNIQUE_LIST_PROPS = new Set([
218
+ var UNIQUE_LIST_PROPS = /* @__PURE__ */ new Set([
201
219
  "multiselect",
202
220
  "tags",
203
221
  "multi-reference",
204
222
  "table",
205
223
  "array"
206
224
  ]);
207
- var UNIQUE_ROW_PROPS = new Set([
225
+ var UNIQUE_ROW_PROPS = /* @__PURE__ */ new Set([
208
226
  "text",
209
227
  "url",
210
228
  "slug",
@@ -215,19 +233,19 @@ var UNIQUE_ROW_PROPS = new Set([
215
233
  "date"
216
234
  ]);
217
235
  /** Types measured by minLength / maxLength (fields: LENGTH_FIELD_TYPES; props: LENGTH_TWIN), and those that take a pattern. */
218
- var LENGTH_FIELDS = new Set([
236
+ var LENGTH_FIELDS = /* @__PURE__ */ new Set([
219
237
  "text",
220
238
  "longtext",
221
239
  "richtext",
222
240
  "document"
223
241
  ]);
224
- var LENGTH_PROPS = new Set([
242
+ var LENGTH_PROPS = /* @__PURE__ */ new Set([
225
243
  "text",
226
244
  "url",
227
245
  "longtext",
228
246
  "richtext"
229
247
  ]);
230
- var PATTERN_TYPES = new Set([
248
+ var PATTERN_TYPES = /* @__PURE__ */ new Set([
231
249
  "text",
232
250
  "longtext",
233
251
  "url"
@@ -236,7 +254,7 @@ var PATTERN_TYPES = new Set([
236
254
  * Leaf types whose value is an object a dotted preview path may read into (previewPathProblem OBJECT_VALUE_TYPES), and the
237
255
  * keys an image or video value holds (LEAF_VALUE_KEYS). V2 reads `image.alt`; prod stores the key as `altText`.
238
256
  */
239
- var OBJECT_VALUE_TYPES = new Set([
257
+ var OBJECT_VALUE_TYPES = /* @__PURE__ */ new Set([
240
258
  "image",
241
259
  "link",
242
260
  "button",
@@ -268,6 +286,8 @@ var LEAF_VALUE_KEYS = {
268
286
  ]
269
287
  };
270
288
  var IMAGE_PATH_KEY = { alt: "altText" };
289
+ /** The keys of a data table's value a preview path may end on (previewPathProblem `<table>.caption` / `.notes`). */
290
+ var TABLE_PATH_KEYS = ["caption", "notes"];
271
291
  var lose = (ctx, path, feature) => ctx.lossy.push({
272
292
  path,
273
293
  feature,
@@ -279,7 +299,7 @@ var cat = (ctx, path, feature) => ctx.catalog.push({
279
299
  reason: CATALOG[feature]
280
300
  });
281
301
  /** Keys every type keeps, including a field degraded to json. */
282
- var COMMON_KEYS = new Set([
302
+ var COMMON_KEYS = /* @__PURE__ */ new Set([
283
303
  "name",
284
304
  "title",
285
305
  "type",
@@ -296,14 +316,54 @@ var COMMON_KEYS = new Set([
296
316
  "advanced",
297
317
  "localized",
298
318
  "ai",
299
- "placeholder"
319
+ "placeholder",
320
+ "archived"
300
321
  ]);
301
- function shapeOf(f, lv, ctx, top) {
322
+ /** Types a slot setting may not be (backend slotSettingsHardening, and a model slot's SLOT_SETTINGS_NESTING: leaf fields only). */
323
+ var SETTING_NESTING_TYPES = /* @__PURE__ */ new Set([
324
+ "object",
325
+ "group",
326
+ "array",
327
+ "table",
328
+ "slot",
329
+ "form",
330
+ "modular"
331
+ ]);
332
+ /** backend slotSettingsHardening: at most this many per-child settings. */
333
+ var SLOT_SETTINGS_MAX = 20;
334
+ /** backend ITEM_TYPE_NAME (what a row's `_type` holds), and an item type label's cap. */
335
+ var ITEM_TYPE_NAME = /^[a-zA-Z][a-zA-Z0-9_-]{0,63}$/;
336
+ var ITEM_TYPE_LABEL_MAX = 120;
337
+ function shapeOf(f, lv, ctx, top, nest) {
338
+ const shape = shapeOfField(f, lv, ctx, top);
339
+ if (nest === "setting" && SETTING_NESTING_TYPES.has(shape.type)) return {
340
+ kind: "json",
341
+ type: "json",
342
+ feature: "slotSettings"
343
+ };
344
+ return shape;
345
+ }
346
+ /**
347
+ * The key a model field is stored under: a V2 name the API refuses (types' isReservedKey, the API lane: `derived`,
348
+ * `layout`, `page`, a forbidden key or a leading `_`; or one off KEY_FORMAT), renamed. V2 authoring follows the API
349
+ * lane, so `id` is a field key like any other (only the derive lane, which mints keys, reserves it).
350
+ */
351
+ function modelKeyOf(name) {
352
+ if (KEY_FORMAT.test(name) && !isReservedKey(name)) return name;
353
+ let k = name.replace(/^_+/, "").replace(/[^A-Za-z0-9_-]/g, "");
354
+ if (!/^[A-Za-z]/.test(k)) k = `f${k}`;
355
+ return isReservedKey(k) ? `${k}Field` : k;
356
+ }
357
+ /** The config keys a model's primitive list holds (types' PRIMITIVE_ARRAY_RULE). */
358
+ var MODEL_LIST_KEYS = new Set(PRIMITIVE_ARRAY_RULE.configKeys);
359
+ /** A slot's V2 allow list as the backend keeps it: 1–50 distinct block names of 1–64 characters (slotAllowIssues). */
360
+ var slotAllowOk = (allow) => Array.isArray(allow) && allow.length >= 1 && allow.length <= SLOT_ALLOW_MAX && new Set(allow).size === allow.length && allow.every((n) => typeof n === "string" && n === n.trim() && n.length >= 1 && n.length <= SLOT_ALLOW_NAME_MAX);
361
+ function shapeOfField(f, lv, ctx, top) {
302
362
  const props = ctx.lane === "props";
303
- const deep = lv >= 10;
363
+ const deep = lv >= MAX_FIELD_DEPTH;
304
364
  const of = Array.isArray(f.of) ? f.of : void 0;
305
- /** a prop reference needs a content model to point at (refTargetNamed): with none it is stored as json */
306
- const noEntry = (r) => props && !(r?.to ?? []).some(ENTRY_TARGET);
365
+ /** a prop reference names a content model or a page / form / collection target (refTargetNamed): with none it is stored as json */
366
+ const noEntry = (r) => props && !(r?.to ?? []).some((t) => ENTRY_TARGET(t) || REF_TARGET_KINDS.includes(t));
307
367
  switch (f.type) {
308
368
  case "object": return deep ? {
309
369
  kind: "json",
@@ -314,11 +374,22 @@ function shapeOf(f, lv, ctx, top) {
314
374
  type: "object"
315
375
  };
316
376
  case "array": {
317
- if (of && of.length > 1 && !props && of.every((i) => i.type === "object")) return {
318
- kind: "modular",
319
- type: "modular",
320
- items: of
321
- };
377
+ if (of && of.length > 1 && of.every((i) => i.type === "object")) {
378
+ if (!props) return {
379
+ kind: "modular",
380
+ type: "modular",
381
+ items: of
382
+ };
383
+ return deep ? {
384
+ kind: "json",
385
+ type: "json",
386
+ feature: "depth>10"
387
+ } : {
388
+ kind: "types",
389
+ type: "table",
390
+ items: of
391
+ };
392
+ }
322
393
  if (!of || of.length !== 1) return {
323
394
  kind: "json",
324
395
  type: "json",
@@ -334,6 +405,13 @@ function shapeOf(f, lv, ctx, top) {
334
405
  type: props ? "table" : "array",
335
406
  item
336
407
  };
408
+ if (item.type === "reference" && shopifyOnly(item.reference)) return {
409
+ kind: "itemType",
410
+ type: "array",
411
+ item,
412
+ itemType: "text",
413
+ shopify: true
414
+ };
337
415
  if (item.type === "reference") return noEntry(item.reference) ? {
338
416
  kind: "json",
339
417
  type: "json",
@@ -356,24 +434,30 @@ function shapeOf(f, lv, ctx, top) {
356
434
  feature: "type:primitive-array"
357
435
  };
358
436
  }
359
- case "slot": return {
360
- kind: "json",
361
- type: "json",
362
- feature: "type:slot"
363
- };
364
- case "table": return props ? {
437
+ case "slot":
438
+ if (!props && !slotAllowOk(f.of?.allow)) return {
439
+ kind: "json",
440
+ type: "json",
441
+ feature: "type:slot"
442
+ };
443
+ return {
444
+ kind: "slot",
445
+ type: "slot"
446
+ };
447
+ case "table": return {
365
448
  kind: "leaf",
366
449
  type: "table"
367
- } : {
368
- kind: "json",
369
- type: "json",
370
- feature: "type:table"
371
450
  };
372
451
  case "reference":
373
452
  if (props && top && f.reference?.to?.length === 1 && f.reference.to[0] === "form" && !f.reference.filter) return {
374
453
  kind: "leaf",
375
454
  type: "form"
376
455
  };
456
+ if (shopifyOnly(f.reference)) return {
457
+ kind: "leaf",
458
+ type: "text",
459
+ shopify: true
460
+ };
377
461
  return noEntry(f.reference) ? {
378
462
  kind: "json",
379
463
  type: "json",
@@ -400,9 +484,25 @@ var trunc = (ctx, at, s, max, feature) => {
400
484
  lose(ctx, at, feature);
401
485
  return s.slice(0, max);
402
486
  };
403
- function convertField(f, at, lv, ctx, siblings, top, inTable = false) {
487
+ /** The prod field types that store a rich text and its features (config.features). */
488
+ var RICH_TYPES = /* @__PURE__ */ new Set(["richtext", "document"]);
489
+ /** Every feature, canonical: RICHTEXT_FEATURES through normaliseRichTextFeatures, so no alias is ever in it. */
490
+ var ALL_RICH_FEATURES = normaliseRichTextFeatures(RICHTEXT_FEATURES).features;
491
+ /**
492
+ * A rich text's features through types normaliseRichTextFeatures (its aliases expand there: `headings` is h1–h6), distinct,
493
+ * in the order given. `named`: something was not carried (a name outside RICHTEXT_FEATURES, a repeat, a non-list).
494
+ */
495
+ function richFeatures(v) {
496
+ const names = Array.isArray(v) ? v.filter((x) => typeof x === "string") : [];
497
+ const { features, unknown } = normaliseRichTextFeatures(names);
498
+ return {
499
+ kept: [...new Set(features)],
500
+ named: !Array.isArray(v) || names.length !== v.length || new Set(names).size !== names.length || unknown.length > 0
501
+ };
502
+ }
503
+ function convertField(f, at, lv, ctx, siblings, top, inTable = false, inItem = false, inSetting = false) {
404
504
  const props = ctx.lane === "props";
405
- const shape = shapeOf(f, lv, ctx, top);
505
+ const shape = shapeOf(f, lv, ctx, top, inTable ? "rows" : inItem ? "item" : inSetting ? "setting" : void 0);
406
506
  const node = {
407
507
  key: f.name,
408
508
  label: f.title,
@@ -414,17 +514,28 @@ function convertField(f, at, lv, ctx, siblings, top, inTable = false) {
414
514
  if (twins) {
415
515
  node.key = `${f.name}_${twins + 1}`;
416
516
  lose(ctx, `${at}/name`, "duplicate name");
517
+ } else if (!props && modelKeyOf(f.name) !== f.name) {
518
+ node.key = modelKeyOf(f.name);
519
+ lose(ctx, `${at}/name`, "reserved name");
417
520
  }
418
- const nesting = shape.kind === "object" || shape.kind === "zones";
419
- /** a list of items (objects, block models, scalars or entries): ui.itemLabel names one item */
420
- const listy = shape.kind === "zones" || shape.kind === "modular" || shape.kind === "itemType" || shape.kind === "refs";
521
+ const nesting = shape.kind === "object" || shape.kind === "zones" || shape.kind === "types";
522
+ /** a list of items (objects, block models, item types, scalars or entries): ui.itemLabel names one item */
523
+ const listy = shape.kind === "zones" || shape.kind === "modular" || shape.kind === "types" || shape.kind === "itemType" || shape.kind === "refs";
421
524
  const looseConfig = props || !STRICT_CONFIG.has(shape.type);
422
525
  if (shape.kind === "json") lose(ctx, `${at}/type`, shape.feature);
526
+ const shopify = (shape.kind === "leaf" || shape.kind === "itemType") && shape.shopify === true;
527
+ if (shopify) {
528
+ if (f.readOnly !== true) lose(ctx, `${at}/readOnly`, "shopify reference");
529
+ node.readOnly = true;
530
+ node.lockReason = SHOPIFY_REF_LOCK;
531
+ }
423
532
  if (!props && f.type === "url") node.config.inputType = "url";
424
533
  if (shape.type === "datetime" && !props) node.config.includeTime = true;
425
534
  if (shape.kind === "leaf" && shape.type === "table") node.config.mode = "data";
535
+ if (shape.kind === "types") node.config.mode = "types";
536
+ if (shape.kind === "slot" && props) node.config.multiple = true;
426
537
  if (listy) {
427
- const label = f.itemNoun !== void 0 ? capitalize(f.itemNoun) : shape.kind === "zones" && shape.item.title !== conventionItem(f.name, f.title).title ? shape.item.title : void 0;
538
+ const label = f.itemNoun !== void 0 ? capitalize(f.itemNoun) : props && shape.kind === "zones" && shape.item.title !== conventionItem(f.name, f.title).title ? shape.item.title : void 0;
428
539
  if (label !== void 0 && label.length <= 24) node.ui.itemLabel = label;
429
540
  }
430
541
  const devLock = f.access?.edit === "schema.manage" && !!f.lockReason;
@@ -435,23 +546,28 @@ function convertField(f, at, lv, ctx, siblings, top, inTable = false) {
435
546
  lose(ctx, p, shape.feature);
436
547
  continue;
437
548
  }
549
+ if (shopify && (k === "reference" || k === "readOnly")) continue;
550
+ if (shopify && (k === "access" || k === "lockReason")) {
551
+ lose(ctx, p, "shopify reference");
552
+ continue;
553
+ }
438
554
  switch (k) {
439
555
  case "name":
440
556
  case "title":
441
557
  case "type": break;
442
558
  case "description":
443
- node.helpText = trunc(ctx, p, f.description, props ? 200 : 500, "help>limit");
559
+ node.helpText = trunc(ctx, p, f.description, props ? PROP_HELP_MAX : FIELD_HELP_MAX, "help>limit");
444
560
  break;
445
561
  case "helpMore":
446
562
  node.helpMore = trunc(ctx, p, f.helpMore, HELP_MORE_MAX, "help>limit");
447
563
  break;
448
564
  case "placeholder":
449
- if (props) node.placeholder = trunc(ctx, p, f.placeholder, 120, "help>limit");
565
+ if (props) node.placeholder = trunc(ctx, p, f.placeholder, PROP_PLACEHOLDER_MAX, "help>limit");
450
566
  else if (looseConfig) node.placeholder = f.placeholder;
451
- else node.ui.placeholder = trunc(ctx, p, f.placeholder, 120, "help>limit");
567
+ else node.ui.placeholder = trunc(ctx, p, f.placeholder, UI_PLACEHOLDER_MAX, "help>limit");
452
568
  break;
453
569
  case "initialValue":
454
- if (props && (shape.type === "reference" || shape.type === "multi-reference")) cat(ctx, p, "seeded instance");
570
+ if (props && (shape.type === "reference" || shape.type === "multi-reference" || shape.kind === "slot")) cat(ctx, p, "seeded instance");
455
571
  else node.defaultValue = defaultToProd(f, shape, f.initialValue, p, ctx, lv, top);
456
572
  break;
457
573
  case "validation":
@@ -471,7 +587,7 @@ function convertField(f, at, lv, ctx, siblings, top, inTable = false) {
471
587
  ofToProd(f, shape, node, p, lv, ctx);
472
588
  break;
473
589
  case "openMode":
474
- if (nesting) node.openMode = f.openMode;
590
+ if (nesting || props && shape.type === "code") node.openMode = f.openMode;
475
591
  else lose(ctx, p, "openMode");
476
592
  break;
477
593
  case "preview":
@@ -509,6 +625,9 @@ function convertField(f, at, lv, ctx, siblings, top, inTable = false) {
509
625
  case "localized":
510
626
  node.localized = f.localized;
511
627
  break;
628
+ case "archived":
629
+ if (f.archived) node.archived = true;
630
+ break;
512
631
  case "advanced":
513
632
  node.advanced = f.advanced;
514
633
  break;
@@ -527,7 +646,7 @@ function convertField(f, at, lv, ctx, siblings, top, inTable = false) {
527
646
  else lose(ctx, p, "unsupported key");
528
647
  break;
529
648
  case "icon": {
530
- const dock = shape.kind === "object" || shape.kind === "modular" ? dockIconOf(f.icon) : void 0;
649
+ const dock = shape.kind === "object" || shape.kind === "modular" || shape.kind === "types" ? dockIconOf(f.icon) : void 0;
531
650
  if (dock) node.ui.icon = dock;
532
651
  else lose(ctx, p, "icon");
533
652
  break;
@@ -543,13 +662,14 @@ function convertField(f, at, lv, ctx, siblings, top, inTable = false) {
543
662
  break;
544
663
  }
545
664
  case "slotSettings":
546
- lose(ctx, p, "slotSettings");
665
+ if (shape.kind === "slot") slotSettingsToProd(f.slotSettings, node, p, lv, ctx);
666
+ else lose(ctx, p, "slotSettings");
547
667
  break;
548
668
  case "itemNoun":
549
669
  if (!listy || node.ui.itemLabel?.toLowerCase() !== f.itemNoun) lose(ctx, p, "itemNoun");
550
670
  break;
551
671
  case "arrayLayout":
552
- if (shape.kind === "zones" || shape.kind === "modular") node.ui.layout = f.arrayLayout;
672
+ if (shape.kind === "zones" || shape.kind === "modular" || shape.kind === "types") node.ui.layout = f.arrayLayout;
553
673
  else if ((shape.kind === "itemType" || shape.kind === "refs") && f.arrayLayout === "grid") node.ui.layout = "gallery";
554
674
  else lose(ctx, p, "layout");
555
675
  break;
@@ -557,6 +677,14 @@ function convertField(f, at, lv, ctx, siblings, top, inTable = false) {
557
677
  if (shape.type === "link" || shape.type === "image" || shape.type === "button") node.ui.valueShape = f.valueShape;
558
678
  else lose(ctx, p, "valueShape");
559
679
  break;
680
+ case "linkHref":
681
+ if (typeof f.linkHref === "string" && f.linkHref) node.config.linkHref = f.linkHref;
682
+ else lose(ctx, p, "unsupported key");
683
+ break;
684
+ case "svgOnly":
685
+ if (shape.type === "image" && f.svgOnly === true) node.config.media = "icon";
686
+ else if (f.svgOnly !== false) lose(ctx, p, "unsupported key");
687
+ break;
560
688
  case "timezone":
561
689
  if (shape.type === "date" || shape.type === "datetime") {
562
690
  const tz = f.timezone;
@@ -577,11 +705,26 @@ function convertField(f, at, lv, ctx, siblings, top, inTable = false) {
577
705
  if (shape.kind === "leaf" && shape.type === "table") Object.assign(node.config, tableConfigFromOptions(f.table));
578
706
  else lose(ctx, p, "type:table");
579
707
  break;
708
+ case "features":
709
+ if (!RICH_TYPES.has(shape.type)) lose(ctx, p, "unsupported key");
710
+ else {
711
+ const { kept, named } = richFeatures(f.features);
712
+ if (named) lose(ctx, p, "richtext features");
713
+ node.config.features = kept;
714
+ }
715
+ break;
580
716
  default: lose(ctx, p, "unsupported key");
581
717
  }
582
718
  }
719
+ if (props && f.description === void 0) node.helpText = null;
720
+ if (RICH_TYPES.has(shape.type) && shape.kind === "leaf" && f.features === void 0) node.config.features = [...ALL_RICH_FEATURES];
583
721
  if (f.itemNoun === void 0 && shape.kind === "zones" && node.ui.itemLabel !== void 0) cat(ctx, `${at}/itemNoun`, "itemNoun (derived)");
584
722
  placeInSet(f, node, at, ctx, top, siblings);
723
+ if (f.type === "rating" && node.type === "rating" && f.numeric?.step === void 0) {
724
+ const lo = typeof node.config.min === "number" ? node.config.min : 0;
725
+ const stops = ((typeof node.config.max === "number" ? node.config.max : 5) - lo) / V2_RATING_STEP;
726
+ if (Math.abs(stops - Math.round(stops)) < 1e-9 && Math.round(stops) <= 100) node.config.step = V2_RATING_STEP;
727
+ }
585
728
  if (node.ui.control === "slider" && !sliderOk(node.config)) {
586
729
  delete node.ui.control;
587
730
  lose(ctx, `${at}/layout`, "layout");
@@ -606,7 +749,16 @@ function sliderOk({ min, max, step }) {
606
749
  /** Where a field sits: its tab (fields lane) or prop group (props lane). */
607
750
  function placeInSet(f, node, at, ctx, top, siblings) {
608
751
  if (ctx.lane === "props") {
609
- if (f.group !== void 0) lose(ctx, `${at}/group`, "group (props)");
752
+ if (!top) {
753
+ if (f.group !== void 0) {
754
+ if (ctx.keptTabs.has(f.group)) node.ui.tab = { id: f.group };
755
+ else lose(ctx, `${at}/group`, "group (nested)");
756
+ }
757
+ } else {
758
+ const t = ctx.propTabs.get(f);
759
+ if (t?.loss) lose(ctx, `${at}/group`, t.loss);
760
+ if (t?.tab) node.ui.tab = ctx.definedTabs.has(t.tab.id) ? { id: t.tab.id } : (ctx.definedTabs.add(t.tab.id), t.tab);
761
+ }
610
762
  if (f.fieldset === void 0) return;
611
763
  const set = ctx.propSets.get(f.fieldset);
612
764
  if (!set) return lose(ctx, `${at}/fieldset`, "fieldset collision");
@@ -646,7 +798,7 @@ function propGroupOf(s, ctx) {
646
798
  if (ctx.groupOrder) g.order = Math.min(99, ctx.propOrder.get(s.name) ?? 0);
647
799
  return g;
648
800
  }
649
- var groupDescriptionOk = (d) => typeof d === "string" && d === d.trim() && d.length >= 1 && d.length <= 200;
801
+ var groupDescriptionOk = (d) => typeof d === "string" && d === d.trim() && d.length >= 1 && d.length <= PROP_GROUP_DESCRIPTION_MAX;
650
802
  /** The zone a datetime field fixes, if any (what lets a stored instant read back with its [Zone]). */
651
803
  var fixedZone = (f) => typeof f.timezone === "string" && f.timezone.startsWith("fixed:") ? f.timezone.slice(6) : void 0;
652
804
  /**
@@ -663,7 +815,7 @@ function defaultToProd(f, shape, v, at, ctx, lv, top) {
663
815
  var AS_IS = { k: "as-is" };
664
816
  /** The write side's plan, from the V2 field and the shape it is stored as. */
665
817
  function writePlan(f, shape, lv, ctx, top) {
666
- const kids = (fields) => new Map(fields.map((c) => [c.name, writePlan(c, shapeOf(c, lv + 1, ctx, false), lv + 1, ctx, false)]));
818
+ const kids = (fields, nest) => new Map(fields.map((c) => [c.name, writePlan(c, shapeOf(c, lv + 1, ctx, false, nest), lv + 1, ctx, false)]));
667
819
  switch (shape.kind) {
668
820
  case "json": return AS_IS;
669
821
  case "itemType": return shape.itemType === "image" || shape.itemType === "link" ? {
@@ -677,8 +829,13 @@ function writePlan(f, shape, lv, ctx, top) {
677
829
  case "zones": return {
678
830
  k: "rows",
679
831
  zoned: ctx.lane === "fields",
680
- fields: kids(shape.item.fields ?? [])
832
+ fields: kids(shape.item.fields ?? [], ctx.lane === "props" ? "rows" : void 0)
833
+ };
834
+ case "types": return {
835
+ k: "types",
836
+ types: new Map(shape.items.map((i) => [i.name, kids(i.fields ?? [], "item")]))
681
837
  };
838
+ case "slot": return ctx.lane === "fields" ? { k: "blocks" } : AS_IS;
682
839
  case "modular": {
683
840
  const top1 = (fields) => new Map(fields.map((c) => [c.name, writePlan(c, shapeOf(c, 1, ctx, false), 1, ctx, false)]));
684
841
  return {
@@ -688,6 +845,7 @@ function writePlan(f, shape, lv, ctx, top) {
688
845
  }
689
846
  case "refs": return { k: "refs" };
690
847
  case "leaf":
848
+ if (shape.shopify) return AS_IS;
691
849
  if (f.type === "link" || f.type === "url") return { k: "link" };
692
850
  if (f.type === "button" || f.type === "image") return { k: f.type };
693
851
  if (f.type === "datetime") return {
@@ -706,7 +864,7 @@ function leafPlan(f, prodType) {
706
864
  scale: {
707
865
  min: f.numeric?.min,
708
866
  max: f.numeric?.max,
709
- step: f.numeric?.step
867
+ step: f.numeric?.step ?? V2_RATING_STEP
710
868
  }
711
869
  };
712
870
  if (f.type === "number" && f.scale) return {
@@ -716,11 +874,12 @@ function leafPlan(f, prodType) {
716
874
  return prodType === "number" ? { k: "number" } : AS_IS;
717
875
  }
718
876
  /**
719
- * The read side's plan, from the V2 field read back (`zoned`: lists read from an array's repeatable zone; `modular`:
720
- * lists read from a modular field, whose item fields live in their block models and pass as stored).
877
+ * The read side's plan, from the V2 field read back (`zoned`: lists read from an array's repeatable zone and groups
878
+ * read from its non-repeatable zone; `modular`: lists read from a modular field, whose item fields live in their block
879
+ * models and pass as stored).
721
880
  */
722
- function readPlan(f, prodType, zoned, modular) {
723
- const kids = (fields) => new Map(fields.map((c) => [c.name, readPlan(c, "", zoned, modular)]));
881
+ function readPlan(f, prodType, zoned, modular, types, slots, blocks) {
882
+ const kids = (fields) => new Map(fields.map((c) => [c.name, readPlan(c, "", zoned, modular, types, slots, blocks)]));
724
883
  switch (f.type) {
725
884
  case "link":
726
885
  case "url": return { k: "link" };
@@ -731,16 +890,29 @@ function readPlan(f, prodType, zoned, modular) {
731
890
  zone: fixedZone(f)
732
891
  };
733
892
  case "date": return { k: "date" };
734
- case "reference": return { k: prodType === "form" ? "form" : "ref" };
893
+ case "reference":
894
+ if (shopifyOnly(f.reference)) return AS_IS;
895
+ return { k: prodType === "form" ? "form" : "ref" };
735
896
  case "object": return {
736
897
  k: "object",
898
+ ...zoned.has(f) ? { zoned: true } : {},
737
899
  fields: kids(f.fields ?? [])
738
900
  };
901
+ case "slot":
902
+ if (blocks?.has(f)) return { k: "blocks" };
903
+ return slots?.has(f) ? {
904
+ k: "slot",
905
+ multiple: slots.get(f)
906
+ } : AS_IS;
739
907
  case "array": {
740
908
  if (modular.has(f)) return {
741
909
  k: "modular",
742
910
  types: new Map((Array.isArray(f.of) ? f.of : []).map((i) => [i.name, kids(i.fields ?? [])]))
743
911
  };
912
+ if (types?.has(f)) return {
913
+ k: "types",
914
+ types: new Map((Array.isArray(f.of) ? f.of : []).map((i) => [i.name, kids(i.fields ?? [])]))
915
+ };
744
916
  const item = Array.isArray(f.of) && f.of.length === 1 ? f.of[0] : void 0;
745
917
  if (item?.type === "object") return {
746
918
  k: "rows",
@@ -751,7 +923,7 @@ function readPlan(f, prodType, zoned, modular) {
751
923
  k: "list",
752
924
  item: item.type
753
925
  };
754
- return item?.type === "reference" ? { k: "refs" } : AS_IS;
926
+ return item?.type === "reference" && !shopifyOnly(item.reference) ? { k: "refs" } : AS_IS;
755
927
  }
756
928
  default: return leafPlan(f, prodType);
757
929
  }
@@ -791,7 +963,10 @@ function planToProd(plan, v, lost, at = "") {
791
963
  case "list": return take(listToProd(v, plan.item));
792
964
  case "scale": return take(scaleToProd(v, plan.scale));
793
965
  case "rating": return take(ratingToProd(v, plan.scale));
794
- case "object": return mapKeys(v, plan.fields, (p, x, k) => planToProd(p, x, lost, under(k)));
966
+ case "object": {
967
+ const out = mapKeys(v, plan.fields, (p, x, k) => planToProd(p, x, lost, under(k)));
968
+ return plan.zoned && isObj(out) ? { nonRepeatable: out } : out;
969
+ }
795
970
  case "rows": {
796
971
  if (!Array.isArray(v)) return v;
797
972
  const rows = v.map((row, i) => mapKeys(row, plan.fields, (p, x, k) => planToProd(p, x, lost, `${under(i)}/${k}`)));
@@ -809,24 +984,46 @@ function planToProd(plan, v, lost, at = "") {
809
984
  data: fields ? mapKeys(rest, fields, (p, x, k) => planToProd(p, x, lost, `${under(i)}/${k}`)) : rest
810
985
  };
811
986
  });
987
+ case "blocks": return Array.isArray(v) ? v.map((c) => isObj(c) && typeof c._type === "string" ? {
988
+ ...c,
989
+ _type: kebab(c._type)
990
+ } : c) : v;
991
+ case "slot": {
992
+ const kids = (type) => plan.child?.(type);
993
+ return slotChildrenToStored(plan.family ? childVariantsToTypes(v, plan.family) : v, plan.multiple !== false, (type, values, i) => {
994
+ const fields = kids(type);
995
+ return fields ? mapKeys(values, fields, (p, x, k) => planToProd(p, x, lost, `${under(i)}/${k}`)) : values;
996
+ }, (rel) => lost?.push(under(rel)));
997
+ }
998
+ case "types": return take(typedRowsToProd(v, (row, i) => {
999
+ const fields = plan.types.get(row._type);
1000
+ return fields ? mapKeys(row, fields, (p, x, k) => planToProd(p, x, lost, `${under(i)}/${k}`)) : row;
1001
+ }));
812
1002
  default: return v;
813
1003
  }
814
1004
  }
815
1005
  /** The keys the kit reads back from a stored image object (imageFromProd) and writes again (imageToProd). */
816
- var IMAGE_KIT_KEYS = new Set([
1006
+ var IMAGE_KIT_KEYS = /* @__PURE__ */ new Set([
817
1007
  "url",
818
1008
  "id",
819
1009
  "name",
820
1010
  "altText",
821
1011
  "focal"
822
1012
  ]);
823
- /** Every key of a stored image the kit cannot hand back: a crop (read back only with the asset's pixel size) or a key it does not model. */
1013
+ /**
1014
+ * Every key of a stored image the kit cannot hand back. A crop rect is read back as it is (imageFromProd), and an older
1015
+ * `{ src, alt }` value reads as its url and alt text, so neither is a loss (QA item 3); a malformed crop, or a key the
1016
+ * kit does not model, is.
1017
+ */
824
1018
  function imageStoredLoss(v, rel, loss) {
825
1019
  if (!isObj(v)) return;
1020
+ const legacy = typeof v.url !== "string" && typeof v.src === "string";
826
1021
  for (const [k, x] of Object.entries(v)) {
827
1022
  if (IMAGE_KIT_KEYS.has(k) || x === void 0) continue;
1023
+ if (legacy && k === "src") continue;
1024
+ if (k === "alt" && typeof x === "string" && v.altText === void 0) continue;
828
1025
  if (k === "crop") {
829
- if (x != null) loss(`${rel}/crop`, "A saved crop is not read back by the kit; editing the image would drop it.");
1026
+ if (x != null && !isCropRect(x)) loss(`${rel}/crop`, "A saved crop the kit can't read; editing the image would drop it.");
830
1027
  } else loss(`${rel}/${k}`, `The image's saved "${k}" is not kept by the kit; editing the image would drop it.`);
831
1028
  }
832
1029
  }
@@ -859,7 +1056,14 @@ function planFromProd(plan, v, loss, rel = "") {
859
1056
  else if (isObj(x) && referenceFromProd(x) === null) loss(`${rel}${Array.isArray(v) ? `/${i}` : ""}` || "(value)", "A stored reference the kit can't read; editing it would drop it.");
860
1057
  }
861
1058
  return referenceFromProd(v);
862
- case "object": return mapKeys(v, plan.fields, (p, x, k) => planFromProd(p, x, loss, `${rel}/${k}`));
1059
+ case "object": {
1060
+ const env = plan.zoned && isObj(v) && "nonRepeatable" in v ? v : void 0;
1061
+ if (env && loss) {
1062
+ for (const k of Object.keys(env)) if (k !== "nonRepeatable" && env[k] !== void 0) loss(`${rel}/${k}`, `The group's saved "${k}" is not one of its fields; editing the group would drop it.`);
1063
+ }
1064
+ const at = env ? `${rel}/nonRepeatable` : rel;
1065
+ return mapKeys(env ? isObj(env.nonRepeatable) ? env.nonRepeatable : {} : v, plan.fields, (p, x, k) => planFromProd(p, x, loss, `${at}/${k}`));
1066
+ }
863
1067
  case "rows": {
864
1068
  const rows = plan.zoned ? zoneListFromProd(v) : v;
865
1069
  return Array.isArray(rows) ? rows.map((row, i) => mapKeys(row, plan.fields, (p, x, k) => planFromProd(p, x, loss, `${rel}/${i}/${k}`))) : rows;
@@ -875,6 +1079,27 @@ function planFromProd(plan, v, loss, rel = "") {
875
1079
  ...back
876
1080
  };
877
1081
  }) : v;
1082
+ case "blocks": return Array.isArray(v) ? v.map((c) => isObj(c) && typeof c._type === "string" ? {
1083
+ ...c,
1084
+ _type: camel(c._type)
1085
+ } : c) : v;
1086
+ case "slot": return slotChildrenFromStored(v, (type, values, i) => {
1087
+ const fields = plan.child?.(type);
1088
+ const read = fields ? mapKeys(values, fields, (p, x, k) => planFromProd(p, x, loss, `${rel}/${i}/${k}`)) : values;
1089
+ const variant = plan.family?.(type)?.current;
1090
+ return variant === void 0 ? read : {
1091
+ ...read,
1092
+ _variant: variant
1093
+ };
1094
+ }, (i, reason) => loss?.(`${rel}/${i}`, reason));
1095
+ case "types": return typedRowsFromProd(v, (row, i) => {
1096
+ const fields = plan.types.get(row._type);
1097
+ if (!fields) {
1098
+ loss?.(`${rel}/${i}/_type`, `A stored row of item type "${row._type}", which this list no longer declares; editing the list would drop it.`);
1099
+ return row;
1100
+ }
1101
+ return mapKeys(row, fields, (p, x, k) => planFromProd(p, x, loss, `${rel}/${i}/${k}`));
1102
+ });
878
1103
  default: return v;
879
1104
  }
880
1105
  }
@@ -895,7 +1120,7 @@ function diffPaths(a, b, path = "", out = []) {
895
1120
  if ((isObj(a) || a === void 0) && (isObj(b) || b === void 0) && (a !== void 0 || b !== void 0)) {
896
1121
  const A = a ?? {};
897
1122
  const B = b ?? {};
898
- for (const k of new Set([...Object.keys(A), ...Object.keys(B)])) diffPaths(A[k], B[k], `${path}/${k}`, out);
1123
+ for (const k of /* @__PURE__ */ new Set([...Object.keys(A), ...Object.keys(B)])) diffPaths(A[k], B[k], `${path}/${k}`, out);
899
1124
  } else if (Array.isArray(a) && Array.isArray(b)) for (let i = 0; i < Math.max(a.length, b.length); i++) diffPaths(a[i], b[i], `${path}/${i}`, out);
900
1125
  else out.push(path);
901
1126
  return out;
@@ -935,17 +1160,21 @@ function validationToProd(f, shape, node, at, ctx, inTable, top) {
935
1160
  case "max": {
936
1161
  const n = v;
937
1162
  const lo = k === "min";
938
- if (lengthy) if (isWhole(n)) set(lo ? "minLength" : "maxLength", n, "validation.length", "minLength", "maxLength");
939
- else lose(ctx, p, "validation.length");
940
- else if (t === "number") {
1163
+ if (lengthy) {
1164
+ if (isWhole(n)) set(lo ? "minLength" : "maxLength", n, "validation.length", "minLength", "maxLength");
1165
+ else lose(ctx, p, "validation.length");
1166
+ } else if (t === "number") {
941
1167
  const b = node.config.validation ??= {};
942
1168
  b[k] = n;
943
1169
  } else if (t === "rating") lose(ctx, p, "rating bounds");
944
- else if (shape.kind === "zones" || shape.kind === "modular" || shape.kind === "itemType") if (props && top ? isWhole(n, lo ? 0 : 1, SLOT_ITEMS_CAP) : isWhole(n)) set(lo ? "minItems" : "maxItems", n, "validation.items", "minItems", "maxItems");
945
- else lose(ctx, p, "validation.items");
946
- else if (shape.kind === "refs" || t === "multiselect" || t === "tags") if (t === "tags" ? isWhole(n, lo ? 0 : 1, 500) : isWhole(n)) set(k, n, "validation.items", "min", "max");
947
- else lose(ctx, p, "validation.items");
948
- else lose(ctx, p, "validation.length");
1170
+ else if (shape.kind === "zones" || shape.kind === "modular" || shape.kind === "types" || shape.kind === "slot" || shape.kind === "itemType") {
1171
+ const cap = shape.kind === "slot" ? SLOT_ITEMS_CAP : LIST_ITEMS_CAP;
1172
+ if (props && top || shape.kind === "slot" ? isWhole(n, lo ? 0 : 1, cap) : isWhole(n)) set(lo ? "minItems" : "maxItems", n, "validation.items", "minItems", "maxItems");
1173
+ else lose(ctx, p, "validation.items");
1174
+ } else if (shape.kind === "refs" || t === "multiselect" || t === "tags") {
1175
+ if (t === "tags" ? isWhole(n, lo ? 0 : 1, 500) : isWhole(n)) set(k, n, "validation.items", "min", "max");
1176
+ else lose(ctx, p, "validation.items");
1177
+ } else lose(ctx, p, "validation.length");
949
1178
  break;
950
1179
  }
951
1180
  case "recommendedMax":
@@ -962,9 +1191,11 @@ function validationToProd(f, shape, node, at, ctx, inTable, top) {
962
1191
  break;
963
1192
  case "unique":
964
1193
  if (val.unique === false) node.unique = false;
965
- else if (props) if (UNIQUE_LIST_PROPS.has(t) || inTable && UNIQUE_ROW_PROPS.has(t)) node.unique = true;
966
- else cat(ctx, p, "not applicable (props)");
967
- else {
1194
+ else if (shape.kind === "types") lose(ctx, p, "validation.unique");
1195
+ else if (props) {
1196
+ if (UNIQUE_LIST_PROPS.has(t) || inTable && UNIQUE_ROW_PROPS.has(t)) node.unique = true;
1197
+ else cat(ctx, p, "not applicable (props)");
1198
+ } else {
968
1199
  const polymorphic = t === "reference" && (f.reference?.to ?? []).filter(ENTRY_TARGET).length > 1;
969
1200
  if (UNIQUE_TYPES.has(t) && !polymorphic) node.unique = true;
970
1201
  else lose(ctx, p, "validation.unique");
@@ -1000,11 +1231,15 @@ function optionsToProd(f, shape, node, at, ctx) {
1000
1231
  label: o.label,
1001
1232
  value: o.value
1002
1233
  };
1003
- if (o.icon !== void 0) if (allIcons) out.icon = icons[i];
1004
- else lose(ctx, `${at}/${i}/icon`, "option icon");
1234
+ if (o.icon !== void 0) {
1235
+ if (allIcons) out.icon = icons[i];
1236
+ else lose(ctx, `${at}/${i}/icon`, "option icon");
1237
+ }
1005
1238
  const d = o.description;
1006
- if (d !== void 0) if (typeof d === "string" && d.trim() !== "" && !/[\r\n]/.test(d) && d.length <= 140) out.description = d;
1007
- else lose(ctx, `${at}/${i}/description`, "option description");
1239
+ if (d !== void 0) {
1240
+ if (typeof d === "string" && d.trim() !== "" && !/[\r\n]/.test(d) && d.length <= OPTION_DESCRIPTION_MAX) out.description = d;
1241
+ else lose(ctx, `${at}/${i}/description`, "option description");
1242
+ }
1008
1243
  for (const k of Object.keys(o)) if (![
1009
1244
  "value",
1010
1245
  "label",
@@ -1019,14 +1254,13 @@ function layoutToProd(f, shape, node, at, ctx) {
1019
1254
  const l = f.layout;
1020
1255
  const t = shape.type;
1021
1256
  const n = f.options?.length ?? 0;
1022
- if (l === "segmented" && t === "select") if (n >= 2 && n <= 4) node.ui.control = "segmented";
1023
- else if (n >= 5) {
1024
- node.ui.control = n <= 6 ? "radio" : "dropdown";
1025
- lose(ctx, at, "segmented>4");
1026
- } else lose(ctx, at, "layout");
1027
- else if (l === "slider" && t === "number" && f.type === "number") node.ui.control = "slider";
1257
+ if (l === "segmented" && t === "select") {
1258
+ if (n >= 2 && n <= 4) node.ui.control = "segmented";
1259
+ else if (n >= 5) cat(ctx, at, "segmented fallback");
1260
+ else lose(ctx, at, "layout");
1261
+ } else if (l === "slider" && t === "number" && f.type === "number") node.ui.control = "slider";
1028
1262
  else if (UI_CONTROLS.includes(l) && l !== "segmented" && l !== "slider" && CONTROL_TYPES[l]?.includes(t)) node.ui.control = l;
1029
- else if (l === "list" && (shape.kind === "zones" || shape.kind === "itemType" || shape.kind === "modular" || shape.kind === "refs") && f.arrayLayout === void 0) node.ui.layout = "rows";
1263
+ else if (l === "list" && (shape.kind === "zones" || shape.kind === "itemType" || shape.kind === "modular" || shape.kind === "types" || shape.kind === "refs") && f.arrayLayout === void 0) node.ui.layout = "rows";
1030
1264
  else lose(ctx, at, "layout");
1031
1265
  }
1032
1266
  /** The prod type a V2 field is stored as, for a preview path reading into it (no loss is reported). */
@@ -1061,6 +1295,7 @@ function previewPathToProd(path, children, ctx) {
1061
1295
  node = next;
1062
1296
  continue;
1063
1297
  }
1298
+ if (node.type === "table") return TABLE_PATH_KEYS.includes(segs[i]) && i === segs.length - 1 ? [...out, segs[i]].join(".") : void 0;
1064
1299
  const type = storedType(node, ctx);
1065
1300
  if (!OBJECT_VALUE_TYPES.has(type)) return void 0;
1066
1301
  const key = type === "image" ? IMAGE_PATH_KEY[segs[i]] ?? segs[i] : segs[i];
@@ -1094,10 +1329,71 @@ function previewToProd(pv, children, at, ctx) {
1094
1329
  }
1095
1330
  return out;
1096
1331
  }
1332
+ /**
1333
+ * A V2 dotted document preview path (`slides.0.heading`, `default.heading`) the backend keeps (componentPreviewRefusals
1334
+ * previewPathOfProps): its head a top-level prop, an index segment stepping into an item of a list, the rest a path
1335
+ * previewPathToProd stores unchanged (a renamed key, `image.alt` → `altText`, has no stored form here).
1336
+ */
1337
+ function documentPreviewPathOk(path, keys, fields, ctx) {
1338
+ const segs = path.split(".");
1339
+ if (segs.length < 2 || !keys.has(segs[0])) return false;
1340
+ const head = fields.find((f) => f.name === segs[0]);
1341
+ if (!head) return false;
1342
+ const polyOf = (f) => f.type === "array" && Array.isArray(f.of) && f.of.length > 1 && f.of.every((o) => o.type === "object") ? f.of : void 0;
1343
+ let node = head;
1344
+ const kept = [segs[0]];
1345
+ let poly = false;
1346
+ for (let i = 1; i < segs.length; i++) {
1347
+ const seg = segs[i];
1348
+ if (/^\d+$/.test(seg) && (node.type === "array" || node.type === "multiselect" || node.type === "tags")) continue;
1349
+ const types = polyOf(node);
1350
+ const kids = types ? types.flatMap((o) => o.fields ?? []) : previewKids(node);
1351
+ if (!kids) {
1352
+ kept.push(...segs.slice(i));
1353
+ break;
1354
+ }
1355
+ const next = kids.find((c) => c.name === seg);
1356
+ if (!next) return false;
1357
+ if (types) poly = true;
1358
+ kept.push(seg);
1359
+ node = next;
1360
+ }
1361
+ if (poly) return true;
1362
+ const clean = kept.join(".");
1363
+ return previewPathToProd(clean, fields, ctx) === clean;
1364
+ }
1365
+ /**
1366
+ * A V2 schema's document-level preview → component.preview (BE componentPreviewSchema): `title` / `subtitle` / `media`
1367
+ * kept when they name a top-level prop key or a dotted path into one (else preview_key_unknown; a catalog note: the row
1368
+ * reads the first text),
1369
+ * `fallback` trimmed and at most COMPONENT_PREVIEW_FALLBACK_MAX, `icon` a dock icon.
1370
+ */
1371
+ function componentPreviewToProd(pv, keys, fields, at, ctx) {
1372
+ const out = {};
1373
+ for (const k of Object.keys(pv)) {
1374
+ const v = pv[k];
1375
+ if (v === void 0) continue;
1376
+ if (k === "title" || k === "subtitle" || k === "media") {
1377
+ if ((keys.has(v) || documentPreviewPathOk(v, keys, fields, ctx)) && v.length <= 64) out[k] = v;
1378
+ else cat(ctx, `${at}/${k}`, "document preview path");
1379
+ } else if (k === "fallback") {
1380
+ const t = v.trim();
1381
+ if (t && t === v && t.length <= 120) out.fallback = v;
1382
+ else lose(ctx, `${at}/${k}`, "help>limit");
1383
+ } else if (k === "icon") {
1384
+ const dock = dockIconOf(v);
1385
+ if (dock) out.icon = dock;
1386
+ else lose(ctx, `${at}/${k}`, "preview icon");
1387
+ } else lose(ctx, `${at}/${k}`, "unsupported key");
1388
+ }
1389
+ return out;
1390
+ }
1097
1391
  /** A list item's name and title: the convention, else ui.itemName / ui.itemTitle (chromeUiProblems). */
1098
1392
  function itemIdentity(item, conv, node, ip, ctx) {
1099
- if (item.name !== conv.name) if (ITEM_NAME_RE.test(item.name)) node.ui.itemName = item.name;
1100
- else lose(ctx, `${ip}/name`, "item identity");
1393
+ if (item.name !== conv.name) {
1394
+ if (ITEM_NAME_RE.test(item.name)) node.ui.itemName = item.name;
1395
+ else lose(ctx, `${ip}/name`, "item identity");
1396
+ }
1101
1397
  if (item.title !== conv.title) {
1102
1398
  const t = item.title;
1103
1399
  if (t === t.trim() && t.length >= 1 && t.length <= 60) node.ui.itemTitle = t;
@@ -1111,6 +1407,21 @@ function ofToProd(f, shape, node, at, lv, ctx) {
1111
1407
  shape.items.forEach((item, i) => blockModelFor(item, `${at}/${i}`, ctx, node));
1112
1408
  return;
1113
1409
  }
1410
+ if (shape.kind === "types") return itemTypesToProd(shape.items, node, at, lv, ctx);
1411
+ if (shape.kind === "slot") {
1412
+ const of = f.of;
1413
+ for (const k of Object.keys(of)) {
1414
+ if (of[k] === void 0) continue;
1415
+ if (k === "allow" && ctx.lane === "fields") {
1416
+ node.config.blockSlugs = of.allow.map(kebab);
1417
+ of.allow.forEach((n, i) => camel(kebab(n)) !== n && lose(ctx, `${at}/allow/${i}`, "type name"));
1418
+ } else if (k === "allow") {
1419
+ if (slotAllowOk(of.allow)) node.config.allow = [...of.allow];
1420
+ else cat(ctx, `${at}/allow`, "slot allow");
1421
+ } else if (k !== "type" || of.type !== "block") lose(ctx, `${at}/${k}`, "unsupported key");
1422
+ }
1423
+ return;
1424
+ }
1114
1425
  const item = shape.item;
1115
1426
  const ip = `${at}/0`;
1116
1427
  if (shape.kind === "zones") {
@@ -1134,15 +1445,96 @@ function ofToProd(f, shape, node, at, lv, ctx) {
1134
1445
  }
1135
1446
  if (shape.kind === "itemType") node.config.itemType = shape.itemType;
1136
1447
  itemIdentity(item, conventionItem(f.name, f.title), node, ip, ctx);
1448
+ const settings = {};
1449
+ const scalars = shape.kind === "itemType" && !shape.shopify && (ctx.lane === "props" || MODEL_LIST_KEYS.has("item"));
1137
1450
  for (const k of Object.keys(item)) {
1138
- if (item[k] === void 0) continue;
1451
+ const v = item[k];
1452
+ if (v === void 0) continue;
1139
1453
  const p = `${ip}/${k}`;
1140
1454
  if (k === "name" || k === "title") continue;
1141
1455
  if (k === "type") {
1456
+ if (shape.kind === "itemType" && shape.shopify) continue;
1142
1457
  if (shape.kind === "itemType" && item.type === "text") lose(ctx, p, "item settings");
1143
1458
  } else if (k === "reference" && shape.kind === "refs") referenceToConfig(item.reference, node, p, ctx);
1459
+ else if (k === "reference" && shape.kind === "itemType" && shape.shopify) continue;
1460
+ else if (scalars && k === "description" && typeof v === "string" && v.trim() === v && v.length >= 1 && v.length <= PROP_HELP_MAX) settings.helpText = v;
1461
+ else if (scalars && k === "localized" && typeof v === "boolean") settings.localized = v;
1462
+ else if (scalars && k === "validation") itemValidationToProd(item, shape.itemType, settings, p, ctx);
1144
1463
  else lose(ctx, p, "item settings");
1145
1464
  }
1465
+ if (nonEmpty(settings)) node.config.item = settings;
1466
+ }
1467
+ /** A scalar list item's V2 validation → config.item: required, a text / rich text item's length, a text item's pattern. */
1468
+ function itemValidationToProd(item, itemType, out, at, ctx) {
1469
+ const val = item.validation;
1470
+ const lengthy = itemType === "text" || itemType === "richtext";
1471
+ for (const k of Object.keys(val)) {
1472
+ const v = val[k];
1473
+ if (v === void 0) continue;
1474
+ if (k === "required" && typeof v === "boolean") out.required = v;
1475
+ else if ((k === "min" || k === "max") && lengthy && isWhole(v)) out[k === "min" ? "minLength" : "maxLength"] = v;
1476
+ else if (k === "pattern" && itemType === "text" && typeof v === "string" && v.length <= 200 && compiles(v)) out.pattern = v;
1477
+ else if (k === "patternMessage" && typeof v === "string" && v.trim() !== "" && v.length <= 200) out.patternMessage = v;
1478
+ else lose(ctx, `${at}/${k}`, "item settings");
1479
+ }
1480
+ if (out.minLength !== void 0 && out.maxLength !== void 0 && out.minLength > out.maxLength) {
1481
+ delete out.minLength;
1482
+ delete out.maxLength;
1483
+ lose(ctx, `${at}/min`, "item settings");
1484
+ }
1485
+ if (out.patternMessage !== void 0 && out.pattern === void 0) {
1486
+ delete out.patternMessage;
1487
+ lose(ctx, `${at}/patternMessage`, "item settings");
1488
+ }
1489
+ }
1490
+ /** A slot's per-child settings → config.slotSettings: leaf sub-fields, at most SLOT_SETTINGS_MAX (slotSettingsHardening). */
1491
+ function slotSettingsToProd(settings, node, at, lv, ctx) {
1492
+ const kept = settings.slice(0, SLOT_SETTINGS_MAX);
1493
+ if (settings.length > SLOT_SETTINGS_MAX) settings.slice(SLOT_SETTINGS_MAX).forEach((_, i) => lose(ctx, `${at}/${SLOT_SETTINGS_MAX + i}`, "slotSettings"));
1494
+ node.slotSettings = kept.map((c, i) => convertField(c, `${at}/${i}`, lv + 1, ctx, kept, false, false, false, true));
1495
+ }
1496
+ /**
1497
+ * A polymorphic list's item types → config.itemTypes `{ name, label, fields }`: the name is what a row's `_type` holds,
1498
+ * the title its label, its icon and row preview `icon` / `preview` (paths over its own fields). One prod cannot name is
1499
+ * not stored, which moves the ones after it up a place, so the whole list is named.
1500
+ */
1501
+ function itemTypesToProd(items, node, at, lv, ctx) {
1502
+ const names = /* @__PURE__ */ new Set();
1503
+ node.itemTypes = [];
1504
+ let dropped = false;
1505
+ items.forEach((item, i) => {
1506
+ const ip = `${at}/${i}`;
1507
+ const fields = item.fields ?? [];
1508
+ const why = !ITEM_TYPE_NAME.test(item.name) || !fields.length ? "item identity" : names.has(item.name) ? "duplicate name" : void 0;
1509
+ if (why) {
1510
+ lose(ctx, ip, why);
1511
+ dropped = true;
1512
+ return;
1513
+ }
1514
+ names.add(item.name);
1515
+ const label = trunc(ctx, `${ip}/title`, item.title, ITEM_TYPE_LABEL_MAX, "item identity");
1516
+ const children = fields.map((c, j) => convertField(c, `${ip}/fields/${j}`, lv + 1, ctx, fields, false, false, true));
1517
+ const type = {
1518
+ name: item.name,
1519
+ label,
1520
+ children
1521
+ };
1522
+ for (const k of Object.keys(item)) {
1523
+ if (item[k] === void 0 || k === "name" || k === "title" || k === "type" || k === "fields") continue;
1524
+ const p = `${ip}/${k}`;
1525
+ if (k === "preview") {
1526
+ const preview = previewToProd(item.preview, fields, p, ctx);
1527
+ if (nonEmpty(preview)) type.preview = preview;
1528
+ } else if (k === "icon") {
1529
+ const dock = dockIconOf(item.icon);
1530
+ if (dock) type.icon = dock;
1531
+ else lose(ctx, p, "item icon");
1532
+ } else if (k === "openMode") cat(ctx, p, "item openMode");
1533
+ else lose(ctx, p, "item identity");
1534
+ }
1535
+ node.itemTypes.push(type);
1536
+ });
1537
+ if (dropped && node.itemTypes.length < items.length && node.itemTypes.length) lose(ctx, at, "item identity");
1146
1538
  }
1147
1539
  /**
1148
1540
  * A modular item type → the kind:'block' model its rows are stored against: the item title is the model name, its fields
@@ -1219,16 +1611,28 @@ function blockModelFor(item, at, ctx, list) {
1219
1611
  ctx.catalog.length = startCat;
1220
1612
  lose(ctx, at, "duplicate name");
1221
1613
  }
1614
+ /**
1615
+ * A reference's targets: entry models → contentModelId / polymorphic + allowedModelSlugs; page, form and collection →
1616
+ * config.targets and the filter → config.filter (backend refTargetKeys, both lanes). Read back, entry models come first,
1617
+ * then the targets.
1618
+ */
1222
1619
  function referenceToConfig(ref, node, at, ctx) {
1223
1620
  const to = ref.to ?? [];
1224
1621
  const entries = to.filter(ENTRY_TARGET);
1225
- if (entries.length < to.length) lose(ctx, entries.length || !to.length ? `${at}/to` : at, "reference target");
1622
+ const targets = to.filter((t) => REF_TARGET_KINDS.includes(t));
1623
+ const kept = entries.length + targets.length;
1624
+ if (kept < to.length) lose(ctx, kept || !to.length ? `${at}/to` : at, "shopify reference");
1625
+ else if (to.join() !== [...entries, ...targets].join()) lose(ctx, `${at}/to`, "reference order");
1226
1626
  if (entries.length === 1) node.config.contentModelId = entries[0];
1227
1627
  else if (entries.length > 1) Object.assign(node.config, {
1228
1628
  polymorphic: true,
1229
1629
  allowedModelSlugs: entries
1230
1630
  });
1231
- if (ref.filter !== void 0) lose(ctx, `${at}/filter`, "reference filter");
1631
+ if (targets.length) node.config.targets = targets;
1632
+ if (ref.filter !== void 0) {
1633
+ if (filterKept(ref.filter)) node.config.filter = ref.filter;
1634
+ else lose(ctx, `${at}/filter`, "reference filter");
1635
+ }
1232
1636
  for (const k of Object.keys(ref)) if (k !== "to" && k !== "filter") lose(ctx, `${at}/${k}`, "unsupported key");
1233
1637
  }
1234
1638
  function numericToProd(f, node, at, ctx) {
@@ -1236,7 +1640,7 @@ function numericToProd(f, node, at, ctx) {
1236
1640
  const n = f.numeric;
1237
1641
  const lo = n.min ?? 0;
1238
1642
  const hi = n.max ?? 5;
1239
- const st = n.step ?? 1;
1643
+ const st = n.step ?? (f.type === "rating" ? V2_RATING_STEP : 1);
1240
1644
  const stops = (hi - lo) / st;
1241
1645
  const ratingOk = (n.min === void 0 || Number.isFinite(n.min) && n.min >= 0) && (n.max === void 0 || isWhole(n.max, 1, 10)) && (n.step === void 0 || Number.isFinite(n.step) && n.step > 0) && lo < hi && Math.abs(stops - Math.round(stops)) < 1e-9 && Math.round(stops) <= 100;
1242
1646
  for (const k of Object.keys(n)) {
@@ -1247,13 +1651,14 @@ function numericToProd(f, node, at, ctx) {
1247
1651
  const u = v;
1248
1652
  if (f.type === "number" && typeof u === "string" && u.trim() !== "" && u.length <= 24) node.config.unit = u;
1249
1653
  else lose(ctx, p, "numeric unit");
1250
- } else if (k === "min" || k === "max" || k === "step") if (f.type === "number") node.config[k] = v;
1251
- else if (ratingOk) node.config[k] = v;
1252
- else lose(ctx, p, "rating bounds");
1253
- else lose(ctx, p, "unsupported key");
1654
+ } else if (k === "min" || k === "max" || k === "step") {
1655
+ if (f.type === "number") node.config[k] = v;
1656
+ else if (ratingOk) node.config[k] = v;
1657
+ else lose(ctx, p, "rating bounds");
1658
+ } else lose(ctx, p, "unsupported key");
1254
1659
  }
1255
1660
  }
1256
- var SCALE_UNIT_SET = new Set([
1661
+ var SCALE_UNIT_SET = /* @__PURE__ */ new Set([
1257
1662
  "px",
1258
1663
  "%",
1259
1664
  "rem",
@@ -1298,13 +1703,16 @@ function scaleToConfig(s, node, at, ctx) {
1298
1703
  const v = s[k];
1299
1704
  if (v === void 0 || k === "steps") continue;
1300
1705
  const p = `${at}/${k}`;
1301
- if (k === "units") if (unitsOk(v)) out.units = [...v];
1302
- else lose(ctx, p, "scale rules");
1303
- else if (k === "defaultUnit") if (SCALE_UNIT_SET.has(v)) out.defaultUnit = s.defaultUnit;
1304
- else lose(ctx, p, "scale rules");
1305
- else if (k === "allowCustom") if (customOk) out.allowCustom = typeof c === "boolean" ? c : JSON.parse(JSON.stringify(c));
1306
- else lose(ctx, p, "scale rules");
1307
- else if (k === "presets") {
1706
+ if (k === "units") {
1707
+ if (unitsOk(v)) out.units = [...v];
1708
+ else lose(ctx, p, "scale rules");
1709
+ } else if (k === "defaultUnit") {
1710
+ if (SCALE_UNIT_SET.has(v)) out.defaultUnit = s.defaultUnit;
1711
+ else lose(ctx, p, "scale rules");
1712
+ } else if (k === "allowCustom") {
1713
+ if (customOk) out.allowCustom = typeof c === "boolean" ? c : JSON.parse(JSON.stringify(c));
1714
+ else lose(ctx, p, "scale rules");
1715
+ } else if (k === "presets") {
1308
1716
  const presets = s.presets;
1309
1717
  if (presets.length <= 20 && presets.every((x) => {
1310
1718
  if (x.label !== void 0 && !textOk(x.label, 24)) return false;
@@ -1322,45 +1730,102 @@ function scaleToConfig(s, node, at, ctx) {
1322
1730
  }
1323
1731
  node.config.scale = out;
1324
1732
  }
1733
+ /** V2 operator → prod showIf op (the legacy `equals` / `notEmpty` forms read as is / is one of / has a value). */
1734
+ var SHOW_IF_OP = {
1735
+ is: "eq",
1736
+ isNot: "neq",
1737
+ in: "in",
1738
+ notIn: "notIn",
1739
+ empty: "empty",
1740
+ notEmpty: "notEmpty",
1741
+ gt: "gt",
1742
+ lt: "lt"
1743
+ };
1744
+ var VALUELESS = /* @__PURE__ */ new Set([
1745
+ "empty",
1746
+ "notEmpty",
1747
+ "isSet"
1748
+ ]);
1325
1749
  function conditionToShowIf(h, at, siblings, ctx, variantOk) {
1326
- const variant = variantOk && h.field === "_variant";
1327
- if (!h.field || !variant && !siblings.some((s) => s.name === h.field)) return void lose(ctx, at, "condition");
1328
- if (Object.keys(h).filter((k) => ![
1750
+ const rules = [];
1751
+ for (const [i, r] of [h, ...h.also ?? []].entries()) {
1752
+ const rule = conditionRule(r, i === 0, siblings, variantOk);
1753
+ if (!rule) return void lose(ctx, at, "condition");
1754
+ rules.push(rule);
1755
+ }
1756
+ if (h.match !== void 0 && h.match !== "all" && h.match !== "any") return void lose(ctx, at, "condition");
1757
+ return {
1758
+ match: h.match ?? "all",
1759
+ rules
1760
+ };
1761
+ }
1762
+ /** One V2 rule as a prod rule, or null when prod cannot hold it (a scope, a non-sibling, a value showIf cannot test). */
1763
+ function conditionRule(r, head, siblings, variantOk) {
1764
+ const variant = variantOk && r.field === "_variant";
1765
+ if (!r.field || r.scope !== void 0 || !variant && !siblings.some((s) => s.name === r.field)) return null;
1766
+ const allowed = head ? [
1329
1767
  "field",
1330
1768
  "equals",
1331
- "notEmpty"
1332
- ].includes(k) && h[k] !== void 0).length || h.equals !== void 0 === (h.notEmpty !== void 0)) return void lose(ctx, at, "condition");
1333
- if (h.notEmpty !== void 0) return h.notEmpty === true ? {
1334
- match: "all",
1335
- rules: [{
1336
- field: h.field,
1337
- op: "notEmpty"
1338
- }]
1339
- } : void lose(ctx, at, "condition");
1340
- const sibType = variant ? void 0 : siblings.find((s) => s.name === h.field)?.type;
1341
- const list = Array.isArray(h.equals) ? h.equals : [h.equals];
1769
+ "notEmpty",
1770
+ "op",
1771
+ "value",
1772
+ "also",
1773
+ "match"
1774
+ ] : [
1775
+ "field",
1776
+ "equals",
1777
+ "notEmpty",
1778
+ "op",
1779
+ "value"
1780
+ ];
1781
+ if (Object.keys(r).some((k) => !allowed.includes(k) && r[k] !== void 0)) return null;
1782
+ if ([
1783
+ r.op !== void 0,
1784
+ r.equals !== void 0,
1785
+ r.notEmpty !== void 0
1786
+ ].filter(Boolean).length !== 1) return null;
1787
+ if (r.notEmpty !== void 0) return r.notEmpty === true ? {
1788
+ field: r.field,
1789
+ op: "notEmpty"
1790
+ } : null;
1791
+ const target = r.op !== void 0 ? r.value : r.equals;
1792
+ const op = r.op !== void 0 ? SHOW_IF_OP[r.op] : Array.isArray(target) ? "in" : "eq";
1793
+ if (!op) return null;
1794
+ if (VALUELESS.has(op)) return r.value === void 0 ? {
1795
+ field: r.field,
1796
+ op
1797
+ } : null;
1798
+ const sibType = variant ? void 0 : siblings.find((s) => s.name === r.field)?.type;
1799
+ if (op === "gt" || op === "lt") return typeof target === "number" && Number.isFinite(target) ? {
1800
+ field: r.field,
1801
+ op,
1802
+ value: String(target)
1803
+ } : null;
1804
+ const list = Array.isArray(target) ? target : [target];
1342
1805
  const strs = [];
1343
1806
  for (const v of list) {
1344
1807
  if (![
1345
1808
  "string",
1346
1809
  "number",
1347
1810
  "boolean"
1348
- ].includes(typeof v)) return void lose(ctx, at, "condition");
1811
+ ].includes(typeof v)) return null;
1349
1812
  const s = String(v);
1350
- if (coerceRuleValue(sibType, s) !== v) return void lose(ctx, at, "condition");
1813
+ if (coerceRuleValue(sibType, s) !== v) return null;
1351
1814
  strs.push(s);
1352
1815
  }
1353
- return {
1354
- match: "all",
1355
- rules: [Array.isArray(h.equals) ? {
1356
- field: h.field,
1357
- op: "in",
1358
- value: strs
1359
- } : {
1360
- field: h.field,
1361
- op: "eq",
1362
- value: strs[0]
1363
- }]
1816
+ if (op === "in" || op === "notIn") return {
1817
+ field: r.field,
1818
+ op,
1819
+ value: strs
1820
+ };
1821
+ return Array.isArray(target) ? strs.length ? {
1822
+ field: r.field,
1823
+ op: op === "eq" ? "in" : "notIn",
1824
+ value: strs
1825
+ } : null : {
1826
+ field: r.field,
1827
+ op,
1828
+ value: strs[0]
1364
1829
  };
1365
1830
  }
1366
1831
  var nonEmpty = (o) => Object.keys(o).length ? o : void 0;
@@ -1379,7 +1844,8 @@ function common(n) {
1379
1844
  "advanced",
1380
1845
  "recommendedMax",
1381
1846
  "openMode",
1382
- "localized"
1847
+ "localized",
1848
+ "archived"
1383
1849
  ]) if (n[k] !== void 0) out[k] = n[k];
1384
1850
  return out;
1385
1851
  }
@@ -1394,6 +1860,7 @@ function emitField(n) {
1394
1860
  if (n.type === "select" && n.options) out.options = n.options;
1395
1861
  if ((n.type === "multiselect" || n.type === "tags") && n.options) config.options = n.options;
1396
1862
  if (n.type === "object") out.fields = (n.children ?? []).map(emitField);
1863
+ if (n.slotSettings) config.slotSettings = n.slotSettings.map(emitField);
1397
1864
  if (n.zoned) {
1398
1865
  const { minItems, maxItems, ...rest } = config;
1399
1866
  out.config = {
@@ -1418,6 +1885,8 @@ function emitProp(n) {
1418
1885
  const config = { ...n.config };
1419
1886
  if (n.options) config.options = n.options;
1420
1887
  if (n.children) config.fields = n.children.map(emitSubField);
1888
+ if (n.itemTypes) config.itemTypes = emitItemTypes(n.itemTypes);
1889
+ if (n.slotSettings) config.slotSettings = n.slotSettings.map(emitSubField);
1421
1890
  if (nonEmpty(config)) out.config = config;
1422
1891
  const ui = nonEmpty(n.ui);
1423
1892
  if (ui) out.ui = ui;
@@ -1431,11 +1900,22 @@ function emitSubField(n) {
1431
1900
  if (n.placeholder !== void 0) out.placeholder = n.placeholder;
1432
1901
  if (n.options) out.options = n.options;
1433
1902
  if (n.children) out.fields = n.children.map(emitSubField);
1434
- if (nonEmpty(n.config)) out.config = { ...n.config };
1903
+ const config = { ...n.config };
1904
+ if (n.itemTypes) config.itemTypes = emitItemTypes(n.itemTypes);
1905
+ if (n.slotSettings) config.slotSettings = n.slotSettings.map(emitSubField);
1906
+ if (nonEmpty(config)) out.config = config;
1435
1907
  const ui = nonEmpty(n.ui);
1436
1908
  if (ui) out.ui = ui;
1437
1909
  return out;
1438
1910
  }
1911
+ /** A polymorphic list's item types as the backend stores them: `{ name, label, fields }` (typesTableHardening). */
1912
+ var emitItemTypes = (types) => types.map((t) => ({
1913
+ name: t.name,
1914
+ label: t.label,
1915
+ fields: t.children.map(emitSubField),
1916
+ ...t.icon ? { icon: t.icon } : {},
1917
+ ...t.preview ? { preview: t.preview } : {}
1918
+ }));
1439
1919
  function fromTypeSchema(v2, target) {
1440
1920
  const ctx = {
1441
1921
  lane: target,
@@ -1448,6 +1928,10 @@ function fromTypeSchema(v2, target) {
1448
1928
  definedGroups: /* @__PURE__ */ new WeakMap(),
1449
1929
  propOrder: /* @__PURE__ */ new Map(),
1450
1930
  groupOrder: false,
1931
+ propTabs: /* @__PURE__ */ new Map(),
1932
+ definedTabs: /* @__PURE__ */ new Set(),
1933
+ keptTabs: /* @__PURE__ */ new Set(),
1934
+ declaredTabs: [],
1451
1935
  blockModels: /* @__PURE__ */ new Map()
1452
1936
  };
1453
1937
  const props = target === "props";
@@ -1471,10 +1955,10 @@ function fromTypeSchema(v2, target) {
1471
1955
  else if (!props && v2.kind !== "entry") cat(ctx, p, v2.kind === "global" ? "kind:global" : v2.kind === "page" ? "kind:page" : v2.kind === "block" ? "kind:block (fields)" : "kind:object");
1472
1956
  break;
1473
1957
  case "icon":
1474
- cat(ctx, p, "block icon");
1958
+ if (!props || !dockIconOf(v2.icon)) cat(ctx, p, "block icon");
1475
1959
  break;
1476
1960
  case "preview":
1477
- cat(ctx, p, "document preview");
1961
+ if (!props) cat(ctx, p, "document preview");
1478
1962
  break;
1479
1963
  case "variants":
1480
1964
  cat(ctx, p, "variants");
@@ -1492,7 +1976,7 @@ function fromTypeSchema(v2, target) {
1492
1976
  }
1493
1977
  }
1494
1978
  if (props) {
1495
- if (v2.groups?.length) lose(ctx, "/groups", "group (props)");
1979
+ propTabsFor(v2, ctx);
1496
1980
  propSetsFor(v2, ctx);
1497
1981
  } else fieldsetsFor(v2, ctx, fieldsets);
1498
1982
  const nodes = v2.fields.map((f, i) => convertField(f, `/fields/${i}`, 1, ctx, v2.fields, true));
@@ -1504,6 +1988,13 @@ function fromTypeSchema(v2, target) {
1504
1988
  };
1505
1989
  if (v2.description !== void 0) component.description = v2.description;
1506
1990
  if (category) component.category = category;
1991
+ const icon = v2.icon === void 0 ? void 0 : dockIconOf(v2.icon);
1992
+ if (icon) component.icon = icon;
1993
+ if (v2.preview !== void 0) {
1994
+ const preview = componentPreviewToProd(v2.preview, new Set(component.props.map((x) => x.key)), v2.fields, "/preview", ctx);
1995
+ if (Object.keys(preview).length) component.preview = preview;
1996
+ }
1997
+ if (ctx.declaredTabs.length) component.tabs = ctx.declaredTabs.map((t) => ({ ...t }));
1507
1998
  return {
1508
1999
  schema: component,
1509
2000
  blockModels: [],
@@ -1533,7 +2024,7 @@ function fieldsetsFor(v2, ctx, out) {
1533
2024
  let shifted = false;
1534
2025
  (v2.groups ?? []).forEach((g, i) => {
1535
2026
  const p = `/groups/${i}`;
1536
- const stored = g.title.slice(0, 20);
2027
+ const stored = g.title.slice(0, 40);
1537
2028
  const folded = stored.trim().toLowerCase();
1538
2029
  const drop = (why) => {
1539
2030
  if (!ctx.tabs.has(g.name) && !ctx.droppedTabs.has(g.name)) ctx.droppedTabs.set(g.name, why);
@@ -1541,14 +2032,14 @@ function fieldsetsFor(v2, ctx, out) {
1541
2032
  lose(ctx, p, why);
1542
2033
  };
1543
2034
  if (ctx.tabs.has(g.name) || names.has(folded)) return drop("group name collision");
1544
- if (out.length >= 3) return drop("groups>3");
2035
+ if (out.length >= TAB_MAX$1) return drop("groups>3");
1545
2036
  names.add(folded);
1546
2037
  const tab = {
1547
2038
  id: g.name,
1548
2039
  name: stored,
1549
2040
  kind: "tab"
1550
2041
  };
1551
- if (g.title.length > 20) lose(ctx, `${p}/title`, "group name>20");
2042
+ if (g.title.length > 40) lose(ctx, `${p}/title`, "group name>40");
1552
2043
  if (g.icon !== void 0) {
1553
2044
  const dock = dockIconOf(g.icon);
1554
2045
  if (dock) tab.icon = dock;
@@ -1585,9 +2076,10 @@ function fieldsetsFor(v2, ctx, out) {
1585
2076
  for (const k of Object.keys(fs)) {
1586
2077
  const v = fs[k];
1587
2078
  if (v === void 0 || k === "name" || k === "title") continue;
1588
- if (k === "description") if (v.length <= 500) set.description = v;
1589
- else lose(ctx, `${p}/${k}`, "help>limit");
1590
- else if (k === "collapsed" || k === "collapsible" || k === "columns" || k === "layout") set[k] = v;
2079
+ if (k === "description") {
2080
+ if (v.length <= FIELDSET_DESCRIPTION_MAX) set.description = v;
2081
+ else lose(ctx, `${p}/${k}`, "help>limit");
2082
+ } else if (k === "collapsed" || k === "collapsible" || k === "columns" || k === "layout") set[k] = v;
1591
2083
  else lose(ctx, `${p}/${k}`, "unsupported key");
1592
2084
  }
1593
2085
  ctx.sets.set(fs.name, tab);
@@ -1595,25 +2087,146 @@ function fieldsetsFor(v2, ctx, out) {
1595
2087
  });
1596
2088
  }
1597
2089
  /**
2090
+ * Props lane: V2 groups → each top-level prop's `ui.tab`, defined on the first prop in it (label, icon, and `order` when
2091
+ * the V2 order differs from where each tab is first used). A field with no group sits in the V2 default group; a group
2092
+ * not stored puts its fields in the default tab; a fieldset's top-level members share the tab most of them sit in
2093
+ * (prop_group_split_tabs). No groups → no tab anywhere, so a component without tabs stores as before.
2094
+ */
2095
+ function propTabsFor(v2, ctx) {
2096
+ const groups = v2.groups ?? [];
2097
+ const kept = [];
2098
+ const dropped = /* @__PURE__ */ new Map();
2099
+ const labels = /* @__PURE__ */ new Set();
2100
+ groups.forEach((g, i) => {
2101
+ const p = `/groups/${i}`;
2102
+ const drop = (why) => {
2103
+ if (!kept.some((k) => k.g.name === g.name) && !dropped.has(g.name)) dropped.set(g.name, why);
2104
+ lose(ctx, p, why);
2105
+ };
2106
+ const label = g.title.slice(0, 40).trim();
2107
+ const folded = label.toLowerCase();
2108
+ if (kept.some((k) => k.g.name === g.name) || labels.has(folded)) return drop("group name collision");
2109
+ if (kept.length >= PROP_TAB_MAX || g.name.length < 1 || g.name.length > 40) return drop("prop tab limit");
2110
+ if (!label) return drop("group title");
2111
+ labels.add(folded);
2112
+ const tab = {
2113
+ id: g.name,
2114
+ label
2115
+ };
2116
+ if (label !== g.title.trim()) lose(ctx, `${p}/title`, "group name>40");
2117
+ else if (label !== g.title) lose(ctx, `${p}/title`, "group title");
2118
+ if (g.icon !== void 0) {
2119
+ const dock = dockIconOf(g.icon);
2120
+ if (dock) tab.icon = dock;
2121
+ else lose(ctx, `${p}/icon`, "group icon");
2122
+ }
2123
+ if (i === 0 ? g.default !== true : g.default !== void 0) lose(ctx, `${p}/default`, "group default");
2124
+ for (const k of Object.keys(g)) if (![
2125
+ "name",
2126
+ "title",
2127
+ "icon",
2128
+ "default"
2129
+ ].includes(k) && g[k] !== void 0) lose(ctx, `${p}/${k}`, "unsupported key");
2130
+ kept.push({
2131
+ g,
2132
+ i,
2133
+ tab
2134
+ });
2135
+ });
2136
+ const isKept = (name) => name !== void 0 && kept.some((k) => k.g.name === name);
2137
+ const v2Default = (groups.find((g) => g.default) ?? groups[0])?.name;
2138
+ const fallback = isKept(v2Default) ? v2Default : kept[0]?.g.name;
2139
+ const place = /* @__PURE__ */ new Map();
2140
+ for (const f of v2.fields) {
2141
+ const want = f.group ?? v2Default;
2142
+ if (isKept(want)) place.set(f, { tab: want });
2143
+ else if (f.group !== void 0 || want !== void 0) place.set(f, {
2144
+ tab: fallback,
2145
+ loss: dropped.get(want) ?? "unsupported key"
2146
+ });
2147
+ }
2148
+ if (!kept.length) {
2149
+ for (const [f, t] of place) if (f.group !== void 0) ctx.propTabs.set(f, { loss: t.loss });
2150
+ return;
2151
+ }
2152
+ const tally = /* @__PURE__ */ new Map();
2153
+ for (const f of v2.fields) {
2154
+ if (f.fieldset === void 0) continue;
2155
+ const t = tally.get(f.fieldset) ?? /* @__PURE__ */ new Map();
2156
+ t.set(place.get(f)?.tab, (t.get(place.get(f)?.tab) ?? 0) + 1);
2157
+ tally.set(f.fieldset, t);
2158
+ }
2159
+ for (const f of v2.fields) {
2160
+ const counts = f.fieldset !== void 0 ? tally.get(f.fieldset) : void 0;
2161
+ if (!counts) continue;
2162
+ const best = [...counts.entries()].sort((a, b) => b[1] - a[1])[0][0];
2163
+ const cur = place.get(f);
2164
+ if (cur?.tab !== best) place.set(f, {
2165
+ tab: best,
2166
+ loss: cur?.loss ?? "fieldset spans groups"
2167
+ });
2168
+ }
2169
+ const firstUse = [];
2170
+ for (const f of v2.fields) {
2171
+ const t = place.get(f)?.tab;
2172
+ if (t !== void 0 && !firstUse.includes(t)) firstUse.push(t);
2173
+ }
2174
+ const used = kept.filter((k) => firstUse.includes(k.g.name));
2175
+ const unused = kept.filter((k) => !firstUse.includes(k.g.name));
2176
+ for (const k of kept) ctx.keptTabs.add(k.g.name);
2177
+ const firstGap = groups.findIndex((g) => !kept.some((k) => k.g === g));
2178
+ if (firstGap >= 0 && kept.some((k) => k.i > firstGap)) {
2179
+ const g = groups[firstGap];
2180
+ lose(ctx, "/groups", dropped.get(g.name) ?? "unsupported key");
2181
+ }
2182
+ const ordered = unused.length > 0 || used.some((k, i) => k.g.name !== firstUse[i]);
2183
+ kept.forEach((k, i) => {
2184
+ if (ordered) k.tab.order = Math.min(99, i);
2185
+ });
2186
+ ctx.declaredTabs = unused.map((k) => k.tab);
2187
+ for (const [f, t] of place) {
2188
+ const k = used.find((u) => u.g.name === t.tab);
2189
+ if (k) ctx.propTabs.set(f, {
2190
+ tab: k.tab,
2191
+ loss: t.loss
2192
+ });
2193
+ else if (t.loss) ctx.propTabs.set(f, { loss: t.loss });
2194
+ }
2195
+ }
2196
+ /**
1598
2197
  * Props lane: a V2 fieldset is an inline ui.group, defined on the first prop (at each level, sub-fields too) that uses
1599
2198
  * it. A fieldset no prop uses has nothing to be defined on; when the V2 order differs from where each group is first
1600
2199
  * met (depth first, a prop before its sub-fields: how propsToTypeSchema meets them), every group carries its `order`.
1601
2200
  */
1602
2201
  function propSetsFor(v2, ctx) {
1603
2202
  const sets = v2.fieldsets ?? [];
1604
- if (!sets.length) return;
1605
2203
  const named = new Set(sets.map((s) => s.name));
1606
2204
  const firstSeen = [];
2205
+ const undeclared = [];
1607
2206
  const visit = (fields, lv) => {
1608
2207
  for (const f of fields) {
1609
- if (f.fieldset !== void 0 && named.has(f.fieldset) && !firstSeen.includes(f.fieldset)) firstSeen.push(f.fieldset);
1610
- if (lv >= 10) continue;
2208
+ if (f.fieldset !== void 0 && !firstSeen.includes(f.fieldset)) {
2209
+ firstSeen.push(f.fieldset);
2210
+ if (!named.has(f.fieldset)) undeclared.push(f.fieldset);
2211
+ }
2212
+ if (lv >= MAX_FIELD_DEPTH) continue;
1611
2213
  const kids = previewKids(f);
1612
2214
  if (kids) visit(kids, lv + 1);
1613
2215
  }
1614
2216
  };
1615
2217
  visit(v2.fields, 1);
1616
- ctx.groupOrder = sets.filter((s) => firstSeen.includes(s.name)).some((s, i) => s.name !== firstSeen[i]);
2218
+ const derived = undeclared.map((name) => ({
2219
+ name,
2220
+ title: name
2221
+ }));
2222
+ const declaredUsed = sets.filter((s) => firstSeen.includes(s.name)).length;
2223
+ derived.forEach((s, j) => {
2224
+ ctx.propSets.set(s.name, s);
2225
+ ctx.propOrder.set(s.name, sets.length + j);
2226
+ cat(ctx, `/fieldsets/${declaredUsed + j}`, "fieldset (derived)");
2227
+ });
2228
+ if (!sets.length && !derived.length) return;
2229
+ ctx.groupOrder = [...sets, ...derived].filter((s) => firstSeen.includes(s.name)).some((s, i) => s.name !== firstSeen[i]);
1617
2230
  let gap = false;
1618
2231
  sets.forEach((s, i) => {
1619
2232
  ctx.propSets.set(s.name, s);
@@ -1654,6 +2267,10 @@ function readField(f) {
1654
2267
  delete cfg.options;
1655
2268
  }
1656
2269
  n.fields = f.fields?.map(readField);
2270
+ if (f.type === "array" && !cfg.zones && Array.isArray(cfg.fields)) {
2271
+ n.fields = [...n.fields ?? [], ...cfg.fields.map(readField)];
2272
+ delete cfg.fields;
2273
+ }
1657
2274
  if (cfg.zones) {
1658
2275
  const z = cfg.zones;
1659
2276
  n.zones = {
@@ -1717,12 +2334,32 @@ function scaleFromConfig(s, at, ctx) {
1717
2334
  }));
1718
2335
  return out;
1719
2336
  }
2337
+ /**
2338
+ * Slot children with each `_variant` written as the family member that places it (`_type`): the variant the child's
2339
+ * component already is drops, another member swaps in. A variant no member places stays, and the write names it lost.
2340
+ */
2341
+ function childVariantsToTypes(v, family) {
2342
+ if (!Array.isArray(v)) return v;
2343
+ return v.map((c) => {
2344
+ if (!isObj(c) || typeof c._variant !== "string") return c;
2345
+ const own = family(typeof c._type === "string" ? c._type : "");
2346
+ const { _variant: _gone, ...rest } = c;
2347
+ if (own?.current === c._variant) return rest;
2348
+ const member = own?.member(c._variant);
2349
+ if (!member) return c;
2350
+ return {
2351
+ ...rest,
2352
+ _type: member
2353
+ };
2354
+ });
2355
+ }
1720
2356
  /** The prod children a preview path segment may name below a node (an object's, a list's rows'), or null for a leaf. */
1721
2357
  var readKids = (n) => n.zones ? [...n.zones.nonRepeatable ?? [], ...n.zones.repeatable?.fields ?? []] : n.fields && [
1722
2358
  "object",
1723
2359
  "group",
1724
2360
  "repeater",
1725
- "table"
2361
+ "table",
2362
+ "array"
1726
2363
  ].includes(n.type) ? n.fields : null;
1727
2364
  /** A stored preview path → the V2 path: `image.altText` reads as `image.alt`, the rest as it is. */
1728
2365
  function previewPathFromProd(path, children) {
@@ -1753,12 +2390,26 @@ function previewFromProd(pv, children) {
1753
2390
  return out;
1754
2391
  }
1755
2392
  function showIfToCondition(s, siblings, at, ctx) {
1756
- const drop = (why) => void ctx.onLoss(at, "condition", why);
1757
- if (s.rules.length !== 1) return drop("V2 conditions test one sibling; a multi-rule showIf is dropped (the field always shows).");
1758
- const r = s.rules[0];
2393
+ const conds = [];
2394
+ for (const r of s.rules) {
2395
+ const c = showIfRuleToCondition(r, siblings);
2396
+ if (!c) return void ctx.onLoss(at, "condition", `V2 conditions have no "${r.op}" with this value; the field always shows.`);
2397
+ conds.push(c);
2398
+ }
2399
+ if (!conds.length) return void 0;
2400
+ const [head, ...also] = conds;
2401
+ return {
2402
+ ...head,
2403
+ ...also.length ? { also } : {},
2404
+ ...s.match === "any" && conds.length > 1 ? { match: "any" } : {}
2405
+ };
2406
+ }
2407
+ /** One prod rule as a V2 rule: eq / in / isSet keep V2's legacy forms, the rest name their `op`. */
2408
+ function showIfRuleToCondition(r, siblings) {
1759
2409
  const sib = siblings.find((x) => x.key === r.field);
1760
2410
  const t = sib?.type === "number" ? "number" : sib?.type;
1761
2411
  const read = (v) => coerceRuleValue(t, v);
2412
+ const list = (Array.isArray(r.value) ? r.value : [r.value ?? ""]).map(read);
1762
2413
  switch (r.op) {
1763
2414
  case "eq": return Array.isArray(r.value) ? {
1764
2415
  field: r.field,
@@ -1769,14 +2420,37 @@ function showIfToCondition(s, siblings, at, ctx) {
1769
2420
  };
1770
2421
  case "in": return {
1771
2422
  field: r.field,
1772
- equals: (Array.isArray(r.value) ? r.value : [r.value ?? ""]).map(read)
2423
+ equals: list
1773
2424
  };
1774
2425
  case "isSet":
1775
2426
  case "notEmpty": return {
1776
2427
  field: r.field,
1777
2428
  notEmpty: true
1778
2429
  };
1779
- default: return drop(`V2 conditions have no "${r.op}"; the field always shows.`);
2430
+ case "empty": return {
2431
+ field: r.field,
2432
+ op: "empty"
2433
+ };
2434
+ case "neq": return {
2435
+ field: r.field,
2436
+ op: "isNot",
2437
+ value: Array.isArray(r.value) ? list : read(r.value ?? "")
2438
+ };
2439
+ case "notIn": return {
2440
+ field: r.field,
2441
+ op: "notIn",
2442
+ value: list
2443
+ };
2444
+ case "gt":
2445
+ case "lt": {
2446
+ const n = Number(r.value);
2447
+ return typeof r.value === "string" && r.value.trim() !== "" && Number.isFinite(n) ? {
2448
+ field: r.field,
2449
+ op: r.op,
2450
+ value: n
2451
+ } : void 0;
2452
+ }
2453
+ default: return;
1780
2454
  }
1781
2455
  }
1782
2456
  function v2Type(n, at, ctx) {
@@ -1811,9 +2485,14 @@ function v2Type(n, at, ctx) {
1811
2485
  }
1812
2486
  }
1813
2487
  function readToV2(n, at, siblings, ctx, top, lane) {
1814
- /** a data-table prop (config.mode "data") holds one TableValue: the V2 `table` field, not a list of rows */
1815
- const dataTable = lane === "props" && isDataTableProp(n);
1816
- const type = dataTable ? "table" : v2Type(n, at, ctx);
2488
+ /** a data table holds one TableValue: the V2 `table` field, not a list of rows. A prop says so (config.mode "data");
2489
+ * a field's table is always one (BE tableFieldSchema: `mode` is optional there and can only be "data") */
2490
+ const dataTable = isDataTableProp(n) || lane === "fields" && n.type === "table";
2491
+ /** a slot prop: component instances, read as the V2 slot (its children `{ _key, _type, _settings?, ...props }`) */
2492
+ const slotProp = lane === "props" && n.type === "slot";
2493
+ /** a model slot field: inline children of the kind:'block' models config.blockSlugs names */
2494
+ const blockSlot = lane === "fields" && n.type === "slot";
2495
+ const type = dataTable ? "table" : slotProp || blockSlot ? "slot" : v2Type(n, at, ctx);
1817
2496
  const f = {
1818
2497
  name: n.key,
1819
2498
  title: n.label,
@@ -1822,7 +2501,7 @@ function readToV2(n, at, siblings, ctx, top, lane) {
1822
2501
  const cfg = n.config;
1823
2502
  const val = {};
1824
2503
  const loss = (k, feature, reason) => ctx.onLoss(`${at}/${k}`, feature, reason);
1825
- if (n.helpText !== void 0) f.description = n.helpText;
2504
+ if (typeof n.helpText === "string") f.description = n.helpText;
1826
2505
  if (n.helpMore !== void 0) f.helpMore = n.helpMore;
1827
2506
  if (n.placeholder !== void 0) f.placeholder = n.placeholder;
1828
2507
  if (n.required !== void 0) val.required = n.required;
@@ -1831,9 +2510,18 @@ function readToV2(n, at, siblings, ctx, top, lane) {
1831
2510
  if (n.advanced !== void 0) f.advanced = n.advanced;
1832
2511
  if (n.openMode !== void 0) f.openMode = n.openMode;
1833
2512
  if (n.readOnly === true) f.readOnly = true;
2513
+ if (n.archived === true) f.archived = true;
1834
2514
  if (n.ui.access) f.access = { ...n.ui.access };
1835
2515
  else if (n.readOnly === "developer") f.access = { edit: "schema.manage" };
1836
2516
  if (n.lockReason !== void 0) f.lockReason = n.lockReason;
2517
+ const shopify = n.readOnly === true && n.lockReason === "A Shopify product id. No Shopify integration backs a reference in BetterCMS yet, so the id is kept as it is." && (n.type === "text" || n.type === "array" && n.config.itemType === "text");
2518
+ if (shopify) {
2519
+ delete f.lockReason;
2520
+ if (n.type === "text") {
2521
+ f.type = "reference";
2522
+ f.reference = { to: ["shopify"] };
2523
+ }
2524
+ }
1837
2525
  if (n.localized !== void 0) f.localized = n.localized;
1838
2526
  if (n.ui.hidden !== void 0) f.hidden = n.ui.hidden;
1839
2527
  if (n.ui.rows !== void 0) f.rows = n.ui.rows;
@@ -1850,26 +2538,28 @@ function readToV2(n, at, siblings, ctx, top, lane) {
1850
2538
  if (n.ui.input !== void 0) loss("ui/input", "ui.input", "Custom dock inputs are a dock feature V2 has no key for.");
1851
2539
  if (n.fieldsetId !== void 0) {
1852
2540
  const set = ctx.sets.get(n.fieldsetId);
1853
- if (set?.kind === "tab") if (top) f.group = set.id;
1854
- else loss("fieldsetId", "fieldset", "Only top-level V2 fields sit in a tab; this nested field's tab is dropped.");
1855
- else if (set) {
2541
+ if (set?.kind === "tab") {
2542
+ if (top) f.group = set.id;
2543
+ else loss("fieldsetId", "fieldset", "Only top-level V2 fields sit in a tab; this nested field's tab is dropped.");
2544
+ } else if (set) {
1856
2545
  f.fieldset = set.id;
1857
2546
  if (top && set.tab !== void 0) f.group = set.tab;
1858
2547
  } else loss("fieldsetId", "fieldset", `No fieldset "${n.fieldsetId}" exists on this model; the membership is dropped.`);
1859
2548
  }
1860
2549
  if (lane === "props" && n.ui.group) f.fieldset = n.ui.group.id;
2550
+ if (lane === "props" && n.ui.tab) f.group = n.ui.tab.id;
1861
2551
  for (const k of [
1862
2552
  "target",
1863
2553
  "stale",
1864
2554
  "origin"
1865
2555
  ]) if (n[k] !== void 0) loss(k, k, k === "target" ? "A prop's target (where it writes in the component) is binding, not schema: V2 has no key for it, so the host keeps it." : `V2 has no "${k}" key.`);
1866
- const used = new Set([
2556
+ const used = /* @__PURE__ */ new Set([
1867
2557
  "placeholder",
1868
2558
  "zones",
1869
2559
  "options",
1870
2560
  "fields"
1871
2561
  ]);
1872
- const usedUi = new Set([
2562
+ const usedUi = /* @__PURE__ */ new Set([
1873
2563
  "collapsed",
1874
2564
  "reorderable",
1875
2565
  "input",
@@ -1882,6 +2572,13 @@ function readToV2(n, at, siblings, ctx, top, lane) {
1882
2572
  const use = (...k) => k.forEach((x) => used.add(x));
1883
2573
  const useUi = (...k) => k.forEach((x) => usedUi.add(x));
1884
2574
  if (lane === "props") useUi("group");
2575
+ if (lane === "props") useUi("tab");
2576
+ if (RICH_TYPES.has(n.type) && cfg.features !== void 0) {
2577
+ use("features");
2578
+ const { kept, named } = richFeatures(cfg.features);
2579
+ if (named) loss("config/features", "richtext features", FEATURES["richtext features"].reason);
2580
+ if (kept.length < ALL_RICH_FEATURES.length) f.features = kept;
2581
+ }
1885
2582
  if ([
1886
2583
  "text",
1887
2584
  "longtext",
@@ -1902,6 +2599,10 @@ function readToV2(n, at, siblings, ctx, top, lane) {
1902
2599
  "max",
1903
2600
  "step"
1904
2601
  ]) if (typeof cfg[k] === "number") numeric[k] = cfg[k];
2602
+ if (n.type === "rating") {
2603
+ if (numeric.step === V2_RATING_STEP) delete numeric.step;
2604
+ else if (numeric.step === void 0) numeric.step = 1;
2605
+ }
1905
2606
  const ratingDefault = type === "rating" && n.type === "number" && ratingIsDefault(numeric);
1906
2607
  if (typeof cfg.unit === "string") numeric.unit = cfg.unit;
1907
2608
  if (Object.keys(numeric).length && !ratingDefault) f.numeric = numeric;
@@ -1920,6 +2621,12 @@ function readToV2(n, at, siblings, ctx, top, lane) {
1920
2621
  useUi("control");
1921
2622
  }
1922
2623
  if (n.type === "select") use("multiSelect");
2624
+ if (n.type === "image") {
2625
+ use("media");
2626
+ if (cfg.media === "icon") f.svgOnly = true;
2627
+ }
2628
+ if (typeof cfg.linkHref === "string" && cfg.linkHref) f.linkHref = cfg.linkHref;
2629
+ use("linkHref");
1923
2630
  if ((type === "select" || type === "multiselect" || type === "tags") && n.options) f.options = optionsFromProd(n.options);
1924
2631
  if (type === "multiselect" || type === "tags") {
1925
2632
  if (typeof cfg.min === "number") val.min = cfg.min;
@@ -1937,10 +2644,10 @@ function readToV2(n, at, siblings, ctx, top, lane) {
1937
2644
  if (type === "date" || type === "datetime") use("timezone", "includeTime");
1938
2645
  if ((type === "date" || type === "datetime") && typeof cfg.timezone === "string") f.timezone = cfg.timezone === "site" ? "site" : `fixed:${cfg.timezone}`;
1939
2646
  if ((type === "date" || type === "datetime") && typeof cfg.timezone === "boolean") f.timezone = cfg.timezone;
1940
- if (n.type === "reference" || n.type === "multi-reference" || n.type === "multiReference") use("contentModelId", "polymorphic", "allowedModelSlugs");
2647
+ if (n.type === "reference" || n.type === "multi-reference" || n.type === "multiReference") use("contentModelId", "polymorphic", "allowedModelSlugs", "targets", "filter");
1941
2648
  if (n.type === "reference") {
1942
- const to = cfg.polymorphic ? cfg.allowedModelSlugs ?? [] : cfg.contentModelId ? [cfg.contentModelId] : [];
1943
- if (to.length) f.reference = { to };
2649
+ const ref = referenceFromConfig(cfg);
2650
+ if (ref) f.reference = ref;
1944
2651
  }
1945
2652
  if (n.type === "form") f.reference = { to: ["form"] };
1946
2653
  if (n.type === "object" || n.type === "group") {
@@ -1951,22 +2658,87 @@ function readToV2(n, at, siblings, ctx, top, lane) {
1951
2658
  }
1952
2659
  if (n.type === "array" && n.zones?.nonRepeatable) {
1953
2660
  const nr = n.zones.nonRepeatable;
1954
- const kids = [{
1955
- name: "nonRepeatable",
1956
- title: n.label,
1957
- type: "object",
1958
- fields: nr.map((c, i) => readToV2(c, `${at}/config/zones/nonRepeatable/${i}`, nr, ctx, false, lane))
1959
- }];
1960
- if (n.zones.repeatable) kids.push(listOf(n, n.zones.repeatable.fields, "repeatable", `${at}/config/zones/repeatable`, ctx, lane));
1961
- ctx.onLoss(`${at}/config/zones`, "zone envelope", "A non-repeatable zone reads as an object with nonRepeatable (and repeatable) children, matching its stored {nonRepeatable, repeatable} value.");
1962
- Object.assign(f, {
1963
- type: "object",
1964
- fields: kids
2661
+ const fields = nr.map((c, i) => readToV2(c, `${at}/config/zones/nonRepeatable/${i}`, nr, ctx, false, lane));
2662
+ if (n.zones.repeatable) {
2663
+ const kids = [{
2664
+ name: "nonRepeatable",
2665
+ title: n.label,
2666
+ type: "object",
2667
+ fields
2668
+ }, listOf(n, n.zones.repeatable.fields, "repeatable", `${at}/config/zones/repeatable`, ctx, lane)];
2669
+ ctx.onLoss(`${at}/config/zones`, "zone envelope", "A zone pair reads as an object with nonRepeatable and repeatable children, matching its stored {nonRepeatable, repeatable} value.");
2670
+ Object.assign(f, {
2671
+ type: "object",
2672
+ fields: kids
2673
+ });
2674
+ } else {
2675
+ Object.assign(f, {
2676
+ type: "object",
2677
+ fields
2678
+ });
2679
+ ctx.zoned.add(f);
2680
+ }
2681
+ } else if (slotProp) {
2682
+ const ids = Array.isArray(cfg.componentIds) ? cfg.componentIds.filter((x) => typeof x === "string") : [];
2683
+ const names = Array.isArray(cfg.allow) ? cfg.allow.filter((x) => typeof x === "string") : [];
2684
+ f.of = ids.length ? {
2685
+ type: "block",
2686
+ allow: ids
2687
+ } : names.length ? {
2688
+ type: "block",
2689
+ allow: names
2690
+ } : { type: "block" };
2691
+ ctx.slots.set(f, cfg.multiple === true);
2692
+ const settings = Array.isArray(cfg.slotSettings) ? cfg.slotSettings.map(readProp) : [];
2693
+ if (settings.length) f.slotSettings = settings.map((c, k) => readToV2(c, `${at}/config/slotSettings/${k}`, settings, ctx, false, lane));
2694
+ if (cfg.multiple !== true) {
2695
+ val.max = 1;
2696
+ loss("config/multiple", "slot single", "A single slot holds one component instance; V2 reads it as a slot of at most one block.");
2697
+ } else {
2698
+ if (typeof cfg.minItems === "number") val.min = cfg.minItems;
2699
+ if (typeof cfg.maxItems === "number") val.max = cfg.maxItems;
2700
+ }
2701
+ use("multiple", "componentIds", "allow", "minItems", "maxItems", "slotSettings");
2702
+ } else if (blockSlot) {
2703
+ const slugs = Array.isArray(cfg.blockSlugs) ? cfg.blockSlugs.filter((x) => typeof x === "string") : [];
2704
+ f.of = slugs.length ? {
2705
+ type: "block",
2706
+ allow: slugs.map(camel)
2707
+ } : { type: "block" };
2708
+ ctx.blocks.add(f);
2709
+ const settings = Array.isArray(cfg.slotSettings) ? cfg.slotSettings.map(readField) : [];
2710
+ if (settings.length) f.slotSettings = settings.map((c, k) => readToV2(c, `${at}/config/slotSettings/${k}`, settings, ctx, false, lane));
2711
+ if (typeof cfg.minItems === "number") val.min = cfg.minItems;
2712
+ if (typeof cfg.maxItems === "number") val.max = cfg.maxItems;
2713
+ use("blockSlugs", "minItems", "maxItems", "slotSettings");
2714
+ } else if (lane === "props" && n.type === "table" && cfg.mode === "types") {
2715
+ f.of = (Array.isArray(cfg.itemTypes) ? cfg.itemTypes : []).map((t, j) => {
2716
+ const kids = (t.fields ?? []).map(readProp);
2717
+ const item = {
2718
+ name: t.name,
2719
+ title: t.label ?? t.name,
2720
+ type: "object",
2721
+ fields: kids.map((c, k) => readToV2(c, `${at}/config/itemTypes/${j}/fields/${k}`, kids, ctx, false, lane))
2722
+ };
2723
+ if (t.icon) item.icon = tablerIconOf(t.icon);
2724
+ if (t.preview) item.preview = previewFromProd(t.preview, kids);
2725
+ return item;
1965
2726
  });
2727
+ ctx.types.add(f);
2728
+ if (n.ui.itemLabel !== void 0) f.itemNoun = n.ui.itemLabel.toLowerCase();
2729
+ if (n.ui.icon) f.icon = tablerIconOf(n.ui.icon);
2730
+ const l = n.ui.layout;
2731
+ if (l === "grid" || l === "list") f.arrayLayout = l;
2732
+ else if (l === "rows") f.layout = "list";
2733
+ else if (l) loss("ui/layout", "ui.layout", `V2 has no "${l}" layout for a list of item types.`);
2734
+ if (typeof cfg.minItems === "number") val.min = cfg.minItems;
2735
+ if (typeof cfg.maxItems === "number") val.max = cfg.maxItems;
2736
+ use("mode", "itemTypes", "minItems", "maxItems");
2737
+ useUi("itemLabel", "icon", "layout");
1966
2738
  } else if (dataTable) {
1967
2739
  f.table = tableOptionsFromConfig(cfg);
1968
2740
  use("mode", "columnTypes", "lockColumns", "requireCaption", "allowGroups", "allowFootnotes", "allowNote", "minRows", "maxRows", "maxColumns", "settings", "jsonLd");
1969
- } else if (n.type === "array" && n.zones?.repeatable || n.type === "repeater" || n.type === "table") {
2741
+ } else if (n.type === "array" && (n.zones?.repeatable || !n.zones && !cfg.itemType && n.fields?.length) || n.type === "repeater" || n.type === "table") {
1970
2742
  const list = listOf(n, n.zones?.repeatable?.fields ?? n.fields ?? [], n.key, at, ctx, lane);
1971
2743
  useUi("preview", "icon", "itemLabel", "layout", "itemName", "itemTitle");
1972
2744
  if (n.zones?.repeatable) ctx.zoned.add(f);
@@ -1982,14 +2754,19 @@ function readToV2(n, at, siblings, ctx, top, lane) {
1982
2754
  if (typeof r?.maxItems === "number") val.max = r.maxItems;
1983
2755
  if (n.type === "table") use("minItems", "maxItems");
1984
2756
  } else if (n.type === "array" && cfg.itemType) {
1985
- f.of = [{
2757
+ f.of = [shopify ? {
1986
2758
  ...itemOf(n),
1987
- type: cfg.itemType === "text" ? "string" : cfg.itemType
2759
+ type: "reference",
2760
+ reference: { to: ["shopify"] }
2761
+ } : {
2762
+ ...itemOf(n),
2763
+ type: cfg.itemType === "text" ? "string" : cfg.itemType,
2764
+ ...itemSettingsFromProd(cfg.item)
1988
2765
  }];
1989
2766
  leafListChrome(n, f, loss, "a list of scalars");
1990
2767
  if (typeof cfg.minItems === "number") val.min = cfg.minItems;
1991
2768
  if (typeof cfg.maxItems === "number") val.max = cfg.maxItems;
1992
- use("itemType", "minItems", "maxItems");
2769
+ use("itemType", "minItems", "maxItems", "item");
1993
2770
  useUi("layout", "itemLabel", "itemName", "itemTitle");
1994
2771
  } else if (n.type === "modular") {
1995
2772
  const slugs = cfg.blockSlugs ?? [];
@@ -2026,11 +2803,11 @@ function readToV2(n, at, siblings, ctx, top, lane) {
2026
2803
  use("blockSlugs", "minItems", "maxItems");
2027
2804
  useUi("itemLabel", "icon", "layout", "blocks");
2028
2805
  } else if (n.type === "multi-reference" || n.type === "multiReference") {
2029
- const to = cfg.polymorphic ? cfg.allowedModelSlugs ?? [] : cfg.contentModelId ? [cfg.contentModelId] : [];
2806
+ const ref = referenceFromConfig(cfg);
2030
2807
  f.of = [{
2031
2808
  ...itemOf(n),
2032
2809
  type: "reference",
2033
- ...to.length ? { reference: { to } } : {}
2810
+ ...ref ? { reference: ref } : {}
2034
2811
  }];
2035
2812
  leafListChrome(n, f, loss, "a list of entries");
2036
2813
  if (typeof cfg.min === "number") val.min = cfg.min;
@@ -2039,16 +2816,47 @@ function readToV2(n, at, siblings, ctx, top, lane) {
2039
2816
  useUi("layout", "itemLabel", "itemName", "itemTitle");
2040
2817
  }
2041
2818
  if (n.type === "array" && n.zones?.nonRepeatable && n.zones.repeatable) useUi("preview", "icon", "itemLabel", "layout");
2042
- for (const [k, x] of Object.entries(cfg)) if (x !== void 0 && !used.has(k)) loss(`config/${k}`, "config", `V2 has no "${k}" setting on a ${type} field.`);
2819
+ for (const [k, x] of Object.entries(cfg)) {
2820
+ if (x === void 0 || used.has(k)) continue;
2821
+ if (isHostConfigKey(n.type, k) || isHostConfigKey(type, k)) loss(`config/${k}`, "host config", CATALOG["host config"]);
2822
+ else loss(`config/${k}`, "config", `V2 has no "${k}" setting on a ${type} field.`);
2823
+ }
2043
2824
  for (const [k, x] of Object.entries(n.ui)) if (x !== void 0 && !usedUi.has(k)) loss(`ui/${k}`, `ui.${k}`, `V2 has no ui.${k} on a ${type} field.`);
2044
2825
  if (n.defaultValue !== void 0 || top && ctx.plans) {
2045
- const plan = readPlan(f, n.type, ctx.zoned, ctx.modular);
2826
+ const plan = readPlan(f, n.type, ctx.zoned, ctx.modular, ctx.types, ctx.slots, ctx.blocks);
2046
2827
  if (top) ctx.plans?.set(f.name, plan);
2047
2828
  if (n.defaultValue !== void 0) f.initialValue = planFromProd(plan, n.defaultValue, (rel, reason) => loss(`defaultValue${rel}`, "stored value", reason));
2048
2829
  }
2049
2830
  if (Object.keys(val).length) f.validation = val;
2050
2831
  return f;
2051
2832
  }
2833
+ /** config.item → the V2 item field's description, localized flag and validation (in V2's key order). */
2834
+ function itemSettingsFromProd(item) {
2835
+ if (!isObj(item)) return {};
2836
+ const s = item;
2837
+ const out = {};
2838
+ const val = {};
2839
+ if (s.required !== void 0) val.required = s.required;
2840
+ if (s.minLength !== void 0) val.min = s.minLength;
2841
+ if (s.maxLength !== void 0) val.max = s.maxLength;
2842
+ if (s.pattern !== void 0) val.pattern = s.pattern;
2843
+ if (s.patternMessage !== void 0) val.patternMessage = s.patternMessage;
2844
+ if (Object.keys(val).length) out.validation = val;
2845
+ if (s.localized !== void 0) out.localized = s.localized;
2846
+ if (s.helpText !== void 0) out.description = s.helpText;
2847
+ return out;
2848
+ }
2849
+ /** A reference config → the V2 `reference`: entry models first, then config.targets; config.filter as written. */
2850
+ function referenceFromConfig(cfg) {
2851
+ const entries = cfg.polymorphic ? cfg.allowedModelSlugs ?? [] : cfg.contentModelId ? [cfg.contentModelId] : [];
2852
+ const targets = Array.isArray(cfg.targets) ? cfg.targets.filter((t) => typeof t === "string") : [];
2853
+ const to = [...entries, ...targets];
2854
+ if (!to.length && typeof cfg.filter !== "string") return void 0;
2855
+ return {
2856
+ to,
2857
+ ...typeof cfg.filter === "string" ? { filter: cfg.filter } : {}
2858
+ };
2859
+ }
2052
2860
  /** A list of scalars or entries: its item's name and title (ui.itemName / ui.itemTitle, else the convention). */
2053
2861
  var itemOf = (n) => {
2054
2862
  const conv = conventionItem(n.key, n.label);
@@ -2100,6 +2908,9 @@ function toTypeSchema(model, opts = {}) {
2100
2908
  sets: /* @__PURE__ */ new Map(),
2101
2909
  zoned: /* @__PURE__ */ new WeakSet(),
2102
2910
  modular: /* @__PURE__ */ new WeakSet(),
2911
+ types: /* @__PURE__ */ new WeakSet(),
2912
+ slots: /* @__PURE__ */ new WeakMap(),
2913
+ blocks: /* @__PURE__ */ new WeakSet(),
2103
2914
  blockModels: new Map((opts.blockModels ?? []).map((m) => [m.slug, m])),
2104
2915
  plans: opts.plans
2105
2916
  };
@@ -2156,6 +2967,9 @@ function propsToTypeSchema(component, opts = {}) {
2156
2967
  sets: /* @__PURE__ */ new Map(),
2157
2968
  zoned: /* @__PURE__ */ new WeakSet(),
2158
2969
  modular: /* @__PURE__ */ new WeakSet(),
2970
+ types: /* @__PURE__ */ new WeakSet(),
2971
+ slots: /* @__PURE__ */ new WeakMap(),
2972
+ blocks: /* @__PURE__ */ new WeakSet(),
2159
2973
  blockModels: new Map((opts.blockModels ?? []).map((m) => [m.slug, m])),
2160
2974
  plans: opts.plans
2161
2975
  };
@@ -2186,6 +3000,23 @@ function propsToTypeSchema(component, opts = {}) {
2186
3000
  const at = new Map(fieldsets.map((s, i) => [s.name, i]));
2187
3001
  fieldsets.sort((a, b) => (order.get(a.name) ?? at.get(a.name)) - (order.get(b.name) ?? at.get(b.name)));
2188
3002
  }
3003
+ const groups = [];
3004
+ const tabOrder = /* @__PURE__ */ new Map();
3005
+ for (const t of [...nodes.map((n) => n.ui.tab), ...component.tabs ?? []]) {
3006
+ if (!t || groups.some((g) => g.name === t.id)) continue;
3007
+ const g = {
3008
+ name: t.id,
3009
+ title: t.label ?? t.id
3010
+ };
3011
+ if (t.icon) g.icon = tablerIconOf(t.icon);
3012
+ if (typeof t.order === "number") tabOrder.set(t.id, t.order);
3013
+ groups.push(g);
3014
+ }
3015
+ if (tabOrder.size) {
3016
+ const at = new Map(groups.map((g, i) => [g.name, i]));
3017
+ groups.sort((a, b) => (tabOrder.get(a.name) ?? at.get(a.name)) - (tabOrder.get(b.name) ?? at.get(b.name)));
3018
+ }
3019
+ if (groups.length) groups[0].default = true;
2189
3020
  const out = {
2190
3021
  name: camel(component.slug),
2191
3022
  title: component.name,
@@ -2194,10 +3025,13 @@ function propsToTypeSchema(component, opts = {}) {
2194
3025
  };
2195
3026
  if (component.description !== void 0 && component.description !== null) out.description = component.description;
2196
3027
  if (component.category !== void 0 && component.category !== "custom") out.category = capitalize(component.category);
3028
+ if (component.icon) out.icon = tablerIconOf(component.icon);
3029
+ if (component.preview) out.preview = previewFromProd(component.preview, nodes);
2197
3030
  if (fieldsets.length) out.fieldsets = fieldsets;
3031
+ if (groups.length) out.groups = groups;
2198
3032
  return out;
2199
3033
  }
2200
3034
  //#endregion
2201
- export { CATEGORY_TO_PROD, fromTypeSchema, leafValuePlan, planFromProd, planToProd, propsToTypeSchema, toTypeSchema };
3035
+ export { CATEGORY_TO_PROD, SHOPIFY_REF_LOCK, fromTypeSchema, leafValuePlan, planFromProd, planToProd, propsToTypeSchema, schemaSlugOf, toTypeSchema };
2202
3036
 
2203
3037
  //# sourceMappingURL=schema.js.map