@pilotiq/pilotiq 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (1409) hide show
  1. package/.turbo/turbo-build.log +4 -0
  2. package/CHANGELOG.md +11 -0
  3. package/CLAUDE.md +207 -0
  4. package/LICENSE +21 -0
  5. package/dist/Cluster.d.ts +56 -0
  6. package/dist/Cluster.d.ts.map +1 -0
  7. package/dist/Cluster.js +62 -0
  8. package/dist/Cluster.js.map +1 -0
  9. package/dist/Column.d.ts +378 -0
  10. package/dist/Column.d.ts.map +1 -0
  11. package/dist/Column.js +434 -0
  12. package/dist/Column.js.map +1 -0
  13. package/dist/Global.d.ts +123 -0
  14. package/dist/Global.d.ts.map +1 -0
  15. package/dist/Global.js +124 -0
  16. package/dist/Global.js.map +1 -0
  17. package/dist/Page.d.ts +90 -0
  18. package/dist/Page.d.ts.map +1 -0
  19. package/dist/Page.js +107 -0
  20. package/dist/Page.js.map +1 -0
  21. package/dist/Pilotiq.d.ts +505 -0
  22. package/dist/Pilotiq.d.ts.map +1 -0
  23. package/dist/Pilotiq.js +463 -0
  24. package/dist/Pilotiq.js.map +1 -0
  25. package/dist/PilotiqRegistry.d.ts +10 -0
  26. package/dist/PilotiqRegistry.d.ts.map +1 -0
  27. package/dist/PilotiqRegistry.js +32 -0
  28. package/dist/PilotiqRegistry.js.map +1 -0
  29. package/dist/PilotiqServiceProvider.d.ts +16 -0
  30. package/dist/PilotiqServiceProvider.d.ts.map +1 -0
  31. package/dist/PilotiqServiceProvider.js +57 -0
  32. package/dist/PilotiqServiceProvider.js.map +1 -0
  33. package/dist/RelationManager.d.ts +372 -0
  34. package/dist/RelationManager.d.ts.map +1 -0
  35. package/dist/RelationManager.js +342 -0
  36. package/dist/RelationManager.js.map +1 -0
  37. package/dist/RenderHook.d.ts +86 -0
  38. package/dist/RenderHook.d.ts.map +1 -0
  39. package/dist/RenderHook.js +116 -0
  40. package/dist/RenderHook.js.map +1 -0
  41. package/dist/Resource.d.ts +290 -0
  42. package/dist/Resource.d.ts.map +1 -0
  43. package/dist/Resource.js +362 -0
  44. package/dist/Resource.js.map +1 -0
  45. package/dist/RightPanel.d.ts +92 -0
  46. package/dist/RightPanel.d.ts.map +1 -0
  47. package/dist/RightPanel.js +61 -0
  48. package/dist/RightPanel.js.map +1 -0
  49. package/dist/Tab.d.ts +92 -0
  50. package/dist/Tab.d.ts.map +1 -0
  51. package/dist/Tab.js +93 -0
  52. package/dist/Tab.js.map +1 -0
  53. package/dist/UserMenuItem.d.ts +76 -0
  54. package/dist/UserMenuItem.d.ts.map +1 -0
  55. package/dist/UserMenuItem.js +87 -0
  56. package/dist/UserMenuItem.js.map +1 -0
  57. package/dist/actions/Action.d.ts +888 -0
  58. package/dist/actions/Action.d.ts.map +1 -0
  59. package/dist/actions/Action.js +1652 -0
  60. package/dist/actions/Action.js.map +1 -0
  61. package/dist/actions/ActionGroup.d.ts +85 -0
  62. package/dist/actions/ActionGroup.d.ts.map +1 -0
  63. package/dist/actions/ActionGroup.js +132 -0
  64. package/dist/actions/ActionGroup.js.map +1 -0
  65. package/dist/actions/attachFactory.d.ts +67 -0
  66. package/dist/actions/attachFactory.d.ts.map +1 -0
  67. package/dist/actions/attachFactory.js +115 -0
  68. package/dist/actions/attachFactory.js.map +1 -0
  69. package/dist/actions/exportFactory.d.ts +88 -0
  70. package/dist/actions/exportFactory.d.ts.map +1 -0
  71. package/dist/actions/exportFactory.js +144 -0
  72. package/dist/actions/exportFactory.js.map +1 -0
  73. package/dist/actions/importFactory.d.ts +97 -0
  74. package/dist/actions/importFactory.d.ts.map +1 -0
  75. package/dist/actions/importFactory.js +143 -0
  76. package/dist/actions/importFactory.js.map +1 -0
  77. package/dist/actions/index.d.ts +3 -0
  78. package/dist/actions/index.d.ts.map +1 -0
  79. package/dist/actions/index.js +3 -0
  80. package/dist/actions/index.js.map +1 -0
  81. package/dist/applyPageHooks.d.ts +54 -0
  82. package/dist/applyPageHooks.d.ts.map +1 -0
  83. package/dist/applyPageHooks.js +149 -0
  84. package/dist/applyPageHooks.js.map +1 -0
  85. package/dist/cells/coerce.d.ts +25 -0
  86. package/dist/cells/coerce.d.ts.map +1 -0
  87. package/dist/cells/coerce.js +87 -0
  88. package/dist/cells/coerce.js.map +1 -0
  89. package/dist/clusterPaths.d.ts +9 -0
  90. package/dist/clusterPaths.d.ts.map +1 -0
  91. package/dist/clusterPaths.js +19 -0
  92. package/dist/clusterPaths.js.map +1 -0
  93. package/dist/columns/BadgeColumn.d.ts +16 -0
  94. package/dist/columns/BadgeColumn.d.ts.map +1 -0
  95. package/dist/columns/BadgeColumn.js +25 -0
  96. package/dist/columns/BadgeColumn.js.map +1 -0
  97. package/dist/columns/BooleanColumn.d.ts +10 -0
  98. package/dist/columns/BooleanColumn.d.ts.map +1 -0
  99. package/dist/columns/BooleanColumn.js +18 -0
  100. package/dist/columns/BooleanColumn.js.map +1 -0
  101. package/dist/columns/ColorColumn.d.ts +27 -0
  102. package/dist/columns/ColorColumn.d.ts.map +1 -0
  103. package/dist/columns/ColorColumn.js +35 -0
  104. package/dist/columns/ColorColumn.js.map +1 -0
  105. package/dist/columns/IconColumn.d.ts +22 -0
  106. package/dist/columns/IconColumn.d.ts.map +1 -0
  107. package/dist/columns/IconColumn.js +28 -0
  108. package/dist/columns/IconColumn.js.map +1 -0
  109. package/dist/columns/ImageColumn.d.ts +17 -0
  110. package/dist/columns/ImageColumn.d.ts.map +1 -0
  111. package/dist/columns/ImageColumn.js +24 -0
  112. package/dist/columns/ImageColumn.js.map +1 -0
  113. package/dist/columns/SelectColumn.d.ts +36 -0
  114. package/dist/columns/SelectColumn.d.ts.map +1 -0
  115. package/dist/columns/SelectColumn.js +52 -0
  116. package/dist/columns/SelectColumn.js.map +1 -0
  117. package/dist/columns/TextColumn.d.ts +18 -0
  118. package/dist/columns/TextColumn.d.ts.map +1 -0
  119. package/dist/columns/TextColumn.js +20 -0
  120. package/dist/columns/TextColumn.js.map +1 -0
  121. package/dist/columns/TextInputColumn.d.ts +47 -0
  122. package/dist/columns/TextInputColumn.d.ts.map +1 -0
  123. package/dist/columns/TextInputColumn.js +60 -0
  124. package/dist/columns/TextInputColumn.js.map +1 -0
  125. package/dist/columns/ToggleColumn.d.ts +32 -0
  126. package/dist/columns/ToggleColumn.d.ts.map +1 -0
  127. package/dist/columns/ToggleColumn.js +45 -0
  128. package/dist/columns/ToggleColumn.js.map +1 -0
  129. package/dist/columns/index.d.ts +10 -0
  130. package/dist/columns/index.d.ts.map +1 -0
  131. package/dist/columns/index.js +10 -0
  132. package/dist/columns/index.js.map +1 -0
  133. package/dist/defaultGlobalPages.d.ts +11 -0
  134. package/dist/defaultGlobalPages.d.ts.map +1 -0
  135. package/dist/defaultGlobalPages.js +87 -0
  136. package/dist/defaultGlobalPages.js.map +1 -0
  137. package/dist/defaultPages.d.ts +247 -0
  138. package/dist/defaultPages.d.ts.map +1 -0
  139. package/dist/defaultPages.js +558 -0
  140. package/dist/defaultPages.js.map +1 -0
  141. package/dist/elements/Form.d.ts +219 -0
  142. package/dist/elements/Form.d.ts.map +1 -0
  143. package/dist/elements/Form.js +259 -0
  144. package/dist/elements/Form.js.map +1 -0
  145. package/dist/elements/ListTabs.d.ts +17 -0
  146. package/dist/elements/ListTabs.d.ts.map +1 -0
  147. package/dist/elements/ListTabs.js +23 -0
  148. package/dist/elements/ListTabs.js.map +1 -0
  149. package/dist/elements/Table.d.ts +535 -0
  150. package/dist/elements/Table.d.ts.map +1 -0
  151. package/dist/elements/Table.js +481 -0
  152. package/dist/elements/Table.js.map +1 -0
  153. package/dist/elements/TableGroup.d.ts +121 -0
  154. package/dist/elements/TableGroup.d.ts.map +1 -0
  155. package/dist/elements/TableGroup.js +162 -0
  156. package/dist/elements/TableGroup.js.map +1 -0
  157. package/dist/elements/dispatchAction.d.ts +127 -0
  158. package/dist/elements/dispatchAction.d.ts.map +1 -0
  159. package/dist/elements/dispatchAction.js +254 -0
  160. package/dist/elements/dispatchAction.js.map +1 -0
  161. package/dist/elements/dispatchForm.d.ts +220 -0
  162. package/dist/elements/dispatchForm.d.ts.map +1 -0
  163. package/dist/elements/dispatchForm.js +1645 -0
  164. package/dist/elements/dispatchForm.js.map +1 -0
  165. package/dist/elements/dispatchTable.d.ts +69 -0
  166. package/dist/elements/dispatchTable.d.ts.map +1 -0
  167. package/dist/elements/dispatchTable.js +606 -0
  168. package/dist/elements/dispatchTable.js.map +1 -0
  169. package/dist/elements/index.d.ts +3 -0
  170. package/dist/elements/index.d.ts.map +1 -0
  171. package/dist/elements/index.js +3 -0
  172. package/dist/elements/index.js.map +1 -0
  173. package/dist/entries/BadgeEntry.d.ts +21 -0
  174. package/dist/entries/BadgeEntry.d.ts.map +1 -0
  175. package/dist/entries/BadgeEntry.js +32 -0
  176. package/dist/entries/BadgeEntry.js.map +1 -0
  177. package/dist/entries/CodeEntry.d.ts +38 -0
  178. package/dist/entries/CodeEntry.d.ts.map +1 -0
  179. package/dist/entries/CodeEntry.js +44 -0
  180. package/dist/entries/CodeEntry.js.map +1 -0
  181. package/dist/entries/ColorEntry.d.ts +32 -0
  182. package/dist/entries/ColorEntry.d.ts.map +1 -0
  183. package/dist/entries/ColorEntry.js +48 -0
  184. package/dist/entries/ColorEntry.js.map +1 -0
  185. package/dist/entries/ComponentEntry.d.ts +66 -0
  186. package/dist/entries/ComponentEntry.d.ts.map +1 -0
  187. package/dist/entries/ComponentEntry.js +86 -0
  188. package/dist/entries/ComponentEntry.js.map +1 -0
  189. package/dist/entries/Entry.d.ts +175 -0
  190. package/dist/entries/Entry.d.ts.map +1 -0
  191. package/dist/entries/Entry.js +233 -0
  192. package/dist/entries/Entry.js.map +1 -0
  193. package/dist/entries/IconEntry.d.ts +30 -0
  194. package/dist/entries/IconEntry.d.ts.map +1 -0
  195. package/dist/entries/IconEntry.js +34 -0
  196. package/dist/entries/IconEntry.js.map +1 -0
  197. package/dist/entries/ImageEntry.d.ts +33 -0
  198. package/dist/entries/ImageEntry.d.ts.map +1 -0
  199. package/dist/entries/ImageEntry.js +47 -0
  200. package/dist/entries/ImageEntry.js.map +1 -0
  201. package/dist/entries/KeyValueEntry.d.ts +30 -0
  202. package/dist/entries/KeyValueEntry.d.ts.map +1 -0
  203. package/dist/entries/KeyValueEntry.js +38 -0
  204. package/dist/entries/KeyValueEntry.js.map +1 -0
  205. package/dist/entries/RepeatableEntry.d.ts +122 -0
  206. package/dist/entries/RepeatableEntry.d.ts.map +1 -0
  207. package/dist/entries/RepeatableEntry.js +121 -0
  208. package/dist/entries/RepeatableEntry.js.map +1 -0
  209. package/dist/entries/TextEntry.d.ts +38 -0
  210. package/dist/entries/TextEntry.d.ts.map +1 -0
  211. package/dist/entries/TextEntry.js +49 -0
  212. package/dist/entries/TextEntry.js.map +1 -0
  213. package/dist/entries/index.d.ts +2 -0
  214. package/dist/entries/index.d.ts.map +1 -0
  215. package/dist/entries/index.js +8 -0
  216. package/dist/entries/index.js.map +1 -0
  217. package/dist/entries/registry.d.ts +41 -0
  218. package/dist/entries/registry.d.ts.map +1 -0
  219. package/dist/entries/registry.js +17 -0
  220. package/dist/entries/registry.js.map +1 -0
  221. package/dist/fields/BuilderField.d.ts +420 -0
  222. package/dist/fields/BuilderField.d.ts.map +1 -0
  223. package/dist/fields/BuilderField.js +359 -0
  224. package/dist/fields/BuilderField.js.map +1 -0
  225. package/dist/fields/CheckboxField.d.ts +18 -0
  226. package/dist/fields/CheckboxField.d.ts.map +1 -0
  227. package/dist/fields/CheckboxField.js +23 -0
  228. package/dist/fields/CheckboxField.js.map +1 -0
  229. package/dist/fields/CheckboxListField.d.ts +25 -0
  230. package/dist/fields/CheckboxListField.d.ts.map +1 -0
  231. package/dist/fields/CheckboxListField.js +46 -0
  232. package/dist/fields/CheckboxListField.js.map +1 -0
  233. package/dist/fields/ColorPickerField.d.ts +16 -0
  234. package/dist/fields/ColorPickerField.d.ts.map +1 -0
  235. package/dist/fields/ColorPickerField.js +21 -0
  236. package/dist/fields/ColorPickerField.js.map +1 -0
  237. package/dist/fields/DateField.d.ts +29 -0
  238. package/dist/fields/DateField.d.ts.map +1 -0
  239. package/dist/fields/DateField.js +45 -0
  240. package/dist/fields/DateField.js.map +1 -0
  241. package/dist/fields/EmailField.d.ts +8 -0
  242. package/dist/fields/EmailField.d.ts.map +1 -0
  243. package/dist/fields/EmailField.js +13 -0
  244. package/dist/fields/EmailField.js.map +1 -0
  245. package/dist/fields/Field.d.ts +485 -0
  246. package/dist/fields/Field.d.ts.map +1 -0
  247. package/dist/fields/Field.js +539 -0
  248. package/dist/fields/Field.js.map +1 -0
  249. package/dist/fields/FileUploadField.d.ts +43 -0
  250. package/dist/fields/FileUploadField.d.ts.map +1 -0
  251. package/dist/fields/FileUploadField.js +60 -0
  252. package/dist/fields/FileUploadField.js.map +1 -0
  253. package/dist/fields/HiddenField.d.ts +19 -0
  254. package/dist/fields/HiddenField.d.ts.map +1 -0
  255. package/dist/fields/HiddenField.js +24 -0
  256. package/dist/fields/HiddenField.js.map +1 -0
  257. package/dist/fields/KeyValueField.d.ts +36 -0
  258. package/dist/fields/KeyValueField.d.ts.map +1 -0
  259. package/dist/fields/KeyValueField.js +47 -0
  260. package/dist/fields/KeyValueField.js.map +1 -0
  261. package/dist/fields/MarkdownField.d.ts +79 -0
  262. package/dist/fields/MarkdownField.d.ts.map +1 -0
  263. package/dist/fields/MarkdownField.js +117 -0
  264. package/dist/fields/MarkdownField.js.map +1 -0
  265. package/dist/fields/NumberField.d.ts +17 -0
  266. package/dist/fields/NumberField.d.ts.map +1 -0
  267. package/dist/fields/NumberField.js +27 -0
  268. package/dist/fields/NumberField.js.map +1 -0
  269. package/dist/fields/RadioField.d.ts +26 -0
  270. package/dist/fields/RadioField.d.ts.map +1 -0
  271. package/dist/fields/RadioField.js +47 -0
  272. package/dist/fields/RadioField.js.map +1 -0
  273. package/dist/fields/RepeaterField.d.ts +594 -0
  274. package/dist/fields/RepeaterField.d.ts.map +1 -0
  275. package/dist/fields/RepeaterField.js +504 -0
  276. package/dist/fields/RepeaterField.js.map +1 -0
  277. package/dist/fields/RowButton.d.ts +86 -0
  278. package/dist/fields/RowButton.d.ts.map +1 -0
  279. package/dist/fields/RowButton.js +85 -0
  280. package/dist/fields/RowButton.js.map +1 -0
  281. package/dist/fields/SelectField.d.ts +127 -0
  282. package/dist/fields/SelectField.d.ts.map +1 -0
  283. package/dist/fields/SelectField.js +160 -0
  284. package/dist/fields/SelectField.js.map +1 -0
  285. package/dist/fields/SliderField.d.ts +31 -0
  286. package/dist/fields/SliderField.d.ts.map +1 -0
  287. package/dist/fields/SliderField.js +45 -0
  288. package/dist/fields/SliderField.js.map +1 -0
  289. package/dist/fields/SlugField.d.ts +11 -0
  290. package/dist/fields/SlugField.d.ts.map +1 -0
  291. package/dist/fields/SlugField.js +19 -0
  292. package/dist/fields/SlugField.js.map +1 -0
  293. package/dist/fields/TagsInputField.d.ts +65 -0
  294. package/dist/fields/TagsInputField.d.ts.map +1 -0
  295. package/dist/fields/TagsInputField.js +104 -0
  296. package/dist/fields/TagsInputField.js.map +1 -0
  297. package/dist/fields/TextField.d.ts +11 -0
  298. package/dist/fields/TextField.d.ts.map +1 -0
  299. package/dist/fields/TextField.js +19 -0
  300. package/dist/fields/TextField.js.map +1 -0
  301. package/dist/fields/TextareaField.d.ts +40 -0
  302. package/dist/fields/TextareaField.d.ts.map +1 -0
  303. package/dist/fields/TextareaField.js +51 -0
  304. package/dist/fields/TextareaField.js.map +1 -0
  305. package/dist/fields/ToggleButtonsField.d.ts +24 -0
  306. package/dist/fields/ToggleButtonsField.d.ts.map +1 -0
  307. package/dist/fields/ToggleButtonsField.js +41 -0
  308. package/dist/fields/ToggleButtonsField.js.map +1 -0
  309. package/dist/fields/ToggleField.d.ts +8 -0
  310. package/dist/fields/ToggleField.d.ts.map +1 -0
  311. package/dist/fields/ToggleField.js +13 -0
  312. package/dist/fields/ToggleField.js.map +1 -0
  313. package/dist/fields/optionsResolver.d.ts +54 -0
  314. package/dist/fields/optionsResolver.d.ts.map +1 -0
  315. package/dist/fields/optionsResolver.js +62 -0
  316. package/dist/fields/optionsResolver.js.map +1 -0
  317. package/dist/fields/resolveField.d.ts +21 -0
  318. package/dist/fields/resolveField.d.ts.map +1 -0
  319. package/dist/fields/resolveField.js +26 -0
  320. package/dist/fields/resolveField.js.map +1 -0
  321. package/dist/filters/BooleanFilter.d.ts +20 -0
  322. package/dist/filters/BooleanFilter.d.ts.map +1 -0
  323. package/dist/filters/BooleanFilter.js +31 -0
  324. package/dist/filters/BooleanFilter.js.map +1 -0
  325. package/dist/filters/DateRangeFilter.d.ts +68 -0
  326. package/dist/filters/DateRangeFilter.d.ts.map +1 -0
  327. package/dist/filters/DateRangeFilter.js +137 -0
  328. package/dist/filters/DateRangeFilter.js.map +1 -0
  329. package/dist/filters/Filter.d.ts +140 -0
  330. package/dist/filters/Filter.d.ts.map +1 -0
  331. package/dist/filters/Filter.js +99 -0
  332. package/dist/filters/Filter.js.map +1 -0
  333. package/dist/filters/FormFilter.d.ts +103 -0
  334. package/dist/filters/FormFilter.d.ts.map +1 -0
  335. package/dist/filters/FormFilter.js +180 -0
  336. package/dist/filters/FormFilter.js.map +1 -0
  337. package/dist/filters/MultiSelectFilter.d.ts +41 -0
  338. package/dist/filters/MultiSelectFilter.d.ts.map +1 -0
  339. package/dist/filters/MultiSelectFilter.js +67 -0
  340. package/dist/filters/MultiSelectFilter.js.map +1 -0
  341. package/dist/filters/QueryBuilderFilter.d.ts +145 -0
  342. package/dist/filters/QueryBuilderFilter.d.ts.map +1 -0
  343. package/dist/filters/QueryBuilderFilter.js +323 -0
  344. package/dist/filters/QueryBuilderFilter.js.map +1 -0
  345. package/dist/filters/SelectFilter.d.ts +26 -0
  346. package/dist/filters/SelectFilter.d.ts.map +1 -0
  347. package/dist/filters/SelectFilter.js +35 -0
  348. package/dist/filters/SelectFilter.js.map +1 -0
  349. package/dist/filters/TernaryFilter.d.ts +35 -0
  350. package/dist/filters/TernaryFilter.d.ts.map +1 -0
  351. package/dist/filters/TernaryFilter.js +71 -0
  352. package/dist/filters/TernaryFilter.js.map +1 -0
  353. package/dist/filters/TrashedFilter.d.ts +28 -0
  354. package/dist/filters/TrashedFilter.d.ts.map +1 -0
  355. package/dist/filters/TrashedFilter.js +52 -0
  356. package/dist/filters/TrashedFilter.js.map +1 -0
  357. package/dist/filters/queryBuilder/BooleanConstraint.d.ts +13 -0
  358. package/dist/filters/queryBuilder/BooleanConstraint.d.ts.map +1 -0
  359. package/dist/filters/queryBuilder/BooleanConstraint.js +27 -0
  360. package/dist/filters/queryBuilder/BooleanConstraint.js.map +1 -0
  361. package/dist/filters/queryBuilder/Constraint.d.ts +74 -0
  362. package/dist/filters/queryBuilder/Constraint.d.ts.map +1 -0
  363. package/dist/filters/queryBuilder/Constraint.js +45 -0
  364. package/dist/filters/queryBuilder/Constraint.js.map +1 -0
  365. package/dist/filters/queryBuilder/DateConstraint.d.ts +18 -0
  366. package/dist/filters/queryBuilder/DateConstraint.d.ts.map +1 -0
  367. package/dist/filters/queryBuilder/DateConstraint.js +63 -0
  368. package/dist/filters/queryBuilder/DateConstraint.js.map +1 -0
  369. package/dist/filters/queryBuilder/NumberConstraint.d.ts +12 -0
  370. package/dist/filters/queryBuilder/NumberConstraint.d.ts.map +1 -0
  371. package/dist/filters/queryBuilder/NumberConstraint.js +61 -0
  372. package/dist/filters/queryBuilder/NumberConstraint.js.map +1 -0
  373. package/dist/filters/queryBuilder/SelectConstraint.d.ts +22 -0
  374. package/dist/filters/queryBuilder/SelectConstraint.d.ts.map +1 -0
  375. package/dist/filters/queryBuilder/SelectConstraint.js +66 -0
  376. package/dist/filters/queryBuilder/SelectConstraint.js.map +1 -0
  377. package/dist/filters/queryBuilder/TextConstraint.d.ts +18 -0
  378. package/dist/filters/queryBuilder/TextConstraint.d.ts.map +1 -0
  379. package/dist/filters/queryBuilder/TextConstraint.js +58 -0
  380. package/dist/filters/queryBuilder/TextConstraint.js.map +1 -0
  381. package/dist/filters/queryBuilder/index.d.ts +7 -0
  382. package/dist/filters/queryBuilder/index.d.ts.map +1 -0
  383. package/dist/filters/queryBuilder/index.js +7 -0
  384. package/dist/filters/queryBuilder/index.js.map +1 -0
  385. package/dist/icons/index.d.ts +3 -0
  386. package/dist/icons/index.d.ts.map +1 -0
  387. package/dist/icons/index.js +3 -0
  388. package/dist/icons/index.js.map +1 -0
  389. package/dist/icons/lucide.d.ts +16 -0
  390. package/dist/icons/lucide.d.ts.map +1 -0
  391. package/dist/icons/lucide.js +173 -0
  392. package/dist/icons/lucide.js.map +1 -0
  393. package/dist/icons/registry.d.ts +27 -0
  394. package/dist/icons/registry.d.ts.map +1 -0
  395. package/dist/icons/registry.js +35 -0
  396. package/dist/icons/registry.js.map +1 -0
  397. package/dist/icons/types.d.ts +38 -0
  398. package/dist/icons/types.d.ts.map +1 -0
  399. package/dist/icons/types.js +23 -0
  400. package/dist/icons/types.js.map +1 -0
  401. package/dist/index.d.ts +118 -0
  402. package/dist/index.d.ts.map +1 -0
  403. package/dist/index.js +135 -0
  404. package/dist/index.js.map +1 -0
  405. package/dist/io/csv.d.ts +51 -0
  406. package/dist/io/csv.d.ts.map +1 -0
  407. package/dist/io/csv.js +168 -0
  408. package/dist/io/csv.js.map +1 -0
  409. package/dist/notifications/Notification.d.ts +181 -0
  410. package/dist/notifications/Notification.d.ts.map +1 -0
  411. package/dist/notifications/Notification.js +290 -0
  412. package/dist/notifications/Notification.js.map +1 -0
  413. package/dist/notifications/broadcast.d.ts +58 -0
  414. package/dist/notifications/broadcast.d.ts.map +1 -0
  415. package/dist/notifications/broadcast.js +72 -0
  416. package/dist/notifications/broadcast.js.map +1 -0
  417. package/dist/notifications/database.d.ts +164 -0
  418. package/dist/notifications/database.d.ts.map +1 -0
  419. package/dist/notifications/database.js +321 -0
  420. package/dist/notifications/database.js.map +1 -0
  421. package/dist/notifications/dispatchNotificationAction.d.ts +48 -0
  422. package/dist/notifications/dispatchNotificationAction.d.ts.map +1 -0
  423. package/dist/notifications/dispatchNotificationAction.js +100 -0
  424. package/dist/notifications/dispatchNotificationAction.js.map +1 -0
  425. package/dist/notifications/flash.d.ts +34 -0
  426. package/dist/notifications/flash.d.ts.map +1 -0
  427. package/dist/notifications/flash.js +51 -0
  428. package/dist/notifications/flash.js.map +1 -0
  429. package/dist/notifications/index.d.ts +8 -0
  430. package/dist/notifications/index.d.ts.map +1 -0
  431. package/dist/notifications/index.js +6 -0
  432. package/dist/notifications/index.js.map +1 -0
  433. package/dist/notifications/registerBroadcastAuth.d.ts +45 -0
  434. package/dist/notifications/registerBroadcastAuth.d.ts.map +1 -0
  435. package/dist/notifications/registerBroadcastAuth.js +86 -0
  436. package/dist/notifications/registerBroadcastAuth.js.map +1 -0
  437. package/dist/notifications/resolveSavedNotification.d.ts +21 -0
  438. package/dist/notifications/resolveSavedNotification.d.ts.map +1 -0
  439. package/dist/notifications/resolveSavedNotification.js +43 -0
  440. package/dist/notifications/resolveSavedNotification.js.map +1 -0
  441. package/dist/notifications/types.d.ts +87 -0
  442. package/dist/notifications/types.d.ts.map +1 -0
  443. package/dist/notifications/types.js +2 -0
  444. package/dist/notifications/types.js.map +1 -0
  445. package/dist/orm/m2mAccessor.d.ts +49 -0
  446. package/dist/orm/m2mAccessor.d.ts.map +1 -0
  447. package/dist/orm/m2mAccessor.js +45 -0
  448. package/dist/orm/m2mAccessor.js.map +1 -0
  449. package/dist/orm/modelDefaults.d.ts +347 -0
  450. package/dist/orm/modelDefaults.d.ts.map +1 -0
  451. package/dist/orm/modelDefaults.js +375 -0
  452. package/dist/orm/modelDefaults.js.map +1 -0
  453. package/dist/pageData.d.ts +778 -0
  454. package/dist/pageData.d.ts.map +1 -0
  455. package/dist/pageData.js +3725 -0
  456. package/dist/pageData.js.map +1 -0
  457. package/dist/plugins/index.d.ts +2 -0
  458. package/dist/plugins/index.d.ts.map +1 -0
  459. package/dist/plugins/index.js +2 -0
  460. package/dist/plugins/index.js.map +1 -0
  461. package/dist/plugins/themeEditor.d.ts +17 -0
  462. package/dist/plugins/themeEditor.d.ts.map +1 -0
  463. package/dist/plugins/themeEditor.js +23 -0
  464. package/dist/plugins/themeEditor.js.map +1 -0
  465. package/dist/react/AppShell.d.ts +58 -0
  466. package/dist/react/AppShell.d.ts.map +1 -0
  467. package/dist/react/AppShell.js +58 -0
  468. package/dist/react/AppShell.js.map +1 -0
  469. package/dist/react/CommandPalette.d.ts +21 -0
  470. package/dist/react/CommandPalette.d.ts.map +1 -0
  471. package/dist/react/CommandPalette.js +236 -0
  472. package/dist/react/CommandPalette.js.map +1 -0
  473. package/dist/react/FormStateContext.d.ts +83 -0
  474. package/dist/react/FormStateContext.d.ts.map +1 -0
  475. package/dist/react/FormStateContext.js +284 -0
  476. package/dist/react/FormStateContext.js.map +1 -0
  477. package/dist/react/HeadHooks.d.ts +26 -0
  478. package/dist/react/HeadHooks.d.ts.map +1 -0
  479. package/dist/react/HeadHooks.js +141 -0
  480. package/dist/react/HeadHooks.js.map +1 -0
  481. package/dist/react/NotificationActionStrip.d.ts +39 -0
  482. package/dist/react/NotificationActionStrip.d.ts.map +1 -0
  483. package/dist/react/NotificationActionStrip.js +129 -0
  484. package/dist/react/NotificationActionStrip.js.map +1 -0
  485. package/dist/react/NotificationBell.d.ts +20 -0
  486. package/dist/react/NotificationBell.d.ts.map +1 -0
  487. package/dist/react/NotificationBell.js +273 -0
  488. package/dist/react/NotificationBell.js.map +1 -0
  489. package/dist/react/RenderHookSlot.d.ts +20 -0
  490. package/dist/react/RenderHookSlot.d.ts.map +1 -0
  491. package/dist/react/RenderHookSlot.js +24 -0
  492. package/dist/react/RenderHookSlot.js.map +1 -0
  493. package/dist/react/RightSidebar.d.ts +33 -0
  494. package/dist/react/RightSidebar.d.ts.map +1 -0
  495. package/dist/react/RightSidebar.js +82 -0
  496. package/dist/react/RightSidebar.js.map +1 -0
  497. package/dist/react/RightSidebarContext.d.ts +62 -0
  498. package/dist/react/RightSidebarContext.d.ts.map +1 -0
  499. package/dist/react/RightSidebarContext.js +178 -0
  500. package/dist/react/RightSidebarContext.js.map +1 -0
  501. package/dist/react/RightSidebarTrigger.d.ts +16 -0
  502. package/dist/react/RightSidebarTrigger.d.ts.map +1 -0
  503. package/dist/react/RightSidebarTrigger.js +24 -0
  504. package/dist/react/RightSidebarTrigger.js.map +1 -0
  505. package/dist/react/SchemaRenderer.d.ts +63 -0
  506. package/dist/react/SchemaRenderer.d.ts.map +1 -0
  507. package/dist/react/SchemaRenderer.js +3458 -0
  508. package/dist/react/SchemaRenderer.js.map +1 -0
  509. package/dist/react/SearchTrigger.d.ts +13 -0
  510. package/dist/react/SearchTrigger.d.ts.map +1 -0
  511. package/dist/react/SearchTrigger.js +30 -0
  512. package/dist/react/SearchTrigger.js.map +1 -0
  513. package/dist/react/ThemeProvider.d.ts +18 -0
  514. package/dist/react/ThemeProvider.d.ts.map +1 -0
  515. package/dist/react/ThemeProvider.js +66 -0
  516. package/dist/react/ThemeProvider.js.map +1 -0
  517. package/dist/react/ThemeSettingsPage.d.ts +10 -0
  518. package/dist/react/ThemeSettingsPage.d.ts.map +1 -0
  519. package/dist/react/ThemeSettingsPage.js +293 -0
  520. package/dist/react/ThemeSettingsPage.js.map +1 -0
  521. package/dist/react/ThemeToggle.d.ts +2 -0
  522. package/dist/react/ThemeToggle.d.ts.map +1 -0
  523. package/dist/react/ThemeToggle.js +8 -0
  524. package/dist/react/ThemeToggle.js.map +1 -0
  525. package/dist/react/Toaster.d.ts +25 -0
  526. package/dist/react/Toaster.d.ts.map +1 -0
  527. package/dist/react/Toaster.js +89 -0
  528. package/dist/react/Toaster.js.map +1 -0
  529. package/dist/react/UserMenu.d.ts +23 -0
  530. package/dist/react/UserMenu.d.ts.map +1 -0
  531. package/dist/react/UserMenu.js +78 -0
  532. package/dist/react/UserMenu.js.map +1 -0
  533. package/dist/react/WidgetDataContext.d.ts +64 -0
  534. package/dist/react/WidgetDataContext.d.ts.map +1 -0
  535. package/dist/react/WidgetDataContext.js +89 -0
  536. package/dist/react/WidgetDataContext.js.map +1 -0
  537. package/dist/react/cells/EditableCell.d.ts +20 -0
  538. package/dist/react/cells/EditableCell.d.ts.map +1 -0
  539. package/dist/react/cells/EditableCell.js +251 -0
  540. package/dist/react/cells/EditableCell.js.map +1 -0
  541. package/dist/react/fieldJsHandler.d.ts +33 -0
  542. package/dist/react/fieldJsHandler.d.ts.map +1 -0
  543. package/dist/react/fieldJsHandler.js +61 -0
  544. package/dist/react/fieldJsHandler.js.map +1 -0
  545. package/dist/react/fields/BuilderInput.d.ts +21 -0
  546. package/dist/react/fields/BuilderInput.d.ts.map +1 -0
  547. package/dist/react/fields/BuilderInput.js +553 -0
  548. package/dist/react/fields/BuilderInput.js.map +1 -0
  549. package/dist/react/fields/CheckboxInput.d.ts +9 -0
  550. package/dist/react/fields/CheckboxInput.d.ts.map +1 -0
  551. package/dist/react/fields/CheckboxInput.js +23 -0
  552. package/dist/react/fields/CheckboxInput.js.map +1 -0
  553. package/dist/react/fields/CheckboxListInput.d.ts +19 -0
  554. package/dist/react/fields/CheckboxListInput.d.ts.map +1 -0
  555. package/dist/react/fields/CheckboxListInput.js +53 -0
  556. package/dist/react/fields/CheckboxListInput.js.map +1 -0
  557. package/dist/react/fields/ColorInput.d.ts +12 -0
  558. package/dist/react/fields/ColorInput.d.ts.map +1 -0
  559. package/dist/react/fields/ColorInput.js +29 -0
  560. package/dist/react/fields/ColorInput.js.map +1 -0
  561. package/dist/react/fields/DateFieldInput.d.ts +8 -0
  562. package/dist/react/fields/DateFieldInput.d.ts.map +1 -0
  563. package/dist/react/fields/DateFieldInput.js +39 -0
  564. package/dist/react/fields/DateFieldInput.js.map +1 -0
  565. package/dist/react/fields/DateTimeInput.d.ts +13 -0
  566. package/dist/react/fields/DateTimeInput.d.ts.map +1 -0
  567. package/dist/react/fields/DateTimeInput.js +29 -0
  568. package/dist/react/fields/DateTimeInput.js.map +1 -0
  569. package/dist/react/fields/FieldShell.d.ts +23 -0
  570. package/dist/react/fields/FieldShell.d.ts.map +1 -0
  571. package/dist/react/fields/FieldShell.js +46 -0
  572. package/dist/react/fields/FieldShell.js.map +1 -0
  573. package/dist/react/fields/FileUploadInput.d.ts +21 -0
  574. package/dist/react/fields/FileUploadInput.d.ts.map +1 -0
  575. package/dist/react/fields/FileUploadInput.js +120 -0
  576. package/dist/react/fields/FileUploadInput.js.map +1 -0
  577. package/dist/react/fields/HiddenInput.d.ts +11 -0
  578. package/dist/react/fields/HiddenInput.d.ts.map +1 -0
  579. package/dist/react/fields/HiddenInput.js +14 -0
  580. package/dist/react/fields/HiddenInput.js.map +1 -0
  581. package/dist/react/fields/KeyValueInput.d.ts +18 -0
  582. package/dist/react/fields/KeyValueInput.d.ts.map +1 -0
  583. package/dist/react/fields/KeyValueInput.js +122 -0
  584. package/dist/react/fields/KeyValueInput.js.map +1 -0
  585. package/dist/react/fields/MarkdownInput.d.ts +29 -0
  586. package/dist/react/fields/MarkdownInput.d.ts.map +1 -0
  587. package/dist/react/fields/MarkdownInput.js +250 -0
  588. package/dist/react/fields/MarkdownInput.js.map +1 -0
  589. package/dist/react/fields/RadioInput.d.ts +18 -0
  590. package/dist/react/fields/RadioInput.d.ts.map +1 -0
  591. package/dist/react/fields/RadioInput.js +34 -0
  592. package/dist/react/fields/RadioInput.js.map +1 -0
  593. package/dist/react/fields/RepeaterInput.d.ts +92 -0
  594. package/dist/react/fields/RepeaterInput.d.ts.map +1 -0
  595. package/dist/react/fields/RepeaterInput.js +705 -0
  596. package/dist/react/fields/RepeaterInput.js.map +1 -0
  597. package/dist/react/fields/SelectFieldInput.d.ts +23 -0
  598. package/dist/react/fields/SelectFieldInput.d.ts.map +1 -0
  599. package/dist/react/fields/SelectFieldInput.js +146 -0
  600. package/dist/react/fields/SelectFieldInput.js.map +1 -0
  601. package/dist/react/fields/SliderInput.d.ts +16 -0
  602. package/dist/react/fields/SliderInput.d.ts.map +1 -0
  603. package/dist/react/fields/SliderInput.js +37 -0
  604. package/dist/react/fields/SliderInput.js.map +1 -0
  605. package/dist/react/fields/TagsInput.d.ts +27 -0
  606. package/dist/react/fields/TagsInput.d.ts.map +1 -0
  607. package/dist/react/fields/TagsInput.js +189 -0
  608. package/dist/react/fields/TagsInput.js.map +1 -0
  609. package/dist/react/fields/TextLikeInput.d.ts +18 -0
  610. package/dist/react/fields/TextLikeInput.d.ts.map +1 -0
  611. package/dist/react/fields/TextLikeInput.js +46 -0
  612. package/dist/react/fields/TextLikeInput.js.map +1 -0
  613. package/dist/react/fields/ToggleButtonsInput.d.ts +20 -0
  614. package/dist/react/fields/ToggleButtonsInput.d.ts.map +1 -0
  615. package/dist/react/fields/ToggleButtonsInput.js +42 -0
  616. package/dist/react/fields/ToggleButtonsInput.js.map +1 -0
  617. package/dist/react/fields/ToggleFieldInput.d.ts +7 -0
  618. package/dist/react/fields/ToggleFieldInput.d.ts.map +1 -0
  619. package/dist/react/fields/ToggleFieldInput.js +30 -0
  620. package/dist/react/fields/ToggleFieldInput.js.map +1 -0
  621. package/dist/react/fields/rowChromeButton.d.ts +84 -0
  622. package/dist/react/fields/rowChromeButton.d.ts.map +1 -0
  623. package/dist/react/fields/rowChromeButton.js +111 -0
  624. package/dist/react/fields/rowChromeButton.js.map +1 -0
  625. package/dist/react/fields/syncRowGates.d.ts +11 -0
  626. package/dist/react/fields/syncRowGates.d.ts.map +1 -0
  627. package/dist/react/fields/syncRowGates.js +55 -0
  628. package/dist/react/fields/syncRowGates.js.map +1 -0
  629. package/dist/react/formStateHelpers.d.ts +44 -0
  630. package/dist/react/formStateHelpers.d.ts.map +1 -0
  631. package/dist/react/formStateHelpers.js +230 -0
  632. package/dist/react/formStateHelpers.js.map +1 -0
  633. package/dist/react/hooks/use-mobile.d.ts +2 -0
  634. package/dist/react/hooks/use-mobile.d.ts.map +1 -0
  635. package/dist/react/hooks/use-mobile.js +16 -0
  636. package/dist/react/hooks/use-mobile.js.map +1 -0
  637. package/dist/react/icon-context.d.ts +35 -0
  638. package/dist/react/icon-context.d.ts.map +1 -0
  639. package/dist/react/icon-context.js +45 -0
  640. package/dist/react/icon-context.js.map +1 -0
  641. package/dist/react/index.d.ts +26 -0
  642. package/dist/react/index.d.ts.map +1 -0
  643. package/dist/react/index.js +28 -0
  644. package/dist/react/index.js.map +1 -0
  645. package/dist/react/layouts/SidebarLayout.d.ts +3 -0
  646. package/dist/react/layouts/SidebarLayout.d.ts.map +1 -0
  647. package/dist/react/layouts/SidebarLayout.js +85 -0
  648. package/dist/react/layouts/SidebarLayout.js.map +1 -0
  649. package/dist/react/layouts/TopbarLayout.d.ts +3 -0
  650. package/dist/react/layouts/TopbarLayout.d.ts.map +1 -0
  651. package/dist/react/layouts/TopbarLayout.js +103 -0
  652. package/dist/react/layouts/TopbarLayout.js.map +1 -0
  653. package/dist/react/navigate.d.ts +22 -0
  654. package/dist/react/navigate.d.ts.map +1 -0
  655. package/dist/react/navigate.js +30 -0
  656. package/dist/react/navigate.js.map +1 -0
  657. package/dist/react/registry.d.ts +35 -0
  658. package/dist/react/registry.d.ts.map +1 -0
  659. package/dist/react/registry.js +22 -0
  660. package/dist/react/registry.js.map +1 -0
  661. package/dist/react/right-panel-registry.d.ts +32 -0
  662. package/dist/react/right-panel-registry.d.ts.map +1 -0
  663. package/dist/react/right-panel-registry.js +20 -0
  664. package/dist/react/right-panel-registry.js.map +1 -0
  665. package/dist/react/theme-preview/apply.d.ts +11 -0
  666. package/dist/react/theme-preview/apply.d.ts.map +1 -0
  667. package/dist/react/theme-preview/apply.js +93 -0
  668. package/dist/react/theme-preview/apply.js.map +1 -0
  669. package/dist/react/theme-preview/build-html.d.ts +3 -0
  670. package/dist/react/theme-preview/build-html.d.ts.map +1 -0
  671. package/dist/react/theme-preview/build-html.js +437 -0
  672. package/dist/react/theme-preview/build-html.js.map +1 -0
  673. package/dist/react/ui/button.d.ts +9 -0
  674. package/dist/react/ui/button.d.ts.map +1 -0
  675. package/dist/react/ui/button.js +35 -0
  676. package/dist/react/ui/button.js.map +1 -0
  677. package/dist/react/ui/calendar.d.ts +5 -0
  678. package/dist/react/ui/calendar.d.ts.map +1 -0
  679. package/dist/react/ui/calendar.js +34 -0
  680. package/dist/react/ui/calendar.js.map +1 -0
  681. package/dist/react/ui/checkbox.d.ts +4 -0
  682. package/dist/react/ui/checkbox.d.ts.map +1 -0
  683. package/dist/react/ui/checkbox.js +9 -0
  684. package/dist/react/ui/checkbox.js.map +1 -0
  685. package/dist/react/ui/dialog.d.ts +12 -0
  686. package/dist/react/ui/dialog.d.ts.map +1 -0
  687. package/dist/react/ui/dialog.js +34 -0
  688. package/dist/react/ui/dialog.js.map +1 -0
  689. package/dist/react/ui/dropdown-menu.d.ts +12 -0
  690. package/dist/react/ui/dropdown-menu.d.ts.map +1 -0
  691. package/dist/react/ui/dropdown-menu.js +23 -0
  692. package/dist/react/ui/dropdown-menu.js.map +1 -0
  693. package/dist/react/ui/input.d.ts +4 -0
  694. package/dist/react/ui/input.d.ts.map +1 -0
  695. package/dist/react/ui/input.js +8 -0
  696. package/dist/react/ui/input.js.map +1 -0
  697. package/dist/react/ui/label.d.ts +4 -0
  698. package/dist/react/ui/label.d.ts.map +1 -0
  699. package/dist/react/ui/label.js +7 -0
  700. package/dist/react/ui/label.js.map +1 -0
  701. package/dist/react/ui/popover.d.ts +6 -0
  702. package/dist/react/ui/popover.d.ts.map +1 -0
  703. package/dist/react/ui/popover.js +14 -0
  704. package/dist/react/ui/popover.js.map +1 -0
  705. package/dist/react/ui/select.d.ts +17 -0
  706. package/dist/react/ui/select.d.ts.map +1 -0
  707. package/dist/react/ui/select.js +39 -0
  708. package/dist/react/ui/select.js.map +1 -0
  709. package/dist/react/ui/separator.d.ts +4 -0
  710. package/dist/react/ui/separator.d.ts.map +1 -0
  711. package/dist/react/ui/separator.js +9 -0
  712. package/dist/react/ui/separator.js.map +1 -0
  713. package/dist/react/ui/sheet.d.ts +15 -0
  714. package/dist/react/ui/sheet.d.ts.map +1 -0
  715. package/dist/react/ui/sheet.js +37 -0
  716. package/dist/react/ui/sheet.js.map +1 -0
  717. package/dist/react/ui/sidebar.d.ts +64 -0
  718. package/dist/react/ui/sidebar.d.ts.map +1 -0
  719. package/dist/react/ui/sidebar.js +257 -0
  720. package/dist/react/ui/sidebar.js.map +1 -0
  721. package/dist/react/ui/skeleton.d.ts +3 -0
  722. package/dist/react/ui/skeleton.d.ts.map +1 -0
  723. package/dist/react/ui/skeleton.js +7 -0
  724. package/dist/react/ui/skeleton.js.map +1 -0
  725. package/dist/react/ui/slider.d.ts +4 -0
  726. package/dist/react/ui/slider.d.ts.map +1 -0
  727. package/dist/react/ui/slider.js +8 -0
  728. package/dist/react/ui/slider.js.map +1 -0
  729. package/dist/react/ui/switch.d.ts +4 -0
  730. package/dist/react/ui/switch.d.ts.map +1 -0
  731. package/dist/react/ui/switch.js +8 -0
  732. package/dist/react/ui/switch.js.map +1 -0
  733. package/dist/react/ui/table.d.ts +11 -0
  734. package/dist/react/ui/table.d.ts.map +1 -0
  735. package/dist/react/ui/table.js +28 -0
  736. package/dist/react/ui/table.js.map +1 -0
  737. package/dist/react/ui/tabs.d.ts +7 -0
  738. package/dist/react/ui/tabs.d.ts.map +1 -0
  739. package/dist/react/ui/tabs.js +17 -0
  740. package/dist/react/ui/tabs.js.map +1 -0
  741. package/dist/react/ui/textarea.d.ts +4 -0
  742. package/dist/react/ui/textarea.d.ts.map +1 -0
  743. package/dist/react/ui/textarea.js +7 -0
  744. package/dist/react/ui/textarea.js.map +1 -0
  745. package/dist/react/ui/tooltip.d.ts +7 -0
  746. package/dist/react/ui/tooltip.d.ts.map +1 -0
  747. package/dist/react/ui/tooltip.js +17 -0
  748. package/dist/react/ui/tooltip.js.map +1 -0
  749. package/dist/react/useResizableWidth.d.ts +47 -0
  750. package/dist/react/useResizableWidth.d.ts.map +1 -0
  751. package/dist/react/useResizableWidth.js +99 -0
  752. package/dist/react/useResizableWidth.js.map +1 -0
  753. package/dist/react/utils.d.ts +3 -0
  754. package/dist/react/utils.d.ts.map +1 -0
  755. package/dist/react/utils.js +6 -0
  756. package/dist/react/utils.js.map +1 -0
  757. package/dist/react/widgetRegistry.d.ts +33 -0
  758. package/dist/react/widgetRegistry.d.ts.map +1 -0
  759. package/dist/react/widgetRegistry.js +15 -0
  760. package/dist/react/widgetRegistry.js.map +1 -0
  761. package/dist/react/widgets/StatsOverviewRenderer.d.ts +6 -0
  762. package/dist/react/widgets/StatsOverviewRenderer.d.ts.map +1 -0
  763. package/dist/react/widgets/StatsOverviewRenderer.js +124 -0
  764. package/dist/react/widgets/StatsOverviewRenderer.js.map +1 -0
  765. package/dist/react/widgets/TableWidgetRenderer.d.ts +6 -0
  766. package/dist/react/widgets/TableWidgetRenderer.d.ts.map +1 -0
  767. package/dist/react/widgets/TableWidgetRenderer.js +123 -0
  768. package/dist/react/widgets/TableWidgetRenderer.js.map +1 -0
  769. package/dist/react/widgets/ViewRenderer.d.ts +16 -0
  770. package/dist/react/widgets/ViewRenderer.d.ts.map +1 -0
  771. package/dist/react/widgets/ViewRenderer.js +26 -0
  772. package/dist/react/widgets/ViewRenderer.js.map +1 -0
  773. package/dist/richtext/index.d.ts +2 -0
  774. package/dist/richtext/index.d.ts.map +1 -0
  775. package/dist/richtext/index.js +2 -0
  776. package/dist/richtext/index.js.map +1 -0
  777. package/dist/richtext/registry.d.ts +55 -0
  778. package/dist/richtext/registry.d.ts.map +1 -0
  779. package/dist/richtext/registry.js +66 -0
  780. package/dist/richtext/registry.js.map +1 -0
  781. package/dist/routes.d.ts +9 -0
  782. package/dist/routes.d.ts.map +1 -0
  783. package/dist/routes.js +3116 -0
  784. package/dist/routes.js.map +1 -0
  785. package/dist/schema/Alert.d.ts +33 -0
  786. package/dist/schema/Alert.d.ts.map +1 -0
  787. package/dist/schema/Alert.js +41 -0
  788. package/dist/schema/Alert.js.map +1 -0
  789. package/dist/schema/Block.d.ts +112 -0
  790. package/dist/schema/Block.d.ts.map +1 -0
  791. package/dist/schema/Block.js +136 -0
  792. package/dist/schema/Block.js.map +1 -0
  793. package/dist/schema/Breadcrumbs.d.ts +31 -0
  794. package/dist/schema/Breadcrumbs.d.ts.map +1 -0
  795. package/dist/schema/Breadcrumbs.js +30 -0
  796. package/dist/schema/Breadcrumbs.js.map +1 -0
  797. package/dist/schema/Card.d.ts +17 -0
  798. package/dist/schema/Card.d.ts.map +1 -0
  799. package/dist/schema/Card.js +31 -0
  800. package/dist/schema/Card.js.map +1 -0
  801. package/dist/schema/Divider.d.ts +12 -0
  802. package/dist/schema/Divider.d.ts.map +1 -0
  803. package/dist/schema/Divider.js +19 -0
  804. package/dist/schema/Divider.js.map +1 -0
  805. package/dist/schema/Element.d.ts +150 -0
  806. package/dist/schema/Element.d.ts.map +1 -0
  807. package/dist/schema/Element.js +124 -0
  808. package/dist/schema/Element.js.map +1 -0
  809. package/dist/schema/EmptyState.d.ts +48 -0
  810. package/dist/schema/EmptyState.d.ts.map +1 -0
  811. package/dist/schema/EmptyState.js +57 -0
  812. package/dist/schema/EmptyState.js.map +1 -0
  813. package/dist/schema/Fieldset.d.ts +25 -0
  814. package/dist/schema/Fieldset.d.ts.map +1 -0
  815. package/dist/schema/Fieldset.js +39 -0
  816. package/dist/schema/Fieldset.js.map +1 -0
  817. package/dist/schema/Grid.d.ts +23 -0
  818. package/dist/schema/Grid.d.ts.map +1 -0
  819. package/dist/schema/Grid.js +36 -0
  820. package/dist/schema/Grid.js.map +1 -0
  821. package/dist/schema/Group.d.ts +19 -0
  822. package/dist/schema/Group.d.ts.map +1 -0
  823. package/dist/schema/Group.js +26 -0
  824. package/dist/schema/Group.js.map +1 -0
  825. package/dist/schema/Heading.d.ts +25 -0
  826. package/dist/schema/Heading.d.ts.map +1 -0
  827. package/dist/schema/Heading.js +34 -0
  828. package/dist/schema/Heading.js.map +1 -0
  829. package/dist/schema/Html.d.ts +48 -0
  830. package/dist/schema/Html.d.ts.map +1 -0
  831. package/dist/schema/Html.js +60 -0
  832. package/dist/schema/Html.js.map +1 -0
  833. package/dist/schema/Icon.d.ts +34 -0
  834. package/dist/schema/Icon.d.ts.map +1 -0
  835. package/dist/schema/Icon.js +40 -0
  836. package/dist/schema/Icon.js.map +1 -0
  837. package/dist/schema/Image.d.ts +38 -0
  838. package/dist/schema/Image.d.ts.map +1 -0
  839. package/dist/schema/Image.js +48 -0
  840. package/dist/schema/Image.js.map +1 -0
  841. package/dist/schema/LinkTag.d.ts +48 -0
  842. package/dist/schema/LinkTag.d.ts.map +1 -0
  843. package/dist/schema/LinkTag.js +16 -0
  844. package/dist/schema/LinkTag.js.map +1 -0
  845. package/dist/schema/Markdown.d.ts +57 -0
  846. package/dist/schema/Markdown.d.ts.map +1 -0
  847. package/dist/schema/Markdown.js +75 -0
  848. package/dist/schema/Markdown.js.map +1 -0
  849. package/dist/schema/MetaTag.d.ts +41 -0
  850. package/dist/schema/MetaTag.d.ts.map +1 -0
  851. package/dist/schema/MetaTag.js +16 -0
  852. package/dist/schema/MetaTag.js.map +1 -0
  853. package/dist/schema/RelationTabs.d.ts +50 -0
  854. package/dist/schema/RelationTabs.d.ts.map +1 -0
  855. package/dist/schema/RelationTabs.js +48 -0
  856. package/dist/schema/RelationTabs.js.map +1 -0
  857. package/dist/schema/ScriptTag.d.ts +63 -0
  858. package/dist/schema/ScriptTag.d.ts.map +1 -0
  859. package/dist/schema/ScriptTag.js +16 -0
  860. package/dist/schema/ScriptTag.js.map +1 -0
  861. package/dist/schema/Section.d.ts +93 -0
  862. package/dist/schema/Section.d.ts.map +1 -0
  863. package/dist/schema/Section.js +127 -0
  864. package/dist/schema/Section.js.map +1 -0
  865. package/dist/schema/ServerDataElement.d.ts +101 -0
  866. package/dist/schema/ServerDataElement.d.ts.map +1 -0
  867. package/dist/schema/ServerDataElement.js +135 -0
  868. package/dist/schema/ServerDataElement.js.map +1 -0
  869. package/dist/schema/Split.d.ts +31 -0
  870. package/dist/schema/Split.d.ts.map +1 -0
  871. package/dist/schema/Split.js +41 -0
  872. package/dist/schema/Split.js.map +1 -0
  873. package/dist/schema/Stat.d.ts +92 -0
  874. package/dist/schema/Stat.d.ts.map +1 -0
  875. package/dist/schema/Stat.js +116 -0
  876. package/dist/schema/Stat.js.map +1 -0
  877. package/dist/schema/StatsOverview.d.ts +76 -0
  878. package/dist/schema/StatsOverview.d.ts.map +1 -0
  879. package/dist/schema/StatsOverview.js +71 -0
  880. package/dist/schema/StatsOverview.js.map +1 -0
  881. package/dist/schema/StyleTag.d.ts +32 -0
  882. package/dist/schema/StyleTag.d.ts.map +1 -0
  883. package/dist/schema/StyleTag.js +38 -0
  884. package/dist/schema/StyleTag.js.map +1 -0
  885. package/dist/schema/TableWidget.d.ts +148 -0
  886. package/dist/schema/TableWidget.d.ts.map +1 -0
  887. package/dist/schema/TableWidget.js +190 -0
  888. package/dist/schema/TableWidget.js.map +1 -0
  889. package/dist/schema/Tabs.d.ts +40 -0
  890. package/dist/schema/Tabs.d.ts.map +1 -0
  891. package/dist/schema/Tabs.js +66 -0
  892. package/dist/schema/Tabs.js.map +1 -0
  893. package/dist/schema/Text.d.ts +33 -0
  894. package/dist/schema/Text.d.ts.map +1 -0
  895. package/dist/schema/Text.js +40 -0
  896. package/dist/schema/Text.js.map +1 -0
  897. package/dist/schema/UnorderedList.d.ts +36 -0
  898. package/dist/schema/UnorderedList.d.ts.map +1 -0
  899. package/dist/schema/UnorderedList.js +42 -0
  900. package/dist/schema/UnorderedList.js.map +1 -0
  901. package/dist/schema/View.d.ts +81 -0
  902. package/dist/schema/View.d.ts.map +1 -0
  903. package/dist/schema/View.js +81 -0
  904. package/dist/schema/View.js.map +1 -0
  905. package/dist/schema/Wizard.d.ts +67 -0
  906. package/dist/schema/Wizard.d.ts.map +1 -0
  907. package/dist/schema/Wizard.js +94 -0
  908. package/dist/schema/Wizard.js.map +1 -0
  909. package/dist/schema/index.d.ts +26 -0
  910. package/dist/schema/index.d.ts.map +1 -0
  911. package/dist/schema/index.js +26 -0
  912. package/dist/schema/index.js.map +1 -0
  913. package/dist/schema/resolveSchema.d.ts +122 -0
  914. package/dist/schema/resolveSchema.d.ts.map +1 -0
  915. package/dist/schema/resolveSchema.js +648 -0
  916. package/dist/schema/resolveSchema.js.map +1 -0
  917. package/dist/schema/sanitize.d.ts +21 -0
  918. package/dist/schema/sanitize.d.ts.map +1 -0
  919. package/dist/schema/sanitize.js +46 -0
  920. package/dist/schema/sanitize.js.map +1 -0
  921. package/dist/search.d.ts +53 -0
  922. package/dist/search.d.ts.map +1 -0
  923. package/dist/search.js +114 -0
  924. package/dist/search.js.map +1 -0
  925. package/dist/sessionFilters.d.ts +8 -0
  926. package/dist/sessionFilters.d.ts.map +1 -0
  927. package/dist/sessionFilters.js +115 -0
  928. package/dist/sessionFilters.js.map +1 -0
  929. package/dist/summarizers/Summarizer.d.ts +65 -0
  930. package/dist/summarizers/Summarizer.d.ts.map +1 -0
  931. package/dist/summarizers/Summarizer.js +98 -0
  932. package/dist/summarizers/Summarizer.js.map +1 -0
  933. package/dist/summarizers/index.d.ts +2 -0
  934. package/dist/summarizers/index.d.ts.map +1 -0
  935. package/dist/summarizers/index.js +2 -0
  936. package/dist/summarizers/index.js.map +1 -0
  937. package/dist/theme/base-colors.d.ts +3 -0
  938. package/dist/theme/base-colors.d.ts.map +1 -0
  939. package/dist/theme/base-colors.js +64 -0
  940. package/dist/theme/base-colors.js.map +1 -0
  941. package/dist/theme/chart-colors.d.ts +3 -0
  942. package/dist/theme/chart-colors.d.ts.map +1 -0
  943. package/dist/theme/chart-colors.js +46 -0
  944. package/dist/theme/chart-colors.js.map +1 -0
  945. package/dist/theme/colors.d.ts +56 -0
  946. package/dist/theme/colors.d.ts.map +1 -0
  947. package/dist/theme/colors.js +410 -0
  948. package/dist/theme/colors.js.map +1 -0
  949. package/dist/theme/generate-css.d.ts +9 -0
  950. package/dist/theme/generate-css.d.ts.map +1 -0
  951. package/dist/theme/generate-css.js +36 -0
  952. package/dist/theme/generate-css.js.map +1 -0
  953. package/dist/theme/generate-scale.d.ts +3 -0
  954. package/dist/theme/generate-scale.d.ts.map +1 -0
  955. package/dist/theme/generate-scale.js +89 -0
  956. package/dist/theme/generate-scale.js.map +1 -0
  957. package/dist/theme/icon-map.d.ts +9 -0
  958. package/dist/theme/icon-map.d.ts.map +1 -0
  959. package/dist/theme/icon-map.js +40 -0
  960. package/dist/theme/icon-map.js.map +1 -0
  961. package/dist/theme/index.d.ts +15 -0
  962. package/dist/theme/index.d.ts.map +1 -0
  963. package/dist/theme/index.js +13 -0
  964. package/dist/theme/index.js.map +1 -0
  965. package/dist/theme/migrate.d.ts +14 -0
  966. package/dist/theme/migrate.d.ts.map +1 -0
  967. package/dist/theme/migrate.js +79 -0
  968. package/dist/theme/migrate.js.map +1 -0
  969. package/dist/theme/presets.d.ts +30 -0
  970. package/dist/theme/presets.d.ts.map +1 -0
  971. package/dist/theme/presets.js +128 -0
  972. package/dist/theme/presets.js.map +1 -0
  973. package/dist/theme/radius.d.ts +11 -0
  974. package/dist/theme/radius.d.ts.map +1 -0
  975. package/dist/theme/radius.js +17 -0
  976. package/dist/theme/radius.js.map +1 -0
  977. package/dist/theme/resolve.d.ts +13 -0
  978. package/dist/theme/resolve.d.ts.map +1 -0
  979. package/dist/theme/resolve.js +91 -0
  980. package/dist/theme/resolve.js.map +1 -0
  981. package/dist/theme/spacing.d.ts +14 -0
  982. package/dist/theme/spacing.d.ts.map +1 -0
  983. package/dist/theme/spacing.js +17 -0
  984. package/dist/theme/spacing.js.map +1 -0
  985. package/dist/theme/theme-colors.d.ts +9 -0
  986. package/dist/theme/theme-colors.d.ts.map +1 -0
  987. package/dist/theme/theme-colors.js +84 -0
  988. package/dist/theme/theme-colors.js.map +1 -0
  989. package/dist/theme/types.d.ts +94 -0
  990. package/dist/theme/types.d.ts.map +1 -0
  991. package/dist/theme/types.js +2 -0
  992. package/dist/theme/types.js.map +1 -0
  993. package/dist/uploads/UploadAdapter.d.ts +34 -0
  994. package/dist/uploads/UploadAdapter.d.ts.map +1 -0
  995. package/dist/uploads/UploadAdapter.js +2 -0
  996. package/dist/uploads/UploadAdapter.js.map +1 -0
  997. package/dist/uploads/index.d.ts +3 -0
  998. package/dist/uploads/index.d.ts.map +1 -0
  999. package/dist/uploads/index.js +2 -0
  1000. package/dist/uploads/index.js.map +1 -0
  1001. package/dist/uploads/localUpload.d.ts +25 -0
  1002. package/dist/uploads/localUpload.d.ts.map +1 -0
  1003. package/dist/uploads/localUpload.js +65 -0
  1004. package/dist/uploads/localUpload.js.map +1 -0
  1005. package/dist/validation/Validator.d.ts +40 -0
  1006. package/dist/validation/Validator.d.ts.map +1 -0
  1007. package/dist/validation/Validator.js +25 -0
  1008. package/dist/validation/Validator.js.map +1 -0
  1009. package/dist/validation/index.d.ts +5 -0
  1010. package/dist/validation/index.d.ts.map +1 -0
  1011. package/dist/validation/index.js +5 -0
  1012. package/dist/validation/index.js.map +1 -0
  1013. package/dist/validation/rules.d.ts +9 -0
  1014. package/dist/validation/rules.d.ts.map +1 -0
  1015. package/dist/validation/rules.js +61 -0
  1016. package/dist/validation/rules.js.map +1 -0
  1017. package/dist/validation/runValidators.d.ts +30 -0
  1018. package/dist/validation/runValidators.d.ts.map +1 -0
  1019. package/dist/validation/runValidators.js +438 -0
  1020. package/dist/validation/runValidators.js.map +1 -0
  1021. package/dist/validation/uniqueValidator.d.ts +61 -0
  1022. package/dist/validation/uniqueValidator.d.ts.map +1 -0
  1023. package/dist/validation/uniqueValidator.js +80 -0
  1024. package/dist/validation/uniqueValidator.js.map +1 -0
  1025. package/dist/vite.d.ts +19 -0
  1026. package/dist/vite.d.ts.map +1 -0
  1027. package/dist/vite.js +696 -0
  1028. package/dist/vite.js.map +1 -0
  1029. package/dist/widgets/index.d.ts +2 -0
  1030. package/dist/widgets/index.d.ts.map +1 -0
  1031. package/dist/widgets/index.js +7 -0
  1032. package/dist/widgets/index.js.map +1 -0
  1033. package/dist/widgets/registry.d.ts +32 -0
  1034. package/dist/widgets/registry.d.ts.map +1 -0
  1035. package/dist/widgets/registry.js +17 -0
  1036. package/dist/widgets/registry.js.map +1 -0
  1037. package/package.json +101 -0
  1038. package/src/Cluster.test.ts +283 -0
  1039. package/src/Cluster.ts +83 -0
  1040. package/src/Column.test.ts +140 -0
  1041. package/src/Column.ts +612 -0
  1042. package/src/Global.test.ts +367 -0
  1043. package/src/Global.ts +169 -0
  1044. package/src/Page.test.ts +50 -0
  1045. package/src/Page.ts +139 -0
  1046. package/src/Pilotiq.test.ts +47 -0
  1047. package/src/Pilotiq.ts +705 -0
  1048. package/src/PilotiqRegistry.ts +36 -0
  1049. package/src/PilotiqServiceProvider.ts +69 -0
  1050. package/src/RelationManager.test.ts +400 -0
  1051. package/src/RelationManager.ts +527 -0
  1052. package/src/RenderHook.test.ts +252 -0
  1053. package/src/RenderHook.ts +226 -0
  1054. package/src/Resource.test.ts +240 -0
  1055. package/src/Resource.ts +439 -0
  1056. package/src/RightPanel.test.ts +202 -0
  1057. package/src/RightPanel.ts +132 -0
  1058. package/src/Tab.test.ts +91 -0
  1059. package/src/Tab.ts +156 -0
  1060. package/src/UserMenuItem.ts +145 -0
  1061. package/src/actions/Action.test.ts +2479 -0
  1062. package/src/actions/Action.ts +2124 -0
  1063. package/src/actions/ActionGroup.test.ts +112 -0
  1064. package/src/actions/ActionGroup.ts +173 -0
  1065. package/src/actions/attachFactory.ts +172 -0
  1066. package/src/actions/exportFactory.ts +215 -0
  1067. package/src/actions/importFactory.ts +222 -0
  1068. package/src/actions/index.ts +17 -0
  1069. package/src/applyPageHooks.test.ts +298 -0
  1070. package/src/applyPageHooks.ts +242 -0
  1071. package/src/authorization.test.ts +483 -0
  1072. package/src/breadcrumbs.test.ts +238 -0
  1073. package/src/cells/coerce.test.ts +85 -0
  1074. package/src/cells/coerce.ts +84 -0
  1075. package/src/clusterPaths.ts +35 -0
  1076. package/src/columns/BadgeColumn.test.ts +54 -0
  1077. package/src/columns/BadgeColumn.ts +32 -0
  1078. package/src/columns/BooleanColumn.test.ts +41 -0
  1079. package/src/columns/BooleanColumn.ts +18 -0
  1080. package/src/columns/ColorColumn.test.ts +37 -0
  1081. package/src/columns/ColorColumn.ts +38 -0
  1082. package/src/columns/IconColumn.test.ts +54 -0
  1083. package/src/columns/IconColumn.ts +37 -0
  1084. package/src/columns/ImageColumn.test.ts +41 -0
  1085. package/src/columns/ImageColumn.ts +28 -0
  1086. package/src/columns/SelectColumn.ts +60 -0
  1087. package/src/columns/TextColumn.test.ts +190 -0
  1088. package/src/columns/TextColumn.ts +20 -0
  1089. package/src/columns/TextInputColumn.ts +68 -0
  1090. package/src/columns/ToggleColumn.ts +46 -0
  1091. package/src/columns/editableColumns.test.ts +193 -0
  1092. package/src/columns/index.ts +9 -0
  1093. package/src/defaultGlobalPages.ts +95 -0
  1094. package/src/defaultPages.test.ts +634 -0
  1095. package/src/defaultPages.ts +614 -0
  1096. package/src/defaultViewPage.test.ts +147 -0
  1097. package/src/elements/Form.test.ts +223 -0
  1098. package/src/elements/Form.ts +397 -0
  1099. package/src/elements/ListTabs.ts +28 -0
  1100. package/src/elements/Table.test.ts +422 -0
  1101. package/src/elements/Table.ts +816 -0
  1102. package/src/elements/TableGroup.test.ts +149 -0
  1103. package/src/elements/TableGroup.ts +199 -0
  1104. package/src/elements/dispatchAction.test.ts +463 -0
  1105. package/src/elements/dispatchAction.ts +355 -0
  1106. package/src/elements/dispatchForm.test.ts +455 -0
  1107. package/src/elements/dispatchForm.ts +1855 -0
  1108. package/src/elements/dispatchTable.test.ts +1247 -0
  1109. package/src/elements/dispatchTable.ts +666 -0
  1110. package/src/elements/index.ts +21 -0
  1111. package/src/entries/BadgeEntry.ts +39 -0
  1112. package/src/entries/CodeEntry.test.ts +40 -0
  1113. package/src/entries/CodeEntry.ts +52 -0
  1114. package/src/entries/ColorEntry.ts +63 -0
  1115. package/src/entries/ComponentEntry.test.ts +173 -0
  1116. package/src/entries/ComponentEntry.ts +95 -0
  1117. package/src/entries/Entry.ts +304 -0
  1118. package/src/entries/IconEntry.ts +49 -0
  1119. package/src/entries/ImageEntry.ts +61 -0
  1120. package/src/entries/KeyValueEntry.ts +47 -0
  1121. package/src/entries/RepeatableEntry.test.ts +239 -0
  1122. package/src/entries/RepeatableEntry.ts +173 -0
  1123. package/src/entries/TextEntry.test.ts +394 -0
  1124. package/src/entries/TextEntry.ts +60 -0
  1125. package/src/entries/index.ts +12 -0
  1126. package/src/entries/leaves.test.ts +306 -0
  1127. package/src/entries/registry.ts +54 -0
  1128. package/src/fields/BuilderField.test.ts +1188 -0
  1129. package/src/fields/BuilderField.ts +568 -0
  1130. package/src/fields/BuilderRelationship.test.ts +811 -0
  1131. package/src/fields/CheckboxField.test.ts +44 -0
  1132. package/src/fields/CheckboxField.ts +27 -0
  1133. package/src/fields/CheckboxListField.test.ts +99 -0
  1134. package/src/fields/CheckboxListField.ts +66 -0
  1135. package/src/fields/ColorPickerField.test.ts +33 -0
  1136. package/src/fields/ColorPickerField.ts +25 -0
  1137. package/src/fields/DateField.ts +54 -0
  1138. package/src/fields/DateTimeField.test.ts +55 -0
  1139. package/src/fields/EmailField.ts +16 -0
  1140. package/src/fields/Field.test.ts +639 -0
  1141. package/src/fields/Field.ts +773 -0
  1142. package/src/fields/FileUploadField.test.ts +97 -0
  1143. package/src/fields/FileUploadField.ts +71 -0
  1144. package/src/fields/HiddenField.test.ts +27 -0
  1145. package/src/fields/HiddenField.ts +28 -0
  1146. package/src/fields/KeyValueField.test.ts +105 -0
  1147. package/src/fields/KeyValueField.ts +55 -0
  1148. package/src/fields/MarkdownField.test.ts +167 -0
  1149. package/src/fields/MarkdownField.ts +151 -0
  1150. package/src/fields/NumberField.ts +33 -0
  1151. package/src/fields/RadioField.test.ts +94 -0
  1152. package/src/fields/RadioField.ts +67 -0
  1153. package/src/fields/RepeaterField.test.ts +1806 -0
  1154. package/src/fields/RepeaterField.ts +791 -0
  1155. package/src/fields/RepeaterRelationship.test.ts +1630 -0
  1156. package/src/fields/RepeaterSimple.test.ts +248 -0
  1157. package/src/fields/RowButton.test.ts +149 -0
  1158. package/src/fields/RowButton.ts +125 -0
  1159. package/src/fields/SelectField.test.ts +192 -0
  1160. package/src/fields/SelectField.ts +235 -0
  1161. package/src/fields/SliderField.test.ts +50 -0
  1162. package/src/fields/SliderField.ts +53 -0
  1163. package/src/fields/SlugField.ts +24 -0
  1164. package/src/fields/TagsInputField.test.ts +154 -0
  1165. package/src/fields/TagsInputField.ts +133 -0
  1166. package/src/fields/TextField.ts +24 -0
  1167. package/src/fields/TextareaField.test.ts +58 -0
  1168. package/src/fields/TextareaField.ts +59 -0
  1169. package/src/fields/ToggleButtonsField.test.ts +106 -0
  1170. package/src/fields/ToggleButtonsField.ts +59 -0
  1171. package/src/fields/ToggleField.ts +16 -0
  1172. package/src/fields/disableOptionsWhenSelectedInSiblingRepeaterItems.test.ts +319 -0
  1173. package/src/fields/optionsResolver.ts +95 -0
  1174. package/src/fields/resolveField.ts +28 -0
  1175. package/src/filters/BooleanFilter.ts +35 -0
  1176. package/src/filters/DateRangeFilter.test.ts +194 -0
  1177. package/src/filters/DateRangeFilter.ts +148 -0
  1178. package/src/filters/Filter.test.ts +268 -0
  1179. package/src/filters/Filter.ts +184 -0
  1180. package/src/filters/FormFilter.test.ts +238 -0
  1181. package/src/filters/FormFilter.ts +215 -0
  1182. package/src/filters/MultiSelectFilter.test.ts +119 -0
  1183. package/src/filters/MultiSelectFilter.ts +78 -0
  1184. package/src/filters/QueryBuilderFilter.test.ts +644 -0
  1185. package/src/filters/QueryBuilderFilter.ts +398 -0
  1186. package/src/filters/SelectFilter.ts +46 -0
  1187. package/src/filters/TernaryFilter.test.ts +160 -0
  1188. package/src/filters/TernaryFilter.ts +72 -0
  1189. package/src/filters/TrashedFilter.test.ts +149 -0
  1190. package/src/filters/TrashedFilter.ts +55 -0
  1191. package/src/filters/queryBuilder/BooleanConstraint.ts +31 -0
  1192. package/src/filters/queryBuilder/Constraint.ts +115 -0
  1193. package/src/filters/queryBuilder/DateConstraint.ts +69 -0
  1194. package/src/filters/queryBuilder/NumberConstraint.ts +66 -0
  1195. package/src/filters/queryBuilder/SelectConstraint.ts +72 -0
  1196. package/src/filters/queryBuilder/TextConstraint.ts +65 -0
  1197. package/src/filters/queryBuilder/index.ts +12 -0
  1198. package/src/icons/index.ts +2 -0
  1199. package/src/icons/lucide.ts +204 -0
  1200. package/src/icons/registry.test.ts +56 -0
  1201. package/src/icons/registry.ts +41 -0
  1202. package/src/icons/types.ts +47 -0
  1203. package/src/index.ts +521 -0
  1204. package/src/io/csv.test.ts +142 -0
  1205. package/src/io/csv.ts +170 -0
  1206. package/src/nestedRelationManagerData.test.ts +526 -0
  1207. package/src/notifications/Notification.test.ts +210 -0
  1208. package/src/notifications/Notification.ts +354 -0
  1209. package/src/notifications/broadcast.test.ts +110 -0
  1210. package/src/notifications/broadcast.ts +95 -0
  1211. package/src/notifications/database.test.ts +383 -0
  1212. package/src/notifications/database.ts +398 -0
  1213. package/src/notifications/databaseNotifications.test.ts +187 -0
  1214. package/src/notifications/dispatchNotificationAction.test.ts +341 -0
  1215. package/src/notifications/dispatchNotificationAction.ts +142 -0
  1216. package/src/notifications/flash.test.ts +89 -0
  1217. package/src/notifications/flash.ts +71 -0
  1218. package/src/notifications/index.ts +45 -0
  1219. package/src/notifications/registerBroadcastAuth.test.ts +134 -0
  1220. package/src/notifications/registerBroadcastAuth.ts +100 -0
  1221. package/src/notifications/resolveSavedNotification.test.ts +82 -0
  1222. package/src/notifications/resolveSavedNotification.ts +59 -0
  1223. package/src/notifications/types.ts +93 -0
  1224. package/src/orm/m2mAccessor.ts +66 -0
  1225. package/src/orm/modelDefaults.test.ts +633 -0
  1226. package/src/orm/modelDefaults.ts +632 -0
  1227. package/src/pageData.test.ts +1121 -0
  1228. package/src/pageData.ts +4662 -0
  1229. package/src/plugins/index.ts +1 -0
  1230. package/src/plugins/themeEditor.ts +24 -0
  1231. package/src/react/AppShell.tsx +148 -0
  1232. package/src/react/CommandPalette.tsx +375 -0
  1233. package/src/react/FormStateContext.tsx +398 -0
  1234. package/src/react/HeadHooks.tsx +126 -0
  1235. package/src/react/NotificationActionStrip.tsx +263 -0
  1236. package/src/react/NotificationBell.tsx +426 -0
  1237. package/src/react/RenderHookSlot.tsx +32 -0
  1238. package/src/react/RightSidebar.tsx +257 -0
  1239. package/src/react/RightSidebarContext.tsx +211 -0
  1240. package/src/react/RightSidebarTrigger.tsx +53 -0
  1241. package/src/react/SchemaRenderer.tsx +6128 -0
  1242. package/src/react/SearchTrigger.tsx +46 -0
  1243. package/src/react/ThemeProvider.tsx +93 -0
  1244. package/src/react/ThemeSettingsPage.tsx +579 -0
  1245. package/src/react/ThemeToggle.tsx +20 -0
  1246. package/src/react/Toaster.tsx +158 -0
  1247. package/src/react/UserMenu.tsx +196 -0
  1248. package/src/react/WidgetDataContext.tsx +157 -0
  1249. package/src/react/cells/EditableCell.tsx +376 -0
  1250. package/src/react/fieldJsHandler.test.ts +166 -0
  1251. package/src/react/fieldJsHandler.ts +79 -0
  1252. package/src/react/fields/BuilderInput.tsx +995 -0
  1253. package/src/react/fields/CheckboxInput.tsx +39 -0
  1254. package/src/react/fields/CheckboxListInput.tsx +81 -0
  1255. package/src/react/fields/ColorInput.tsx +51 -0
  1256. package/src/react/fields/DateFieldInput.tsx +70 -0
  1257. package/src/react/fields/DateTimeInput.tsx +42 -0
  1258. package/src/react/fields/FieldShell.tsx +107 -0
  1259. package/src/react/fields/FileUploadInput.tsx +189 -0
  1260. package/src/react/fields/HiddenInput.tsx +17 -0
  1261. package/src/react/fields/KeyValueInput.tsx +200 -0
  1262. package/src/react/fields/MarkdownInput.tsx +333 -0
  1263. package/src/react/fields/RadioInput.tsx +60 -0
  1264. package/src/react/fields/RepeaterInput.test.ts +116 -0
  1265. package/src/react/fields/RepeaterInput.tsx +1313 -0
  1266. package/src/react/fields/SelectFieldInput.tsx +257 -0
  1267. package/src/react/fields/SliderInput.tsx +63 -0
  1268. package/src/react/fields/TagsInput.tsx +265 -0
  1269. package/src/react/fields/TextLikeInput.tsx +54 -0
  1270. package/src/react/fields/ToggleButtonsInput.tsx +60 -0
  1271. package/src/react/fields/ToggleFieldInput.tsx +35 -0
  1272. package/src/react/fields/rowChromeButton.tsx +225 -0
  1273. package/src/react/fields/syncRowGates.test.ts +202 -0
  1274. package/src/react/fields/syncRowGates.ts +66 -0
  1275. package/src/react/formStateHelpers.test.ts +295 -0
  1276. package/src/react/formStateHelpers.ts +218 -0
  1277. package/src/react/hooks/use-mobile.ts +19 -0
  1278. package/src/react/icon-context.tsx +60 -0
  1279. package/src/react/index.ts +85 -0
  1280. package/src/react/layouts/SidebarLayout.tsx +239 -0
  1281. package/src/react/layouts/TopbarLayout.tsx +245 -0
  1282. package/src/react/navigate.tsx +37 -0
  1283. package/src/react/registry.ts +48 -0
  1284. package/src/react/right-panel-registry.tsx +47 -0
  1285. package/src/react/theme-preview/apply.ts +99 -0
  1286. package/src/react/theme-preview/build-html.ts +436 -0
  1287. package/src/react/ui/button.tsx +51 -0
  1288. package/src/react/ui/calendar.tsx +67 -0
  1289. package/src/react/ui/checkbox.tsx +29 -0
  1290. package/src/react/ui/dialog.tsx +108 -0
  1291. package/src/react/ui/dropdown-menu.tsx +97 -0
  1292. package/src/react/ui/input.tsx +20 -0
  1293. package/src/react/ui/label.tsx +21 -0
  1294. package/src/react/ui/popover.tsx +50 -0
  1295. package/src/react/ui/select.tsx +169 -0
  1296. package/src/react/ui/separator.tsx +25 -0
  1297. package/src/react/ui/sheet.tsx +136 -0
  1298. package/src/react/ui/sidebar.tsx +723 -0
  1299. package/src/react/ui/skeleton.tsx +13 -0
  1300. package/src/react/ui/slider.tsx +34 -0
  1301. package/src/react/ui/switch.tsx +28 -0
  1302. package/src/react/ui/table.tsx +105 -0
  1303. package/src/react/ui/tabs.tsx +63 -0
  1304. package/src/react/ui/textarea.tsx +18 -0
  1305. package/src/react/ui/tooltip.tsx +64 -0
  1306. package/src/react/useResizableWidth.ts +139 -0
  1307. package/src/react/utils.ts +6 -0
  1308. package/src/react/widgetRegistry.test.ts +43 -0
  1309. package/src/react/widgetRegistry.ts +50 -0
  1310. package/src/react/widgets/StatsOverviewRenderer.tsx +232 -0
  1311. package/src/react/widgets/TableWidgetRenderer.tsx +231 -0
  1312. package/src/react/widgets/ViewRenderer.tsx +71 -0
  1313. package/src/relationManagerData.test.ts +1146 -0
  1314. package/src/richtext/index.ts +8 -0
  1315. package/src/richtext/registry.ts +89 -0
  1316. package/src/routes-nested-relations.test.ts +676 -0
  1317. package/src/routes-relations.test.ts +972 -0
  1318. package/src/routes.test.ts +1886 -0
  1319. package/src/routes.ts +3262 -0
  1320. package/src/schema/Alert.test.ts +63 -0
  1321. package/src/schema/Alert.ts +49 -0
  1322. package/src/schema/Block.ts +169 -0
  1323. package/src/schema/Breadcrumbs.ts +40 -0
  1324. package/src/schema/Card.ts +35 -0
  1325. package/src/schema/Divider.ts +20 -0
  1326. package/src/schema/Element.ts +219 -0
  1327. package/src/schema/EmptyState.test.ts +37 -0
  1328. package/src/schema/EmptyState.ts +63 -0
  1329. package/src/schema/Fieldset.ts +43 -0
  1330. package/src/schema/Grid.ts +43 -0
  1331. package/src/schema/Group.ts +30 -0
  1332. package/src/schema/Heading.ts +39 -0
  1333. package/src/schema/Html.ts +67 -0
  1334. package/src/schema/Icon.ts +54 -0
  1335. package/src/schema/Image.ts +57 -0
  1336. package/src/schema/LinkTag.ts +41 -0
  1337. package/src/schema/Markdown.ts +85 -0
  1338. package/src/schema/MetaTag.ts +41 -0
  1339. package/src/schema/RelationTabs.ts +71 -0
  1340. package/src/schema/ScriptTag.ts +55 -0
  1341. package/src/schema/Section.ts +143 -0
  1342. package/src/schema/ServerDataElement.test.ts +140 -0
  1343. package/src/schema/ServerDataElement.ts +156 -0
  1344. package/src/schema/Split.ts +50 -0
  1345. package/src/schema/Stat.test.ts +118 -0
  1346. package/src/schema/Stat.ts +154 -0
  1347. package/src/schema/StatsOverview.test.ts +141 -0
  1348. package/src/schema/StatsOverview.ts +119 -0
  1349. package/src/schema/StyleTag.ts +35 -0
  1350. package/src/schema/TableWidget.test.ts +297 -0
  1351. package/src/schema/TableWidget.ts +289 -0
  1352. package/src/schema/Tabs.ts +79 -0
  1353. package/src/schema/Text.ts +58 -0
  1354. package/src/schema/UnorderedList.ts +49 -0
  1355. package/src/schema/View.test.ts +111 -0
  1356. package/src/schema/View.ts +127 -0
  1357. package/src/schema/Wizard.ts +108 -0
  1358. package/src/schema/containers.test.ts +446 -0
  1359. package/src/schema/headTags.test.ts +134 -0
  1360. package/src/schema/index.ts +39 -0
  1361. package/src/schema/primes.test.ts +269 -0
  1362. package/src/schema/resolveSchema.test.ts +329 -0
  1363. package/src/schema/resolveSchema.ts +807 -0
  1364. package/src/schema/sanitize.ts +49 -0
  1365. package/src/search.test.ts +446 -0
  1366. package/src/search.ts +178 -0
  1367. package/src/sessionFilters.test.ts +352 -0
  1368. package/src/sessionFilters.ts +133 -0
  1369. package/src/summarizers/Summarizer.test.ts +84 -0
  1370. package/src/summarizers/Summarizer.ts +123 -0
  1371. package/src/summarizers/index.ts +11 -0
  1372. package/src/theme/base-colors.ts +68 -0
  1373. package/src/theme/chart-colors.ts +50 -0
  1374. package/src/theme/colors.ts +447 -0
  1375. package/src/theme/generate-css.test.ts +139 -0
  1376. package/src/theme/generate-css.ts +44 -0
  1377. package/src/theme/generate-scale.test.ts +106 -0
  1378. package/src/theme/generate-scale.ts +97 -0
  1379. package/src/theme/icon-map.ts +42 -0
  1380. package/src/theme/index.ts +28 -0
  1381. package/src/theme/migrate.ts +81 -0
  1382. package/src/theme/presets.ts +135 -0
  1383. package/src/theme/radius.ts +18 -0
  1384. package/src/theme/resolve.test.ts +238 -0
  1385. package/src/theme/resolve.ts +96 -0
  1386. package/src/theme/spacing.ts +18 -0
  1387. package/src/theme/theme-colors.ts +88 -0
  1388. package/src/theme/types.ts +125 -0
  1389. package/src/uploads/UploadAdapter.ts +35 -0
  1390. package/src/uploads/index.ts +2 -0
  1391. package/src/uploads/localUpload.test.ts +70 -0
  1392. package/src/uploads/localUpload.ts +84 -0
  1393. package/src/validation/Validator.ts +49 -0
  1394. package/src/validation/index.ts +28 -0
  1395. package/src/validation/rules.ts +78 -0
  1396. package/src/validation/runValidators.ts +435 -0
  1397. package/src/validation/uniqueValidator.test.ts +196 -0
  1398. package/src/validation/uniqueValidator.ts +133 -0
  1399. package/src/validation/validators.test.ts +268 -0
  1400. package/src/vite.ts +758 -0
  1401. package/src/widgets/index.ts +10 -0
  1402. package/src/widgets/registry.ts +45 -0
  1403. package/src/widgets.test.ts +592 -0
  1404. package/tsconfig.build.json +11 -0
  1405. package/tsconfig.json +4 -0
  1406. package/tsconfig.test.json +10 -0
  1407. package/views/react/Dashboard.tsx +27 -0
  1408. package/views/react/Resources/Form.tsx +102 -0
  1409. package/views/react/Resources/Index.tsx +49 -0
@@ -0,0 +1,1630 @@
1
+ import { describe, it } from 'node:test'
2
+ import assert from 'node:assert/strict'
3
+
4
+ import { RepeaterField } from './RepeaterField.js'
5
+ import { TextField } from './TextField.js'
6
+ import { Form } from '../elements/Form.js'
7
+ import { dispatchFormSubmit, extractRelationshipRepeaters, loadRelationRows } from '../elements/dispatchForm.js'
8
+ import { applyRelationshipRepeaterFill } from '../pageData.js'
9
+ import type { ModelLike, ModelQuery } from '../orm/modelDefaults.js'
10
+
11
+ /**
12
+ * Test harness: a tiny in-memory ModelLike with a `paginate`-shaped
13
+ * query and basic CRUD methods that record their calls. Lets the
14
+ * relationship tests assert the exact sequence of create / update /
15
+ * delete operations against a shared fake without spinning up a
16
+ * database.
17
+ */
18
+ interface FakeRecord extends Record<string, unknown> {
19
+ id?: string | number
20
+ }
21
+
22
+ function makeFakeChildModel(initial: FakeRecord[] = []) {
23
+ let nextId = 1
24
+ const rows: FakeRecord[] = initial.map(r => ({ ...r }))
25
+ const calls: Array<
26
+ | { kind: 'create'; data: Record<string, unknown> }
27
+ | { kind: 'update'; id: string | number; data: Record<string, unknown> }
28
+ | { kind: 'delete'; id: string | number }
29
+ > = []
30
+
31
+ const model: ModelLike = {
32
+ primaryKey: 'id',
33
+ find: async (id) => rows.find(r => String(r.id) === String(id)) ?? null,
34
+ create: async (data) => {
35
+ calls.push({ kind: 'create', data: { ...data } })
36
+ const id = (data['id'] as string | number | undefined) ?? `c${nextId++}`
37
+ const fresh: FakeRecord = { ...data, id }
38
+ rows.push(fresh)
39
+ return fresh
40
+ },
41
+ update: async (id, data) => {
42
+ calls.push({ kind: 'update', id, data: { ...data } })
43
+ const idx = rows.findIndex(r => String(r.id) === String(id))
44
+ if (idx >= 0) {
45
+ rows[idx] = { ...rows[idx], ...data, id }
46
+ }
47
+ return rows[idx]
48
+ },
49
+ delete: async (id) => {
50
+ calls.push({ kind: 'delete', id })
51
+ const idx = rows.findIndex(r => String(r.id) === String(id))
52
+ if (idx >= 0) rows.splice(idx, 1)
53
+ },
54
+ query: () => makeQuery(rows),
55
+ }
56
+
57
+ return { model, rows, calls }
58
+ }
59
+
60
+ /** Fake `ModelQuery` — only `paginate` is wired since that's all the
61
+ * relationship pipeline calls. Other methods chain back to the same
62
+ * query so `where` / `orderBy` are silently ignored. */
63
+ function makeQuery(rows: FakeRecord[]): ModelQuery {
64
+ const q: ModelQuery = {
65
+ where: () => q,
66
+ orWhere: () => q,
67
+ orderBy: () => q,
68
+ paginate: async () => ({ data: rows.slice(), total: rows.length }),
69
+ }
70
+ return q
71
+ }
72
+
73
+ /**
74
+ * Fake parent model with a `relations` map matching the rudder ORM
75
+ * convention. The `relatedQuery` override pipes through to the child
76
+ * model's rows filtered by FK so calls behave like a real hasMany.
77
+ */
78
+ function makeFakeParentModel(opts: {
79
+ childModel: ModelLike
80
+ childRows: FakeRecord[]
81
+ relationName: string
82
+ foreignKey: string
83
+ }): ModelLike {
84
+ const { childModel, childRows, relationName, foreignKey } = opts
85
+ const parent: ModelLike & { relations: Record<string, unknown> } = {
86
+ primaryKey: 'id',
87
+ find: async () => null,
88
+ create: async () => ({}),
89
+ update: async () => ({}),
90
+ delete: async () => {},
91
+ query: () => makeQuery([]),
92
+ relatedQuery: (parentRecord) => {
93
+ const parentId = (parentRecord as Record<string, unknown>)['id']
94
+ const filtered = childRows.filter(r => String(r[foreignKey]) === String(parentId))
95
+ return makeQuery(filtered)
96
+ },
97
+ relations: {
98
+ [relationName]: { type: 'hasMany', model: () => childModel, foreignKey },
99
+ },
100
+ }
101
+ return parent
102
+ }
103
+
104
+ describe('Repeater.relationship — extraction', () => {
105
+ it('extractRelationshipRepeaters pulls the field value out of data', () => {
106
+ const repeater = RepeaterField.make('items')
107
+ .relationship('items')
108
+ .schema([TextField.make('label').required()])
109
+ const data: Record<string, unknown> = {
110
+ title: 'Order #1',
111
+ items: [{ __id: '1', label: 'A' }, { label: 'B' }],
112
+ otherJsonRepeater: [{ x: 1 }],
113
+ }
114
+ const deferrals = extractRelationshipRepeaters([repeater], data)
115
+ assert.equal(deferrals.length, 1)
116
+ assert.equal(deferrals[0]!.cfg.name, 'items')
117
+ assert.deepEqual(deferrals[0]!.rows, [{ __id: '1', label: 'A' }, { label: 'B' }])
118
+ // Pulled out of `data`.
119
+ assert.equal('items' in data, false)
120
+ // Non-relationship keys untouched.
121
+ assert.equal(data['title'], 'Order #1')
122
+ assert.deepEqual(data['otherJsonRepeater'], [{ x: 1 }])
123
+ })
124
+
125
+ it('extractRelationshipRepeaters skips non-relationship Repeaters', () => {
126
+ const json = RepeaterField.make('jsonItems').schema([TextField.make('x')])
127
+ const rel = RepeaterField.make('relItems').relationship('relItems').schema([TextField.make('y')])
128
+ const data: Record<string, unknown> = { jsonItems: [{ x: 1 }], relItems: [{ y: 2 }] }
129
+ const deferrals = extractRelationshipRepeaters([json, rel], data)
130
+ assert.equal(deferrals.length, 1)
131
+ assert.equal(deferrals[0]!.cfg.name, 'relItems')
132
+ assert.equal('jsonItems' in data, true)
133
+ assert.equal('relItems' in data, false)
134
+ })
135
+ })
136
+
137
+ describe('Repeater.relationship — full pipeline', () => {
138
+ it('create — submits new rows with FK stamped, no existing rows', async () => {
139
+ const child = makeFakeChildModel([])
140
+ const parent = makeFakeParentModel({
141
+ childModel: child.model,
142
+ childRows: child.rows,
143
+ relationName: 'items',
144
+ foreignKey: 'orderId',
145
+ })
146
+
147
+ const form = Form.make()
148
+ .schema([
149
+ TextField.make('title'),
150
+ RepeaterField.make('items').relationship('items').schema([
151
+ TextField.make('label').required(),
152
+ ]),
153
+ ])
154
+ .save(async (data) => {
155
+ // Parent never sees the relationship key — extracted before save.
156
+ assert.equal('items' in data, false)
157
+ return { id: 'p1', title: data['title'] }
158
+ })
159
+
160
+ const result = await dispatchFormSubmit(
161
+ form,
162
+ { title: 'Order', items: [{ label: 'A' }, { label: 'B' }] },
163
+ { values: { title: 'Order', items: [{ label: 'A' }, { label: 'B' }] }, parentModel: parent },
164
+ )
165
+
166
+ assert.equal(result.ok, true)
167
+ // Two creates, no updates, no deletes.
168
+ assert.equal(child.calls.filter(c => c.kind === 'create').length, 2)
169
+ assert.equal(child.calls.filter(c => c.kind === 'update').length, 0)
170
+ assert.equal(child.calls.filter(c => c.kind === 'delete').length, 0)
171
+ // FK stamped on each create payload.
172
+ const creates = child.calls.filter(c => c.kind === 'create') as Array<{ kind: 'create'; data: Record<string, unknown> }>
173
+ assert.equal(creates[0]!.data['orderId'], 'p1')
174
+ assert.equal(creates[1]!.data['orderId'], 'p1')
175
+ assert.equal(creates[0]!.data['label'], 'A')
176
+ assert.equal(creates[1]!.data['label'], 'B')
177
+ })
178
+
179
+ it('update — submits __id matching existing PK; routed through update without overwriting FK', async () => {
180
+ const child = makeFakeChildModel([
181
+ { id: 'c1', orderId: 'p1', label: 'old A' },
182
+ { id: 'c2', orderId: 'p1', label: 'old B' },
183
+ ])
184
+ const parent = makeFakeParentModel({
185
+ childModel: child.model,
186
+ childRows: child.rows,
187
+ relationName: 'items',
188
+ foreignKey: 'orderId',
189
+ })
190
+
191
+ const form = Form.make()
192
+ .schema([
193
+ RepeaterField.make('items').relationship('items').schema([
194
+ TextField.make('label').required(),
195
+ ]),
196
+ ])
197
+ .save(async () => ({ id: 'p1' }))
198
+
199
+ const result = await dispatchFormSubmit(
200
+ form,
201
+ { items: [{ __id: 'c1', label: 'new A' }, { __id: 'c2', label: 'new B' }] },
202
+ {
203
+ values: { items: [{ __id: 'c1', label: 'new A' }, { __id: 'c2', label: 'new B' }] },
204
+ record: { id: 'p1' },
205
+ parentModel: parent,
206
+ },
207
+ )
208
+ assert.equal(result.ok, true)
209
+ const updates = child.calls.filter(c => c.kind === 'update') as Array<{ kind: 'update'; id: string | number; data: Record<string, unknown> }>
210
+ assert.equal(updates.length, 2)
211
+ // FK NOT in update payload — stays as it was on the existing row.
212
+ assert.equal('orderId' in updates[0]!.data, false)
213
+ assert.equal('orderId' in updates[1]!.data, false)
214
+ assert.equal(updates[0]!.data['label'], 'new A')
215
+ assert.equal(updates[1]!.data['label'], 'new B')
216
+ assert.equal(child.calls.filter(c => c.kind === 'create').length, 0)
217
+ assert.equal(child.calls.filter(c => c.kind === 'delete').length, 0)
218
+ })
219
+
220
+ it('delete — existing PK omitted from submitted set is deleted', async () => {
221
+ const child = makeFakeChildModel([
222
+ { id: 'c1', orderId: 'p1', label: 'A' },
223
+ { id: 'c2', orderId: 'p1', label: 'B' },
224
+ { id: 'c3', orderId: 'p1', label: 'C' },
225
+ ])
226
+ const parent = makeFakeParentModel({
227
+ childModel: child.model,
228
+ childRows: child.rows,
229
+ relationName: 'items',
230
+ foreignKey: 'orderId',
231
+ })
232
+
233
+ const form = Form.make()
234
+ .schema([
235
+ RepeaterField.make('items').relationship('items').schema([
236
+ TextField.make('label').required(),
237
+ ]),
238
+ ])
239
+ .save(async () => ({ id: 'p1' }))
240
+
241
+ const result = await dispatchFormSubmit(
242
+ form,
243
+ { items: [{ __id: 'c1', label: 'A' }] },
244
+ {
245
+ values: { items: [{ __id: 'c1', label: 'A' }] },
246
+ record: { id: 'p1' },
247
+ parentModel: parent,
248
+ },
249
+ )
250
+ assert.equal(result.ok, true)
251
+ const deletes = child.calls.filter(c => c.kind === 'delete') as Array<{ kind: 'delete'; id: string | number }>
252
+ assert.deepEqual(deletes.map(c => String(c.id)).sort(), ['c2', 'c3'])
253
+ })
254
+
255
+ it('mixed — single submit performs creates, updates, and deletes in one diff', async () => {
256
+ const child = makeFakeChildModel([
257
+ { id: 'c1', orderId: 'p1', label: 'A' },
258
+ { id: 'c2', orderId: 'p1', label: 'B' },
259
+ ])
260
+ const parent = makeFakeParentModel({
261
+ childModel: child.model,
262
+ childRows: child.rows,
263
+ relationName: 'items',
264
+ foreignKey: 'orderId',
265
+ })
266
+
267
+ const form = Form.make()
268
+ .schema([
269
+ RepeaterField.make('items').relationship('items').schema([
270
+ TextField.make('label').required(),
271
+ ]),
272
+ ])
273
+ .save(async () => ({ id: 'p1' }))
274
+
275
+ // c1 stays (renamed), c2 gone, plus a new row.
276
+ const result = await dispatchFormSubmit(
277
+ form,
278
+ { items: [{ __id: 'c1', label: 'A renamed' }, { label: 'fresh' }] },
279
+ {
280
+ values: { items: [{ __id: 'c1', label: 'A renamed' }, { label: 'fresh' }] },
281
+ record: { id: 'p1' },
282
+ parentModel: parent,
283
+ },
284
+ )
285
+ assert.equal(result.ok, true)
286
+ assert.equal(child.calls.filter(c => c.kind === 'create').length, 1)
287
+ assert.equal(child.calls.filter(c => c.kind === 'update').length, 1)
288
+ assert.equal(child.calls.filter(c => c.kind === 'delete').length, 1)
289
+ // Spot-check the create stamps the FK.
290
+ const created = child.calls.find(c => c.kind === 'create') as { kind: 'create'; data: Record<string, unknown> }
291
+ assert.equal(created.data['orderId'], 'p1')
292
+ assert.equal(created.data['label'], 'fresh')
293
+ })
294
+
295
+ it('orderColumn writes 0-based index on every create + update', async () => {
296
+ const child = makeFakeChildModel([
297
+ { id: 'c1', orderId: 'p1', label: 'A', sort: 5 },
298
+ ])
299
+ const parent = makeFakeParentModel({
300
+ childModel: child.model,
301
+ childRows: child.rows,
302
+ relationName: 'items',
303
+ foreignKey: 'orderId',
304
+ })
305
+
306
+ const form = Form.make()
307
+ .schema([
308
+ RepeaterField.make('items')
309
+ .relationship('items')
310
+ .orderColumn('sort')
311
+ .schema([TextField.make('label').required()]),
312
+ ])
313
+ .save(async () => ({ id: 'p1' }))
314
+
315
+ const result = await dispatchFormSubmit(
316
+ form,
317
+ { items: [{ label: 'fresh first' }, { __id: 'c1', label: 'A second' }] },
318
+ {
319
+ values: { items: [{ label: 'fresh first' }, { __id: 'c1', label: 'A second' }] },
320
+ record: { id: 'p1' },
321
+ parentModel: parent,
322
+ },
323
+ )
324
+ assert.equal(result.ok, true)
325
+ const create = child.calls.find(c => c.kind === 'create') as { kind: 'create'; data: Record<string, unknown> }
326
+ const update = child.calls.find(c => c.kind === 'update') as { kind: 'update'; id: string | number; data: Record<string, unknown> }
327
+ assert.equal(create.data['sort'], 0)
328
+ assert.equal(update.data['sort'], 1)
329
+ })
330
+
331
+ it('throws when parentModel is missing on the FormContext', async () => {
332
+ const form = Form.make()
333
+ .schema([
334
+ RepeaterField.make('items').relationship('items').schema([TextField.make('label')]),
335
+ ])
336
+ .save(async () => ({ id: 'p1' }))
337
+
338
+ await assert.rejects(
339
+ () => dispatchFormSubmit(
340
+ form,
341
+ { items: [{ label: 'A' }] },
342
+ { values: { items: [{ label: 'A' }] } },
343
+ ),
344
+ /parentModel on the FormContext/,
345
+ )
346
+ })
347
+
348
+ it('throws when descriptor lookup fails and no override is set', async () => {
349
+ const child = makeFakeChildModel()
350
+ // Parent missing the `relations` map entry for 'phantom'.
351
+ const parent: ModelLike = {
352
+ find: async () => null,
353
+ create: async () => ({}),
354
+ update: async () => ({}),
355
+ delete: async () => {},
356
+ query: () => makeQuery([]),
357
+ relatedQuery: () => makeQuery([]),
358
+ }
359
+
360
+ const form = Form.make()
361
+ .schema([
362
+ RepeaterField.make('phantom').relationship('phantom').schema([TextField.make('x').required()]),
363
+ ])
364
+ .save(async () => ({ id: 'p1' }))
365
+
366
+ await assert.rejects(
367
+ () => dispatchFormSubmit(
368
+ form,
369
+ { phantom: [{ x: 'a' }] },
370
+ {
371
+ values: { phantom: [{ x: 'a' }] },
372
+ parentModel: parent,
373
+ },
374
+ ),
375
+ /could not resolve the child model/,
376
+ )
377
+ void child
378
+ })
379
+
380
+ it('honors explicit model + foreignKey overrides on the field config (no descriptor needed)', async () => {
381
+ const child = makeFakeChildModel()
382
+ // Parent with NO relations map — overrides have to carry the day.
383
+ const parent: ModelLike = {
384
+ primaryKey: 'id',
385
+ find: async () => null,
386
+ create: async () => ({}),
387
+ update: async () => ({}),
388
+ delete: async () => {},
389
+ query: () => makeQuery([]),
390
+ relatedQuery: () => makeQuery(child.rows),
391
+ }
392
+
393
+ const form = Form.make()
394
+ .schema([
395
+ RepeaterField.make('items')
396
+ .relationship({ name: 'items', model: child.model, foreignKey: 'orderId' })
397
+ .schema([TextField.make('label').required()]),
398
+ ])
399
+ .save(async () => ({ id: 'p1' }))
400
+
401
+ const result = await dispatchFormSubmit(
402
+ form,
403
+ { items: [{ label: 'A' }] },
404
+ {
405
+ values: { items: [{ label: 'A' }] },
406
+ parentModel: parent,
407
+ },
408
+ )
409
+ assert.equal(result.ok, true)
410
+ assert.equal(child.calls.length, 1)
411
+ const created = child.calls[0] as { kind: 'create'; data: Record<string, unknown> }
412
+ assert.equal(created.data['orderId'], 'p1')
413
+ })
414
+ })
415
+
416
+ describe('Repeater.relationship — load (applyRelationshipRepeaterFill)', () => {
417
+ it('stamps __id from PK and strips PK + FK from each row', async () => {
418
+ const child = makeFakeChildModel([
419
+ { id: 'c1', orderId: 'p1', label: 'A' },
420
+ { id: 'c2', orderId: 'p1', label: 'B' },
421
+ ])
422
+ const parent = makeFakeParentModel({
423
+ childModel: child.model,
424
+ childRows: child.rows,
425
+ relationName: 'items',
426
+ foreignKey: 'orderId',
427
+ })
428
+
429
+ const form = Form.make().schema([
430
+ TextField.make('title'),
431
+ RepeaterField.make('items').relationship('items').schema([
432
+ TextField.make('label').required(),
433
+ ]),
434
+ ])
435
+
436
+ const out = await applyRelationshipRepeaterFill(
437
+ form,
438
+ { title: 'Order' },
439
+ { id: 'p1' },
440
+ parent,
441
+ )
442
+ assert.deepEqual(out['items'], [
443
+ { __id: 'c1', label: 'A' },
444
+ { __id: 'c2', label: 'B' },
445
+ ])
446
+ // Non-relationship values untouched.
447
+ assert.equal(out['title'], 'Order')
448
+ })
449
+
450
+ it('no-op when record is null, parentModel is missing, or there are no relationship Repeaters', async () => {
451
+ const form = Form.make().schema([
452
+ RepeaterField.make('items').relationship('items').schema([TextField.make('label')]),
453
+ ])
454
+ const parent = makeFakeParentModel({
455
+ childModel: makeFakeChildModel().model,
456
+ childRows: [],
457
+ relationName: 'items',
458
+ foreignKey: 'orderId',
459
+ })
460
+ // null record
461
+ assert.deepEqual(
462
+ await applyRelationshipRepeaterFill(form, { x: 1 }, null, parent),
463
+ { x: 1 },
464
+ )
465
+ // missing parentModel
466
+ assert.deepEqual(
467
+ await applyRelationshipRepeaterFill(form, { x: 1 }, { id: 'p1' }, undefined),
468
+ { x: 1 },
469
+ )
470
+ // form without relationship Repeaters
471
+ const plain = Form.make().schema([TextField.make('title')])
472
+ assert.deepEqual(
473
+ await applyRelationshipRepeaterFill(plain, { title: 't' }, { id: 'p1' }, parent),
474
+ { title: 't' },
475
+ )
476
+ })
477
+
478
+ it('loadRelationRows reads through resolveRelatedQuery (paginate)', async () => {
479
+ const child = makeFakeChildModel([
480
+ { id: 'c1', orderId: 'p1', label: 'A' },
481
+ ])
482
+ const parent = makeFakeParentModel({
483
+ childModel: child.model,
484
+ childRows: child.rows,
485
+ relationName: 'items',
486
+ foreignKey: 'orderId',
487
+ })
488
+ const rows = await loadRelationRows(parent, { id: 'p1' }, 'items')
489
+ assert.equal(rows.length, 1)
490
+ assert.equal((rows[0] as Record<string, unknown>)['label'], 'A')
491
+ })
492
+ })
493
+
494
+ describe('Repeater.relationship — morphMany', () => {
495
+ // Parent shape: `Order.items: morphMany(Item, 'itemable')` — child
496
+ // carries `itemableId` + `itemableType` instead of an FK column.
497
+ // `computeMorphPayload(parent, descriptor)` reads the discriminator off
498
+ // the parent **record**'s `constructor.morphAlias ?? constructor.name`,
499
+ // so the parent record returned by `Form.save()` has to be a class
500
+ // instance (not a plain object literal).
501
+ function makeMorphParentSetup(opts: {
502
+ childModel: ModelLike
503
+ childRows: FakeRecord[]
504
+ relationName: string
505
+ morphName: string
506
+ }) {
507
+ const { childModel, childRows, relationName, morphName } = opts
508
+ const idCol = `${morphName}Id`
509
+ const typeCol = `${morphName}Type`
510
+
511
+ class Order {
512
+ id?: string
513
+ constructor(init?: Partial<{ id: string }>) { Object.assign(this, init) }
514
+ }
515
+
516
+ const parentModel: ModelLike & { relations: Record<string, unknown> } = {
517
+ primaryKey: 'id',
518
+ find: async () => null,
519
+ create: async () => ({}),
520
+ update: async () => ({}),
521
+ delete: async () => {},
522
+ query: () => makeQuery([]),
523
+ relatedQuery: (parentRecord) => {
524
+ const parentId = (parentRecord as Record<string, unknown>)['id']
525
+ const parentType = (parentRecord as { constructor?: { morphAlias?: string; name?: string } })
526
+ .constructor?.morphAlias
527
+ ?? (parentRecord as { constructor?: { name?: string } }).constructor?.name
528
+ const filtered = childRows.filter(r =>
529
+ String(r[idCol]) === String(parentId) && r[typeCol] === parentType,
530
+ )
531
+ return makeQuery(filtered)
532
+ },
533
+ relations: {
534
+ [relationName]: { type: 'morphMany', morphName, model: () => childModel },
535
+ },
536
+ }
537
+ return { parentModel, makeRecord: (id: string) => new Order({ id }) }
538
+ }
539
+
540
+ it('create — stamps <morphName>Id + <morphName>Type instead of an FK column', async () => {
541
+ const child = makeFakeChildModel([])
542
+ const { parentModel, makeRecord } = makeMorphParentSetup({
543
+ childModel: child.model, childRows: child.rows,
544
+ relationName: 'items', morphName: 'itemable',
545
+ })
546
+
547
+ const form = Form.make()
548
+ .schema([
549
+ RepeaterField.make('items')
550
+ .relationship('items')
551
+ .schema([TextField.make('label').required()]),
552
+ ])
553
+ .save(async () => makeRecord('p1'))
554
+
555
+ const submittedRows = [{ label: 'A' }, { label: 'B' }]
556
+ const result = await dispatchFormSubmit(
557
+ form,
558
+ { items: submittedRows },
559
+ { values: { items: submittedRows }, parentModel },
560
+ )
561
+ assert.equal(result.ok, true)
562
+ const creates = child.calls.filter(c => c.kind === 'create') as Array<{ kind: 'create'; data: Record<string, unknown> }>
563
+ assert.equal(creates.length, 2)
564
+ for (const c of creates) {
565
+ assert.equal(c.data['itemableId'], 'p1')
566
+ assert.equal(c.data['itemableType'], 'Order')
567
+ assert.equal('orderId' in c.data, false)
568
+ }
569
+ assert.equal(creates[0]!.data['label'], 'A')
570
+ assert.equal(creates[1]!.data['label'], 'B')
571
+ })
572
+
573
+ it('update — does not overwrite morph cols on update (defense against re-link)', async () => {
574
+ const child = makeFakeChildModel([
575
+ { id: 'c1', itemableId: 'p1', itemableType: 'Order', label: 'A' },
576
+ { id: 'c2', itemableId: 'p1', itemableType: 'Order', label: 'B' },
577
+ ])
578
+ const { parentModel, makeRecord } = makeMorphParentSetup({
579
+ childModel: child.model, childRows: child.rows,
580
+ relationName: 'items', morphName: 'itemable',
581
+ })
582
+
583
+ const form = Form.make()
584
+ .schema([
585
+ RepeaterField.make('items')
586
+ .relationship('items')
587
+ .schema([TextField.make('label')]),
588
+ ])
589
+ .save(async () => makeRecord('p1'))
590
+
591
+ const submittedRows = [
592
+ // Tampered client tries to send itemableType=Invoice; framework wins last.
593
+ { __id: 'c1', label: 'A2', itemableType: 'Invoice' },
594
+ { __id: 'c2', label: 'B2' },
595
+ ]
596
+ const result = await dispatchFormSubmit(
597
+ form,
598
+ { items: submittedRows },
599
+ { values: { items: submittedRows }, record: makeRecord('p1'), parentModel },
600
+ )
601
+ assert.equal(result.ok, true)
602
+ const updates = child.calls.filter(c => c.kind === 'update') as Array<{ kind: 'update'; id: string | number; data: Record<string, unknown> }>
603
+ assert.equal(updates.length, 2)
604
+ for (const u of updates) {
605
+ assert.equal('itemableId' in u.data, false)
606
+ assert.equal('itemableType' in u.data, false)
607
+ }
608
+ })
609
+
610
+ it('delete — existing PKs missing from submitted set are deleted (same shape as hasMany)', async () => {
611
+ const child = makeFakeChildModel([
612
+ { id: 'c1', itemableId: 'p1', itemableType: 'Order', label: 'A' },
613
+ { id: 'c2', itemableId: 'p1', itemableType: 'Order', label: 'B' },
614
+ { id: 'c3', itemableId: 'p1', itemableType: 'Order', label: 'C' },
615
+ ])
616
+ const { parentModel, makeRecord } = makeMorphParentSetup({
617
+ childModel: child.model, childRows: child.rows,
618
+ relationName: 'items', morphName: 'itemable',
619
+ })
620
+
621
+ const form = Form.make()
622
+ .schema([
623
+ RepeaterField.make('items')
624
+ .relationship('items')
625
+ .schema([TextField.make('label')]),
626
+ ])
627
+ .save(async () => makeRecord('p1'))
628
+
629
+ const submittedRows = [{ __id: 'c1', label: 'A' }]
630
+ const result = await dispatchFormSubmit(
631
+ form,
632
+ { items: submittedRows },
633
+ { values: { items: submittedRows }, record: makeRecord('p1'), parentModel },
634
+ )
635
+ assert.equal(result.ok, true)
636
+ const deletes = child.calls.filter(c => c.kind === 'delete') as Array<{ kind: 'delete'; id: string | number }>
637
+ assert.deepEqual(deletes.map(c => String(c.id)).sort(), ['c2', 'c3'])
638
+ })
639
+
640
+ it('orderColumn writes 0-based index on every morph create + update', async () => {
641
+ const child = makeFakeChildModel([
642
+ { id: 'c1', itemableId: 'p1', itemableType: 'Order', label: 'A', sort: 5 },
643
+ ])
644
+ const { parentModel, makeRecord } = makeMorphParentSetup({
645
+ childModel: child.model, childRows: child.rows,
646
+ relationName: 'items', morphName: 'itemable',
647
+ })
648
+
649
+ const form = Form.make()
650
+ .schema([
651
+ RepeaterField.make('items')
652
+ .relationship('items')
653
+ .orderColumn('sort')
654
+ .schema([TextField.make('label')]),
655
+ ])
656
+ .save(async () => makeRecord('p1'))
657
+
658
+ const submittedRows = [
659
+ { label: 'first' },
660
+ { __id: 'c1', label: 'second' },
661
+ ]
662
+ const result = await dispatchFormSubmit(
663
+ form,
664
+ { items: submittedRows },
665
+ { values: { items: submittedRows }, record: makeRecord('p1'), parentModel },
666
+ )
667
+ assert.equal(result.ok, true)
668
+ const create = child.calls.find(c => c.kind === 'create') as { kind: 'create'; data: Record<string, unknown> }
669
+ const update = child.calls.find(c => c.kind === 'update') as { kind: 'update'; id: string | number; data: Record<string, unknown> }
670
+ assert.equal(create.data['sort'], 0)
671
+ assert.equal(update.data['sort'], 1)
672
+ })
673
+
674
+ it('morphType — explicit override on the relation entry wins over constructor name', async () => {
675
+ const child = makeFakeChildModel([])
676
+ class Order {
677
+ id?: string
678
+ constructor(init?: Partial<{ id: string }>) { Object.assign(this, init) }
679
+ }
680
+ const parentModel: ModelLike & { relations: Record<string, unknown> } = {
681
+ primaryKey: 'id',
682
+ find: async () => null,
683
+ create: async () => ({}),
684
+ update: async () => ({}),
685
+ delete: async () => {},
686
+ query: () => makeQuery([]),
687
+ relatedQuery: () => makeQuery([]),
688
+ relations: {
689
+ items: { type: 'morphMany', morphName: 'itemable', morphType: 'CustomDiscriminator', model: () => child.model },
690
+ },
691
+ }
692
+
693
+ const form = Form.make()
694
+ .schema([
695
+ RepeaterField.make('items')
696
+ .relationship('items')
697
+ .schema([TextField.make('label')]),
698
+ ])
699
+ .save(async () => new Order({ id: 'p1' }))
700
+
701
+ const submittedRows = [{ label: 'A' }]
702
+ const result = await dispatchFormSubmit(
703
+ form,
704
+ { items: submittedRows },
705
+ { values: { items: submittedRows }, parentModel },
706
+ )
707
+ assert.equal(result.ok, true)
708
+ const create = child.calls.find(c => c.kind === 'create') as { kind: 'create'; data: Record<string, unknown> }
709
+ assert.equal(create.data['itemableType'], 'CustomDiscriminator')
710
+ })
711
+
712
+ it('load — applyRelationshipRepeaterFill strips morph cols from rendered rows', async () => {
713
+ const child = makeFakeChildModel([
714
+ { id: 'c1', itemableId: 'p1', itemableType: 'Order', label: 'A' },
715
+ { id: 'c2', itemableId: 'p1', itemableType: 'Order', label: 'B' },
716
+ ])
717
+ const { parentModel, makeRecord } = makeMorphParentSetup({
718
+ childModel: child.model, childRows: child.rows,
719
+ relationName: 'items', morphName: 'itemable',
720
+ })
721
+
722
+ const form = Form.make().schema([
723
+ TextField.make('title'),
724
+ RepeaterField.make('items')
725
+ .relationship('items')
726
+ .schema([TextField.make('label')]),
727
+ ])
728
+
729
+ const out = await applyRelationshipRepeaterFill(form, { title: 'P' }, makeRecord('p1'), parentModel)
730
+ assert.deepEqual(out['items'], [
731
+ { __id: 'c1', label: 'A' },
732
+ { __id: 'c2', label: 'B' },
733
+ ])
734
+ // Morph cols should NOT leak into the rendered row payload.
735
+ for (const row of out['items'] as Array<Record<string, unknown>>) {
736
+ assert.equal('itemableId' in row, false)
737
+ assert.equal('itemableType' in row, false)
738
+ assert.equal('id' in row, false)
739
+ }
740
+ })
741
+
742
+ it('morphMany config without the model thunk surfaces a clear error', async () => {
743
+ const child = makeFakeChildModel([])
744
+ class Order {
745
+ id?: string
746
+ constructor(init?: Partial<{ id: string }>) { Object.assign(this, init) }
747
+ }
748
+ const parentModel: ModelLike & { relations: Record<string, unknown> } = {
749
+ primaryKey: 'id',
750
+ find: async () => null,
751
+ create: async () => ({}),
752
+ update: async () => ({}),
753
+ delete: async () => {},
754
+ query: () => makeQuery([]),
755
+ relatedQuery: () => makeQuery([]),
756
+ relations: {
757
+ // No `model` thunk — getMorphRelationDescriptor returns
758
+ // undefined, so the resolver falls through to the hasMany
759
+ // branch which then asks for foreignKey. The user-facing fix
760
+ // is the same: configure-the-relation.
761
+ items: { type: 'morphMany', morphName: 'itemable' },
762
+ },
763
+ }
764
+
765
+ const form = Form.make()
766
+ .schema([
767
+ RepeaterField.make('items')
768
+ .relationship('items')
769
+ .schema([TextField.make('label')]),
770
+ ])
771
+ .save(async () => new Order({ id: 'p1' }))
772
+
773
+ const submittedRows = [{ label: 'A' }]
774
+ await assert.rejects(
775
+ () => dispatchFormSubmit(
776
+ form,
777
+ { items: submittedRows },
778
+ { values: { items: submittedRows }, parentModel },
779
+ ),
780
+ /could not resolve the child model/,
781
+ )
782
+ void child
783
+ })
784
+ })
785
+
786
+ /**
787
+ * Test harness for M2M relations — `parent[rel]()` returns a recorded
788
+ * accessor with `attach` / `detach` / `sync`. Mirrors `_makeBelongsToManyAccessor`
789
+ * from the rudder ORM (the per-relation accessor returned by
790
+ * `Model.belongsToMany`). Tests assert against `pivotCalls` directly so
791
+ * we can verify the exact sequence of operations against the pivot.
792
+ */
793
+ function makeM2MParentSetup(opts: {
794
+ childModel: ModelLike
795
+ childRows: FakeRecord[]
796
+ relationName: string
797
+ /** When set, the related rows on load are filtered to those whose
798
+ * PK appears in the pivot. Lets `applyRelationshipRepeaterFill`
799
+ * return the right slice. */
800
+ attachedIds?: Set<string | number>
801
+ }) {
802
+ const { childModel, childRows, relationName } = opts
803
+ const attachedIds = opts.attachedIds ?? new Set<string | number>(childRows.map(r => r['id'] as string | number))
804
+ const pivotCalls: Array<
805
+ | { kind: 'attach'; ids: Array<string | number> }
806
+ | { kind: 'detach'; ids: Array<string | number> | undefined }
807
+ | { kind: 'sync'; desired: Array<string | number> }
808
+ > = []
809
+
810
+ class Parent {
811
+ id?: string
812
+ constructor(init?: Partial<{ id: string }>) { Object.assign(this, init) }
813
+ [relationName]() {
814
+ return {
815
+ attach: async (input: ReadonlyArray<string | number> | Record<string, Record<string, unknown>>) => {
816
+ const ids = Array.isArray(input)
817
+ ? [...input]
818
+ : Object.keys(input).map(k => /^\d+$/.test(k) ? Number(k) : k)
819
+ for (const id of ids) attachedIds.add(id)
820
+ pivotCalls.push({ kind: 'attach', ids })
821
+ },
822
+ detach: async (ids?: ReadonlyArray<string | number>) => {
823
+ if (ids === undefined) {
824
+ const removed = [...attachedIds]
825
+ attachedIds.clear()
826
+ pivotCalls.push({ kind: 'detach', ids: undefined })
827
+ return removed.length
828
+ }
829
+ for (const id of ids) attachedIds.delete(id)
830
+ pivotCalls.push({ kind: 'detach', ids: [...ids] })
831
+ return ids.length
832
+ },
833
+ sync: async (desiredIds: ReadonlyArray<string | number>) => {
834
+ pivotCalls.push({ kind: 'sync', desired: [...desiredIds] })
835
+ return { attached: [], detached: [] }
836
+ },
837
+ }
838
+ }
839
+ }
840
+
841
+ const parentModel: ModelLike & { relations: Record<string, unknown> } = {
842
+ primaryKey: 'id',
843
+ find: async () => null,
844
+ create: async () => ({}),
845
+ update: async () => ({}),
846
+ delete: async () => {},
847
+ query: () => makeQuery([]),
848
+ // Resolve "currently attached" rows for the parent — read from the
849
+ // pivot snapshot. Drives both `loadRelationRows` (in the diff loop)
850
+ // and `applyRelationshipRepeaterFill` (in load mode).
851
+ relatedQuery: () => makeQuery(childRows.filter(r => attachedIds.has(r['id'] as string | number))),
852
+ relations: {
853
+ [relationName]: { type: 'belongsToMany', model: () => childModel, pivotTable: 'pivot' },
854
+ },
855
+ }
856
+ return {
857
+ parentModel,
858
+ pivotCalls,
859
+ attachedIds,
860
+ makeRecord: (id: string) => new Parent({ id }),
861
+ }
862
+ }
863
+
864
+ describe('Repeater.relationship — belongsToMany', () => {
865
+ // Parent shape: `Article.tags: belongsToMany(Tag, pivotTable: 'article_tag')`.
866
+ // Child Tag rows have NO FK column — pivot table holds the link.
867
+ // Submit semantics: create-row → M.create + accessor.attach; update-row
868
+ // → M.update (pivot untouched); delete-row → accessor.detach (no
869
+ // M.delete, child may be attached to other parents).
870
+
871
+ it('create — M.create the related child then attach via accessor (no FK on payload)', async () => {
872
+ const child = makeFakeChildModel([])
873
+ const setup = makeM2MParentSetup({
874
+ childModel: child.model,
875
+ childRows: child.rows,
876
+ relationName: 'tags',
877
+ attachedIds: new Set(),
878
+ })
879
+
880
+ const form = Form.make()
881
+ .schema([
882
+ RepeaterField.make('tags')
883
+ .relationship('tags')
884
+ .schema([TextField.make('name').required()]),
885
+ ])
886
+ .save(async () => setup.makeRecord('a1'))
887
+
888
+ const submittedRows = [{ name: 'red' }, { name: 'blue' }]
889
+ const result = await dispatchFormSubmit(
890
+ form,
891
+ { tags: submittedRows },
892
+ { values: { tags: submittedRows }, parentModel: setup.parentModel },
893
+ )
894
+ assert.equal(result.ok, true)
895
+ const creates = child.calls.filter(c => c.kind === 'create') as Array<{ kind: 'create'; data: Record<string, unknown> }>
896
+ assert.equal(creates.length, 2)
897
+ assert.equal(creates[0]!.data['name'], 'red')
898
+ assert.equal(creates[1]!.data['name'], 'blue')
899
+ // No FK / morph cols stamped on the related child — pivot covers it.
900
+ for (const c of creates) {
901
+ assert.equal('articleId' in c.data, false)
902
+ assert.equal('taggableId' in c.data, false)
903
+ assert.equal('taggableType' in c.data, false)
904
+ }
905
+ // One attach per new row, in row order.
906
+ const attachCalls = setup.pivotCalls.filter(c => c.kind === 'attach') as Array<{ kind: 'attach'; ids: Array<string | number> }>
907
+ assert.equal(attachCalls.length, 2)
908
+ assert.equal(attachCalls[0]!.ids.length, 1)
909
+ assert.equal(attachCalls[1]!.ids.length, 1)
910
+ // No pivot detach.
911
+ assert.equal(setup.pivotCalls.filter(c => c.kind === 'detach').length, 0)
912
+ })
913
+
914
+ it('update — __id matching an attached PK routes through M.update; pivot untouched', async () => {
915
+ const child = makeFakeChildModel([
916
+ { id: 'c1', name: 'red' },
917
+ { id: 'c2', name: 'blue' },
918
+ ])
919
+ const setup = makeM2MParentSetup({
920
+ childModel: child.model,
921
+ childRows: child.rows,
922
+ relationName: 'tags',
923
+ attachedIds: new Set(['c1', 'c2']),
924
+ })
925
+
926
+ const form = Form.make()
927
+ .schema([
928
+ RepeaterField.make('tags')
929
+ .relationship('tags')
930
+ .schema([TextField.make('name')]),
931
+ ])
932
+ .save(async () => setup.makeRecord('a1'))
933
+
934
+ const submittedRows = [
935
+ { __id: 'c1', name: 'crimson' },
936
+ { __id: 'c2', name: 'navy' },
937
+ ]
938
+ const result = await dispatchFormSubmit(
939
+ form,
940
+ { tags: submittedRows },
941
+ { values: { tags: submittedRows }, record: setup.makeRecord('a1'), parentModel: setup.parentModel },
942
+ )
943
+ assert.equal(result.ok, true)
944
+ const updates = child.calls.filter(c => c.kind === 'update') as Array<{ kind: 'update'; id: string | number; data: Record<string, unknown> }>
945
+ assert.equal(updates.length, 2)
946
+ assert.equal(updates[0]!.data['name'], 'crimson')
947
+ assert.equal(updates[1]!.data['name'], 'navy')
948
+ // No pivot operations — update doesn't touch attach/detach.
949
+ assert.equal(setup.pivotCalls.length, 0)
950
+ })
951
+
952
+ it('delete — existing attached PK omitted from submitted set is detached only (no M.delete)', async () => {
953
+ const child = makeFakeChildModel([
954
+ { id: 'c1', name: 'red' },
955
+ { id: 'c2', name: 'blue' },
956
+ { id: 'c3', name: 'green' },
957
+ ])
958
+ const setup = makeM2MParentSetup({
959
+ childModel: child.model,
960
+ childRows: child.rows,
961
+ relationName: 'tags',
962
+ attachedIds: new Set(['c1', 'c2', 'c3']),
963
+ })
964
+
965
+ const form = Form.make()
966
+ .schema([
967
+ RepeaterField.make('tags')
968
+ .relationship('tags')
969
+ .schema([TextField.make('name')]),
970
+ ])
971
+ .save(async () => setup.makeRecord('a1'))
972
+
973
+ const submittedRows = [{ __id: 'c1', name: 'red' }]
974
+ const result = await dispatchFormSubmit(
975
+ form,
976
+ { tags: submittedRows },
977
+ { values: { tags: submittedRows }, record: setup.makeRecord('a1'), parentModel: setup.parentModel },
978
+ )
979
+ assert.equal(result.ok, true)
980
+ // No M.delete on the related child — only pivot detach.
981
+ assert.equal(child.calls.filter(c => c.kind === 'delete').length, 0)
982
+ const detachCalls = setup.pivotCalls.filter(c => c.kind === 'detach') as Array<{ kind: 'detach'; ids: Array<string | number> | undefined }>
983
+ // Each missing PK gets its own detach call.
984
+ const detachedIds = detachCalls
985
+ .flatMap(c => c.ids ?? [])
986
+ .map(id => String(id))
987
+ .sort()
988
+ assert.deepEqual(detachedIds, ['c2', 'c3'])
989
+ })
990
+
991
+ it('mixed — single submit performs create+attach, update, and detach in one diff', async () => {
992
+ const child = makeFakeChildModel([
993
+ { id: 'c1', name: 'red' },
994
+ { id: 'c2', name: 'blue' },
995
+ ])
996
+ const setup = makeM2MParentSetup({
997
+ childModel: child.model,
998
+ childRows: child.rows,
999
+ relationName: 'tags',
1000
+ attachedIds: new Set(['c1', 'c2']),
1001
+ })
1002
+
1003
+ const form = Form.make()
1004
+ .schema([
1005
+ RepeaterField.make('tags')
1006
+ .relationship('tags')
1007
+ .schema([TextField.make('name')]),
1008
+ ])
1009
+ .save(async () => setup.makeRecord('a1'))
1010
+
1011
+ const submittedRows = [
1012
+ { __id: 'c1', name: 'crimson' },
1013
+ { name: 'fresh' },
1014
+ ]
1015
+ const result = await dispatchFormSubmit(
1016
+ form,
1017
+ { tags: submittedRows },
1018
+ { values: { tags: submittedRows }, record: setup.makeRecord('a1'), parentModel: setup.parentModel },
1019
+ )
1020
+ assert.equal(result.ok, true)
1021
+ assert.equal(child.calls.filter(c => c.kind === 'create').length, 1)
1022
+ assert.equal(child.calls.filter(c => c.kind === 'update').length, 1)
1023
+ assert.equal(child.calls.filter(c => c.kind === 'delete').length, 0)
1024
+ assert.equal(setup.pivotCalls.filter(c => c.kind === 'attach').length, 1)
1025
+ const detachCalls = setup.pivotCalls.filter(c => c.kind === 'detach') as Array<{ kind: 'detach'; ids: Array<string | number> | undefined }>
1026
+ const detachedIds = detachCalls.flatMap(c => c.ids ?? []).map(id => String(id))
1027
+ assert.deepEqual(detachedIds, ['c2'])
1028
+ })
1029
+
1030
+ it('descriptor lookup — explicit cfg.model wins over the relation entry thunk', async () => {
1031
+ const child = makeFakeChildModel([])
1032
+ const otherChild = makeFakeChildModel([])
1033
+ const setup = makeM2MParentSetup({
1034
+ childModel: child.model,
1035
+ childRows: child.rows,
1036
+ relationName: 'tags',
1037
+ attachedIds: new Set(),
1038
+ })
1039
+
1040
+ const form = Form.make()
1041
+ .schema([
1042
+ RepeaterField.make('tags')
1043
+ .relationship({ name: 'tags', model: otherChild.model })
1044
+ .schema([TextField.make('name')]),
1045
+ ])
1046
+ .save(async () => setup.makeRecord('a1'))
1047
+
1048
+ const submittedRows = [{ name: 'red' }]
1049
+ const result = await dispatchFormSubmit(
1050
+ form,
1051
+ { tags: submittedRows },
1052
+ { values: { tags: submittedRows }, parentModel: setup.parentModel },
1053
+ )
1054
+ assert.equal(result.ok, true)
1055
+ // Override model received the create, NOT the descriptor's model.
1056
+ assert.equal(otherChild.calls.filter(c => c.kind === 'create').length, 1)
1057
+ assert.equal(child.calls.filter(c => c.kind === 'create').length, 0)
1058
+ })
1059
+
1060
+ it('orderColumn — rejected under M2M v1 with a clear error', async () => {
1061
+ const child = makeFakeChildModel([])
1062
+ const setup = makeM2MParentSetup({
1063
+ childModel: child.model,
1064
+ childRows: child.rows,
1065
+ relationName: 'tags',
1066
+ attachedIds: new Set(),
1067
+ })
1068
+
1069
+ const form = Form.make()
1070
+ .schema([
1071
+ RepeaterField.make('tags')
1072
+ .relationship('tags')
1073
+ .orderColumn('sort')
1074
+ .schema([TextField.make('name')]),
1075
+ ])
1076
+ .save(async () => setup.makeRecord('a1'))
1077
+
1078
+ const submittedRows = [{ name: 'red' }]
1079
+ await assert.rejects(
1080
+ () => dispatchFormSubmit(
1081
+ form,
1082
+ { tags: submittedRows },
1083
+ { values: { tags: submittedRows }, parentModel: setup.parentModel },
1084
+ ),
1085
+ /orderColumn\(\) is not supported under 'belongsToMany'/,
1086
+ )
1087
+ })
1088
+
1089
+ it('missing accessor — clear error when parent exposes neither parent[rel]() nor a legacy related() shape', async () => {
1090
+ const child = makeFakeChildModel([])
1091
+ // Parent missing the prototype `tags()` method AND missing `related`.
1092
+ const parentModel: ModelLike & { relations: Record<string, unknown> } = {
1093
+ primaryKey: 'id',
1094
+ find: async () => null,
1095
+ create: async () => ({}),
1096
+ update: async () => ({}),
1097
+ delete: async () => {},
1098
+ query: () => makeQuery([]),
1099
+ relatedQuery: () => makeQuery([]),
1100
+ relations: {
1101
+ tags: { type: 'belongsToMany', model: () => child.model, pivotTable: 'pivot' },
1102
+ },
1103
+ }
1104
+
1105
+ const form = Form.make()
1106
+ .schema([
1107
+ RepeaterField.make('tags')
1108
+ .relationship('tags')
1109
+ .schema([TextField.make('name')]),
1110
+ ])
1111
+ .save(async () => ({ id: 'a1' }))
1112
+
1113
+ const submittedRows = [{ name: 'red' }]
1114
+ await assert.rejects(
1115
+ () => dispatchFormSubmit(
1116
+ form,
1117
+ { tags: submittedRows },
1118
+ { values: { tags: submittedRows }, parentModel },
1119
+ ),
1120
+ /could not resolve the pivot-mutation accessor/,
1121
+ )
1122
+ })
1123
+ })
1124
+
1125
+ describe('Repeater.relationship — morphToMany', () => {
1126
+ // Parent shape: `Post.tags: morphToMany(Tag, pivotTable: 'taggable',
1127
+ // morphName: 'taggable')`. The accessor handles the polymorphic stamp
1128
+ // on the pivot row internally — pilotiq doesn't see the morph cols.
1129
+ // Behavior is identical to belongsToMany from pilotiq's perspective.
1130
+
1131
+ it('create — same path as belongsToMany; the accessor handles polymorphic stamping internally', async () => {
1132
+ const child = makeFakeChildModel([])
1133
+ const attachedIds = new Set<string | number>()
1134
+ const pivotCalls: Array<{ kind: 'attach'; ids: Array<string | number> }> = []
1135
+ class Post {
1136
+ id?: string
1137
+ constructor(init?: Partial<{ id: string }>) { Object.assign(this, init) }
1138
+ tags() {
1139
+ return {
1140
+ attach: async (input: ReadonlyArray<string | number>) => {
1141
+ for (const id of input) attachedIds.add(id)
1142
+ pivotCalls.push({ kind: 'attach', ids: [...input] })
1143
+ },
1144
+ detach: async () => 0,
1145
+ sync: async () => ({ attached: [], detached: [] }),
1146
+ }
1147
+ }
1148
+ }
1149
+ const parentModel: ModelLike & { relations: Record<string, unknown> } = {
1150
+ primaryKey: 'id',
1151
+ find: async () => null,
1152
+ create: async () => ({}),
1153
+ update: async () => ({}),
1154
+ delete: async () => {},
1155
+ query: () => makeQuery([]),
1156
+ relatedQuery: () => makeQuery([]),
1157
+ relations: {
1158
+ tags: { type: 'morphToMany', model: () => child.model, pivotTable: 'taggable', morphName: 'taggable' },
1159
+ },
1160
+ }
1161
+
1162
+ const form = Form.make()
1163
+ .schema([
1164
+ RepeaterField.make('tags')
1165
+ .relationship('tags')
1166
+ .schema([TextField.make('name').required()]),
1167
+ ])
1168
+ .save(async () => new Post({ id: 'p1' }))
1169
+
1170
+ const submittedRows = [{ name: 'red' }, { name: 'blue' }]
1171
+ const result = await dispatchFormSubmit(
1172
+ form,
1173
+ { tags: submittedRows },
1174
+ { values: { tags: submittedRows }, parentModel },
1175
+ )
1176
+ assert.equal(result.ok, true)
1177
+ assert.equal(child.calls.filter(c => c.kind === 'create').length, 2)
1178
+ assert.equal(pivotCalls.filter(c => c.kind === 'attach').length, 2)
1179
+ })
1180
+ })
1181
+
1182
+ describe('Repeater.relationship — morphedByMany', () => {
1183
+ // Parent shape: `Tag.posts: morphedByMany(Post, pivotTable: 'taggable',
1184
+ // morphName: 'taggable')`. Inverse polymorphic side. Same accessor surface.
1185
+
1186
+ it('detach-only on row removal (parallel to belongsToMany / morphToMany)', async () => {
1187
+ const child = makeFakeChildModel([
1188
+ { id: 'c1', title: 'first' },
1189
+ { id: 'c2', title: 'second' },
1190
+ ])
1191
+ const attachedIds = new Set<string | number>(['c1', 'c2'])
1192
+ const pivotCalls: Array<{ kind: 'detach'; ids: Array<string | number> | undefined }> = []
1193
+ class Tag {
1194
+ id?: string
1195
+ constructor(init?: Partial<{ id: string }>) { Object.assign(this, init) }
1196
+ posts() {
1197
+ return {
1198
+ attach: async () => {},
1199
+ detach: async (ids?: ReadonlyArray<string | number>) => {
1200
+ if (ids === undefined) { attachedIds.clear(); pivotCalls.push({ kind: 'detach', ids: undefined }); return 0 }
1201
+ for (const id of ids) attachedIds.delete(id)
1202
+ pivotCalls.push({ kind: 'detach', ids: [...ids] })
1203
+ return ids.length
1204
+ },
1205
+ sync: async () => ({ attached: [], detached: [] }),
1206
+ }
1207
+ }
1208
+ }
1209
+ const parentModel: ModelLike & { relations: Record<string, unknown> } = {
1210
+ primaryKey: 'id',
1211
+ find: async () => null,
1212
+ create: async () => ({}),
1213
+ update: async () => ({}),
1214
+ delete: async () => {},
1215
+ query: () => makeQuery([]),
1216
+ relatedQuery: () => makeQuery(child.rows.filter(r => attachedIds.has(r['id'] as string | number))),
1217
+ relations: {
1218
+ posts: { type: 'morphedByMany', model: () => child.model, pivotTable: 'taggable', morphName: 'taggable' },
1219
+ },
1220
+ }
1221
+
1222
+ const form = Form.make()
1223
+ .schema([
1224
+ RepeaterField.make('posts')
1225
+ .relationship('posts')
1226
+ .schema([TextField.make('title')]),
1227
+ ])
1228
+ .save(async () => new Tag({ id: 't1' }))
1229
+
1230
+ const submittedRows = [{ __id: 'c1', title: 'first' }]
1231
+ const result = await dispatchFormSubmit(
1232
+ form,
1233
+ { posts: submittedRows },
1234
+ { values: { posts: submittedRows }, record: new Tag({ id: 't1' }), parentModel },
1235
+ )
1236
+ assert.equal(result.ok, true)
1237
+ // Detach c2; never touch M.delete.
1238
+ assert.equal(child.calls.filter(c => c.kind === 'delete').length, 0)
1239
+ const detachedIds = pivotCalls.flatMap(c => c.ids ?? []).map(id => String(id))
1240
+ assert.deepEqual(detachedIds, ['c2'])
1241
+ })
1242
+ })
1243
+
1244
+ /**
1245
+ * Test harness for M2M pivot-extras — `withPivot(...cols)` projection on
1246
+ * the load query + `updatePivot(id, data)` + per-id-pivot `attach({ id:
1247
+ * data })` on the accessor. Mirrors rudder ORM's
1248
+ * `feat(orm): pivot-extras read/update + per-id sync` (PR #251).
1249
+ *
1250
+ * Each child row has an associated pivot row keyed by the child's PK.
1251
+ * `withPivot` stamps the listed pivot columns onto each row under
1252
+ * `row.pivot = { … }`. `updatePivot` patches the matching pivot row.
1253
+ */
1254
+ function makeM2MParentSetupWithPivot(opts: {
1255
+ childModel: ModelLike
1256
+ childRows: FakeRecord[]
1257
+ relationName: string
1258
+ /** Pivot rows keyed by child PK. Each entry holds the extra columns. */
1259
+ pivot: Map<string, Record<string, unknown>>
1260
+ attachedIds?: Set<string | number>
1261
+ }) {
1262
+ const { childModel, childRows, relationName, pivot } = opts
1263
+ const attachedIds = opts.attachedIds
1264
+ ?? new Set<string | number>(childRows.map(r => r['id'] as string | number))
1265
+ const pivotCalls: Array<
1266
+ | { kind: 'attach'; ids: Array<string | number>; pivot?: Record<string, Record<string, unknown>> }
1267
+ | { kind: 'detach'; ids: Array<string | number> | undefined }
1268
+ | { kind: 'updatePivot'; id: string | number; data: Record<string, unknown> }
1269
+ > = []
1270
+
1271
+ function projectPivot(rows: FakeRecord[], cols: string[]): FakeRecord[] {
1272
+ return rows.map(r => {
1273
+ const pk = String(r['id'])
1274
+ const pe = pivot.get(pk) ?? {}
1275
+ const proj: Record<string, unknown> = {}
1276
+ for (const c of cols) proj[c] = pe[c] ?? null
1277
+ return { ...r, pivot: proj }
1278
+ })
1279
+ }
1280
+
1281
+ /** Pivot-aware fake query — adds `withPivot` to the chain. */
1282
+ function makePivotAwareQuery(rows: FakeRecord[]): ModelQuery {
1283
+ let pivotCols: string[] | undefined
1284
+ const q: ModelQuery = {
1285
+ where: () => q,
1286
+ orWhere: () => q,
1287
+ orderBy: () => q,
1288
+ withPivot(...cols: string[]) {
1289
+ pivotCols = cols
1290
+ return q
1291
+ },
1292
+ paginate: async () => {
1293
+ const projected = pivotCols ? projectPivot(rows, pivotCols) : rows.slice()
1294
+ return { data: projected, total: projected.length }
1295
+ },
1296
+ }
1297
+ return q
1298
+ }
1299
+
1300
+ class Parent {
1301
+ id?: string
1302
+ constructor(init?: Partial<{ id: string }>) { Object.assign(this, init) }
1303
+ [relationName]() {
1304
+ return {
1305
+ attach: async (input: ReadonlyArray<string | number> | Record<string, Record<string, unknown>>) => {
1306
+ if (Array.isArray(input)) {
1307
+ const ids = [...input]
1308
+ for (const id of ids) attachedIds.add(id)
1309
+ pivotCalls.push({ kind: 'attach', ids })
1310
+ } else {
1311
+ const map = input as Record<string, Record<string, unknown>>
1312
+ const ids = Object.keys(map).map(k => /^\d+$/.test(k) ? Number(k) : k) as Array<string | number>
1313
+ for (const id of ids) {
1314
+ attachedIds.add(id)
1315
+ pivot.set(String(id), { ...(pivot.get(String(id)) ?? {}), ...map[String(id)] })
1316
+ }
1317
+ pivotCalls.push({ kind: 'attach', ids, pivot: map })
1318
+ }
1319
+ },
1320
+ detach: async (ids?: ReadonlyArray<string | number>) => {
1321
+ if (ids === undefined) {
1322
+ const removed = [...attachedIds]
1323
+ attachedIds.clear()
1324
+ for (const id of removed) pivot.delete(String(id))
1325
+ pivotCalls.push({ kind: 'detach', ids: undefined })
1326
+ return removed.length
1327
+ }
1328
+ for (const id of ids) {
1329
+ attachedIds.delete(id)
1330
+ pivot.delete(String(id))
1331
+ }
1332
+ pivotCalls.push({ kind: 'detach', ids: [...ids] })
1333
+ return ids.length
1334
+ },
1335
+ updatePivot: async (id: string | number, data: Record<string, unknown>): Promise<number> => {
1336
+ pivotCalls.push({ kind: 'updatePivot', id, data: { ...data } })
1337
+ const key = String(id)
1338
+ if (!pivot.has(key)) return 0
1339
+ pivot.set(key, { ...pivot.get(key), ...data })
1340
+ return 1
1341
+ },
1342
+ }
1343
+ }
1344
+ }
1345
+
1346
+ const parentModel: ModelLike & { relations: Record<string, unknown> } = {
1347
+ primaryKey: 'id',
1348
+ find: async () => null,
1349
+ create: async () => ({}),
1350
+ update: async () => ({}),
1351
+ delete: async () => {},
1352
+ query: () => makeQuery([]),
1353
+ relatedQuery: () => makePivotAwareQuery(
1354
+ childRows.filter(r => attachedIds.has(r['id'] as string | number)),
1355
+ ),
1356
+ relations: {
1357
+ [relationName]: { type: 'belongsToMany', model: () => childModel, pivotTable: 'pivot' },
1358
+ },
1359
+ }
1360
+ return {
1361
+ parentModel,
1362
+ pivotCalls,
1363
+ pivotState: pivot,
1364
+ attachedIds,
1365
+ makeRecord: (id: string) => new Parent({ id }),
1366
+ }
1367
+ }
1368
+
1369
+ describe('Repeater.relationship — pivotColumns', () => {
1370
+ // Surface check: setter requires .relationship() first; round-trips into cfg.
1371
+ it('Repeater.pivotColumns([…]) requires .relationship() first', () => {
1372
+ assert.throws(
1373
+ () => RepeaterField.make('tags').pivotColumns(['role']),
1374
+ /requires relationship\(\) to be configured first/,
1375
+ )
1376
+ })
1377
+
1378
+ it('Repeater.pivotColumns([…]) writes to the relationship cfg', () => {
1379
+ const r = RepeaterField.make('tags')
1380
+ .relationship('tags')
1381
+ .pivotColumns(['role', 'assignedAt'])
1382
+ assert.deepEqual(r.getRelationship()?.pivotColumns, ['role', 'assignedAt'])
1383
+ })
1384
+
1385
+ it('load — withPivot ferries the configured columns; row values flatten onto the form data', async () => {
1386
+ const child = makeFakeChildModel([
1387
+ { id: 'c1', name: 'red' },
1388
+ { id: 'c2', name: 'blue' },
1389
+ ])
1390
+ const setup = makeM2MParentSetupWithPivot({
1391
+ childModel: child.model,
1392
+ childRows: child.rows,
1393
+ relationName: 'tags',
1394
+ pivot: new Map([
1395
+ ['c1', { role: 'owner' }],
1396
+ ['c2', { role: 'editor' }],
1397
+ ]),
1398
+ attachedIds: new Set(['c1', 'c2']),
1399
+ })
1400
+
1401
+ const form = Form.make().schema([
1402
+ RepeaterField.make('tags')
1403
+ .relationship('tags')
1404
+ .pivotColumns(['role'])
1405
+ .schema([TextField.make('name'), TextField.make('role')]),
1406
+ ])
1407
+
1408
+ const filled = await applyRelationshipRepeaterFill(
1409
+ form, {}, setup.makeRecord('a1'), setup.parentModel,
1410
+ )
1411
+
1412
+ const rows = filled['tags'] as Array<Record<string, unknown>>
1413
+ assert.equal(rows.length, 2)
1414
+ assert.equal(rows[0]?.['name'], 'red')
1415
+ assert.equal(rows[0]?.['role'], 'owner')
1416
+ assert.equal(rows[0]?.['__id'], 'c1')
1417
+ assert.equal(rows[1]?.['name'], 'blue')
1418
+ assert.equal(rows[1]?.['role'], 'editor')
1419
+ // pivot envelope is dropped — it's an internal carrier, not form data.
1420
+ assert.equal('pivot' in (rows[0] ?? {}), false)
1421
+ })
1422
+
1423
+ it('save (existing row) — pivot extras route through updatePivot, child fields through M.update', async () => {
1424
+ const child = makeFakeChildModel([
1425
+ { id: 'c1', name: 'red' },
1426
+ ])
1427
+ const setup = makeM2MParentSetupWithPivot({
1428
+ childModel: child.model,
1429
+ childRows: child.rows,
1430
+ relationName: 'tags',
1431
+ pivot: new Map([['c1', { role: 'editor' }]]),
1432
+ attachedIds: new Set(['c1']),
1433
+ })
1434
+
1435
+ const form = Form.make()
1436
+ .schema([
1437
+ RepeaterField.make('tags')
1438
+ .relationship('tags')
1439
+ .pivotColumns(['role'])
1440
+ .schema([TextField.make('name'), TextField.make('role')]),
1441
+ ])
1442
+ .save(async () => setup.makeRecord('a1'))
1443
+
1444
+ const submittedRows = [{ __id: 'c1', name: 'crimson', role: 'owner' }]
1445
+ const result = await dispatchFormSubmit(
1446
+ form,
1447
+ { tags: submittedRows },
1448
+ { values: { tags: submittedRows }, record: setup.makeRecord('a1'), parentModel: setup.parentModel },
1449
+ )
1450
+ assert.equal(result.ok, true)
1451
+
1452
+ // Child row got the non-pivot field; pivot col was NOT smuggled through.
1453
+ const updates = child.calls.filter(c => c.kind === 'update') as Array<{ kind: 'update'; id: string | number; data: Record<string, unknown> }>
1454
+ assert.equal(updates.length, 1)
1455
+ assert.equal(updates[0]?.data['name'], 'crimson')
1456
+ assert.equal('role' in (updates[0]?.data ?? {}), false)
1457
+
1458
+ // Pivot row patched via updatePivot.
1459
+ const pivotUpdates = setup.pivotCalls.filter(c => c.kind === 'updatePivot') as Array<{ kind: 'updatePivot'; id: string | number; data: Record<string, unknown> }>
1460
+ assert.equal(pivotUpdates.length, 1)
1461
+ assert.equal(pivotUpdates[0]?.id, 'c1')
1462
+ assert.deepEqual(pivotUpdates[0]?.data, { role: 'owner' })
1463
+ })
1464
+
1465
+ it('save (new row) — attach uses the per-id-pivot map shape', async () => {
1466
+ const child = makeFakeChildModel([])
1467
+ const setup = makeM2MParentSetupWithPivot({
1468
+ childModel: child.model,
1469
+ childRows: child.rows,
1470
+ relationName: 'tags',
1471
+ pivot: new Map(),
1472
+ attachedIds: new Set(),
1473
+ })
1474
+
1475
+ const form = Form.make()
1476
+ .schema([
1477
+ RepeaterField.make('tags')
1478
+ .relationship('tags')
1479
+ .pivotColumns(['role'])
1480
+ .schema([TextField.make('name'), TextField.make('role')]),
1481
+ ])
1482
+ .save(async () => setup.makeRecord('a1'))
1483
+
1484
+ const submittedRows = [{ name: 'red', role: 'owner' }]
1485
+ const result = await dispatchFormSubmit(
1486
+ form,
1487
+ { tags: submittedRows },
1488
+ { values: { tags: submittedRows }, parentModel: setup.parentModel },
1489
+ )
1490
+ assert.equal(result.ok, true)
1491
+
1492
+ // Child created without `role` (pivot column).
1493
+ const creates = child.calls.filter(c => c.kind === 'create') as Array<{ kind: 'create'; data: Record<string, unknown> }>
1494
+ assert.equal(creates.length, 1)
1495
+ assert.equal(creates[0]?.data['name'], 'red')
1496
+ assert.equal('role' in (creates[0]?.data ?? {}), false)
1497
+
1498
+ // attach received the per-id-pivot map.
1499
+ const attachCalls = setup.pivotCalls.filter(c => c.kind === 'attach') as Array<{ kind: 'attach'; pivot?: Record<string, Record<string, unknown>> }>
1500
+ assert.equal(attachCalls.length, 1)
1501
+ assert.ok(attachCalls[0]?.pivot, 'attach should have received a per-id pivot map')
1502
+ const map = attachCalls[0]!.pivot!
1503
+ const onlyKey = Object.keys(map)[0]!
1504
+ assert.deepEqual(map[onlyKey], { role: 'owner' })
1505
+ })
1506
+
1507
+ it('save (existing row, no pivot edit) — skips updatePivot when payload has no pivot keys', async () => {
1508
+ const child = makeFakeChildModel([
1509
+ { id: 'c1', name: 'red' },
1510
+ ])
1511
+ const setup = makeM2MParentSetupWithPivot({
1512
+ childModel: child.model,
1513
+ childRows: child.rows,
1514
+ relationName: 'tags',
1515
+ pivot: new Map([['c1', { role: 'editor' }]]),
1516
+ attachedIds: new Set(['c1']),
1517
+ })
1518
+
1519
+ const form = Form.make()
1520
+ .schema([
1521
+ RepeaterField.make('tags')
1522
+ .relationship('tags')
1523
+ .pivotColumns(['role'])
1524
+ .schema([TextField.make('name'), TextField.make('role')]),
1525
+ ])
1526
+ .save(async () => setup.makeRecord('a1'))
1527
+
1528
+ // Submit only changes the child column; role omitted entirely.
1529
+ const submittedRows = [{ __id: 'c1', name: 'crimson' }]
1530
+ const result = await dispatchFormSubmit(
1531
+ form,
1532
+ { tags: submittedRows },
1533
+ { values: { tags: submittedRows }, record: setup.makeRecord('a1'), parentModel: setup.parentModel },
1534
+ )
1535
+ assert.equal(result.ok, true)
1536
+ assert.equal(setup.pivotCalls.filter(c => c.kind === 'updatePivot').length, 0)
1537
+ })
1538
+
1539
+ it('save — throws a clear error when accessor lacks updatePivot but pivot extras changed', async () => {
1540
+ const child = makeFakeChildModel([{ id: 'c1', name: 'red' }])
1541
+ // Build a parent whose accessor does NOT expose updatePivot.
1542
+ const accessorWithoutUpdate = {
1543
+ attach: async () => {},
1544
+ detach: async () => 0,
1545
+ }
1546
+ class Parent {
1547
+ id?: string
1548
+ constructor(init?: Partial<{ id: string }>) { Object.assign(this, init) }
1549
+ tags() { return accessorWithoutUpdate }
1550
+ }
1551
+ const parentModel: ModelLike & { relations: Record<string, unknown> } = {
1552
+ primaryKey: 'id',
1553
+ find: async () => null,
1554
+ create: async () => ({}),
1555
+ update: async () => ({}),
1556
+ delete: async () => {},
1557
+ query: () => makeQuery([]),
1558
+ relatedQuery: () => makeQuery([{ id: 'c1', name: 'red' }]),
1559
+ relations: {
1560
+ tags: { type: 'belongsToMany', model: () => child.model, pivotTable: 'pivot' },
1561
+ },
1562
+ }
1563
+
1564
+ const form = Form.make()
1565
+ .schema([
1566
+ RepeaterField.make('tags')
1567
+ .relationship('tags')
1568
+ .pivotColumns(['role'])
1569
+ .schema([TextField.make('name'), TextField.make('role')]),
1570
+ ])
1571
+ .save(async () => new Parent({ id: 'a1' }))
1572
+
1573
+ const submittedRows = [{ __id: 'c1', name: 'red', role: 'owner' }]
1574
+ await assert.rejects(
1575
+ () => dispatchFormSubmit(
1576
+ form,
1577
+ { tags: submittedRows },
1578
+ { values: { tags: submittedRows }, record: new Parent({ id: 'a1' }), parentModel },
1579
+ ),
1580
+ /requires a rudder ORM with `updatePivot`/,
1581
+ )
1582
+ })
1583
+
1584
+ it('loadRelationRows — passes pivotColumns into withPivot when supported', async () => {
1585
+ let seenCols: string[] | undefined
1586
+ const q: ModelQuery = {
1587
+ where: () => q,
1588
+ orWhere: () => q,
1589
+ orderBy: () => q,
1590
+ withPivot(...cols: string[]) {
1591
+ seenCols = cols
1592
+ return q
1593
+ },
1594
+ paginate: async () => ({ data: [], total: 0 }),
1595
+ }
1596
+ const M: ModelLike = {
1597
+ primaryKey: 'id',
1598
+ find: async () => null,
1599
+ create: async () => ({}),
1600
+ update: async () => ({}),
1601
+ delete: async () => {},
1602
+ query: () => q,
1603
+ relatedQuery: () => q,
1604
+ }
1605
+ await loadRelationRows(M, {}, 'tags', ['role', 'assignedAt'])
1606
+ assert.deepEqual(seenCols, ['role', 'assignedAt'])
1607
+ })
1608
+
1609
+ it('loadRelationRows — silently skips withPivot on a model that does not implement it', async () => {
1610
+ // A model whose query has no `withPivot` method — pilotiq should
1611
+ // call paginate without throwing.
1612
+ const q: ModelQuery = {
1613
+ where: () => q,
1614
+ orWhere: () => q,
1615
+ orderBy: () => q,
1616
+ paginate: async () => ({ data: [], total: 0 }),
1617
+ }
1618
+ const M: ModelLike = {
1619
+ primaryKey: 'id',
1620
+ find: async () => null,
1621
+ create: async () => ({}),
1622
+ update: async () => ({}),
1623
+ delete: async () => {},
1624
+ query: () => q,
1625
+ relatedQuery: () => q,
1626
+ }
1627
+ const rows = await loadRelationRows(M, {}, 'tags', ['role'])
1628
+ assert.deepEqual(rows, [])
1629
+ })
1630
+ })