@nadicodeai/ui 12.1.165 → 12.1.166

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 (363) hide show
  1. package/AGENTS.md +8 -9
  2. package/README.md +2 -17
  3. package/dist/components/accordion.d.ts.map +1 -1
  4. package/dist/components/accordion.js +2 -1
  5. package/dist/components/account-menu.d.ts.map +1 -1
  6. package/dist/components/account-menu.js +5 -3
  7. package/dist/components/activity-calendar.d.ts.map +1 -1
  8. package/dist/components/activity-calendar.js +3 -2
  9. package/dist/components/agent-card.js +1 -1
  10. package/dist/components/agent-orb.d.ts.map +1 -1
  11. package/dist/components/agent-orb.js +1 -0
  12. package/dist/components/agent-profile.d.ts.map +1 -1
  13. package/dist/components/agent-profile.js +7 -4
  14. package/dist/components/alert-dialog.d.ts +1 -1
  15. package/dist/components/alert-dialog.d.ts.map +1 -1
  16. package/dist/components/alert-dialog.js +3 -3
  17. package/dist/components/app-mark.js +1 -1
  18. package/dist/components/avatar.js +8 -8
  19. package/dist/components/badge.d.ts +11 -16
  20. package/dist/components/badge.d.ts.map +1 -1
  21. package/dist/components/badge.js +7 -43
  22. package/dist/components/breadcrumb.d.ts.map +1 -1
  23. package/dist/components/breadcrumb.js +3 -2
  24. package/dist/components/button.d.ts +5 -21
  25. package/dist/components/button.d.ts.map +1 -1
  26. package/dist/components/button.js +6 -76
  27. package/dist/components/calendar.d.ts +1 -1
  28. package/dist/components/calendar.d.ts.map +1 -1
  29. package/dist/components/calendar.js +3 -3
  30. package/dist/components/card-row.js +1 -1
  31. package/dist/components/card.d.ts +4 -1
  32. package/dist/components/card.d.ts.map +1 -1
  33. package/dist/components/card.js +3 -2
  34. package/dist/components/catalog-drawer.d.ts.map +1 -1
  35. package/dist/components/catalog-drawer.js +3 -2
  36. package/dist/components/chart-axis.d.ts.map +1 -1
  37. package/dist/components/chart-axis.js +0 -2
  38. package/dist/components/chart-bar-horizontal.d.ts.map +1 -1
  39. package/dist/components/chart-bar-horizontal.js +16 -14
  40. package/dist/components/chart-card.d.ts.map +1 -1
  41. package/dist/components/chart-card.js +0 -1
  42. package/dist/components/chart.d.ts.map +1 -1
  43. package/dist/components/chart.js +18 -32
  44. package/dist/components/checkbox.d.ts +3 -2
  45. package/dist/components/checkbox.d.ts.map +1 -1
  46. package/dist/components/checkbox.js +4 -3
  47. package/dist/components/choicebox.d.ts +5 -5
  48. package/dist/components/choicebox.d.ts.map +1 -1
  49. package/dist/components/choicebox.js +6 -5
  50. package/dist/components/clearable-input.d.ts.map +1 -1
  51. package/dist/components/clearable-input.js +2 -2
  52. package/dist/components/code-block.d.ts.map +1 -1
  53. package/dist/components/code-block.js +4 -2
  54. package/dist/components/collapsible.js +1 -1
  55. package/dist/components/collection-filters.d.ts.map +1 -1
  56. package/dist/components/collection-filters.js +1 -0
  57. package/dist/components/collection-page.js +1 -1
  58. package/dist/components/collection-sort.d.ts.map +1 -1
  59. package/dist/components/collection-sort.js +1 -4
  60. package/dist/components/collection-toolbar.js +1 -1
  61. package/dist/components/combobox.js +4 -4
  62. package/dist/components/command.d.ts.map +1 -1
  63. package/dist/components/command.js +1 -1
  64. package/dist/components/confirm-dialog.js +2 -2
  65. package/dist/components/context-menu.d.ts +1 -1
  66. package/dist/components/context-menu.d.ts.map +1 -1
  67. package/dist/components/copy-button.js +1 -1
  68. package/dist/components/data-table.d.ts +11 -3
  69. package/dist/components/data-table.d.ts.map +1 -1
  70. package/dist/components/data-table.js +12 -4
  71. package/dist/components/dialog.d.ts +1 -1
  72. package/dist/components/dialog.d.ts.map +1 -1
  73. package/dist/components/dialog.js +2 -2
  74. package/dist/components/drawer.d.ts +4 -4
  75. package/dist/components/drawer.d.ts.map +1 -1
  76. package/dist/components/drawer.js +14 -15
  77. package/dist/components/dropdown-menu.d.ts +1 -1
  78. package/dist/components/dropdown-menu.d.ts.map +1 -1
  79. package/dist/components/dropdown-menu.js +9 -5
  80. package/dist/components/empty-art.js +1 -1
  81. package/dist/components/empty-state.js +2 -2
  82. package/dist/components/entry-scene.d.ts.map +1 -1
  83. package/dist/components/entry-scene.js +2 -3
  84. package/dist/components/field.js +3 -3
  85. package/dist/components/file-diff.d.ts.map +1 -1
  86. package/dist/components/file-diff.js +5 -2
  87. package/dist/components/footer.d.ts +1 -1
  88. package/dist/components/footer.d.ts.map +1 -1
  89. package/dist/components/footer.js +3 -3
  90. package/dist/components/form-dialog.d.ts.map +1 -1
  91. package/dist/components/form-dialog.js +2 -1
  92. package/dist/components/form-drawer.d.ts +7 -9
  93. package/dist/components/form-drawer.d.ts.map +1 -1
  94. package/dist/components/form-drawer.js +5 -5
  95. package/dist/components/form.d.ts +26 -0
  96. package/dist/components/form.d.ts.map +1 -0
  97. package/dist/components/form.js +29 -0
  98. package/dist/components/gauge.js +2 -2
  99. package/dist/components/heatmap/calendar-heatmap.d.ts.map +1 -1
  100. package/dist/components/heatmap/calendar-heatmap.js +15 -13
  101. package/dist/components/hive-scene.d.ts.map +1 -1
  102. package/dist/components/hive-scene.js +3 -1
  103. package/dist/components/hover-card.d.ts +1 -1
  104. package/dist/components/hover-card.d.ts.map +1 -1
  105. package/dist/components/hover-card.js +1 -1
  106. package/dist/components/input-group.d.ts +5 -6
  107. package/dist/components/input-group.d.ts.map +1 -1
  108. package/dist/components/input-group.js +8 -11
  109. package/dist/components/input-otp.d.ts.map +1 -1
  110. package/dist/components/input-otp.js +2 -2
  111. package/dist/components/item.d.ts +1 -1
  112. package/dist/components/item.js +2 -2
  113. package/dist/components/kbd.d.ts +6 -9
  114. package/dist/components/kbd.d.ts.map +1 -1
  115. package/dist/components/kbd.js +7 -3
  116. package/dist/components/message-notice.js +1 -1
  117. package/dist/components/middle-truncate.d.ts.map +1 -1
  118. package/dist/components/middle-truncate.js +1 -0
  119. package/dist/components/model-identity.d.ts +4 -4
  120. package/dist/components/model-identity.d.ts.map +1 -1
  121. package/dist/components/model-identity.js +2 -2
  122. package/dist/components/model-mark.d.ts.map +1 -1
  123. package/dist/components/model-mark.js +4 -2
  124. package/dist/components/multi-select.js +2 -2
  125. package/dist/components/native-select.d.ts.map +1 -1
  126. package/dist/components/native-select.js +2 -6
  127. package/dist/components/notice-banner.js +1 -1
  128. package/dist/components/object-page.js +2 -2
  129. package/dist/components/object-workspace.d.ts.map +1 -1
  130. package/dist/components/object-workspace.js +1 -3
  131. package/dist/components/overview-page.d.ts +1 -1
  132. package/dist/components/overview-page.d.ts.map +1 -1
  133. package/dist/components/overview-page.js +2 -1
  134. package/dist/components/page-actions.d.ts.map +1 -1
  135. package/dist/components/page-actions.js +9 -4
  136. package/dist/components/pairing-code.js +1 -1
  137. package/dist/components/pending-label.d.ts.map +1 -1
  138. package/dist/components/pending-label.js +0 -1
  139. package/dist/components/popover.d.ts +1 -1
  140. package/dist/components/popover.d.ts.map +1 -1
  141. package/dist/components/portal-sidebar-frame.d.ts.map +1 -1
  142. package/dist/components/portal-sidebar-frame.js +1 -1
  143. package/dist/components/product-message.d.ts.map +1 -1
  144. package/dist/components/product-message.js +1 -0
  145. package/dist/components/progress-checklist.d.ts.map +1 -1
  146. package/dist/components/progress-checklist.js +2 -2
  147. package/dist/components/progress.d.ts.map +1 -1
  148. package/dist/components/progress.js +5 -4
  149. package/dist/components/radio-group.js +2 -2
  150. package/dist/components/reading-column.js +1 -1
  151. package/dist/components/record-identity.d.ts.map +1 -1
  152. package/dist/components/record-identity.js +2 -5
  153. package/dist/components/record-list.d.ts.map +1 -1
  154. package/dist/components/record-list.js +4 -3
  155. package/dist/components/record-sheet.js +1 -1
  156. package/dist/components/record-time.d.ts.map +1 -1
  157. package/dist/components/record-time.js +2 -1
  158. package/dist/components/resizable.d.ts.map +1 -1
  159. package/dist/components/resizable.js +1 -1
  160. package/dist/components/row-edit-form.d.ts +2 -1
  161. package/dist/components/row-edit-form.d.ts.map +1 -1
  162. package/dist/components/row-edit-form.js +2 -1
  163. package/dist/components/run-timeline.js +1 -1
  164. package/dist/components/scope-switcher.js +1 -1
  165. package/dist/components/segmented-control.js +1 -1
  166. package/dist/components/select.js +1 -1
  167. package/dist/components/settings-facts.js +1 -1
  168. package/dist/components/settings-section.js +1 -1
  169. package/dist/components/sheet.d.ts +1 -1
  170. package/dist/components/sheet.d.ts.map +1 -1
  171. package/dist/components/sheet.js +1 -1
  172. package/dist/components/sidebar.d.ts +2 -2
  173. package/dist/components/sidebar.d.ts.map +1 -1
  174. package/dist/components/sidebar.js +16 -18
  175. package/dist/components/site-menu.d.ts +28 -0
  176. package/dist/components/site-menu.d.ts.map +1 -0
  177. package/dist/components/site-menu.js +23 -0
  178. package/dist/components/slider.d.ts.map +1 -1
  179. package/dist/components/slider.js +2 -1
  180. package/dist/components/snippet.d.ts +2 -0
  181. package/dist/components/snippet.d.ts.map +1 -1
  182. package/dist/components/snippet.js +13 -6
  183. package/dist/components/sonner.d.ts.map +1 -1
  184. package/dist/components/sonner.js +3 -4
  185. package/dist/components/status-badge.d.ts +1 -1
  186. package/dist/components/status-badge.d.ts.map +1 -1
  187. package/dist/components/switch.js +1 -1
  188. package/dist/components/table-card.js +1 -1
  189. package/dist/components/table.js +4 -4
  190. package/dist/components/tabs-nav.d.ts +52 -0
  191. package/dist/components/tabs-nav.d.ts.map +1 -0
  192. package/dist/components/tabs-nav.js +95 -0
  193. package/dist/components/tabs.d.ts +2 -56
  194. package/dist/components/tabs.d.ts.map +1 -1
  195. package/dist/components/tabs.js +2 -91
  196. package/dist/components/text-link.d.ts +1 -2
  197. package/dist/components/text-link.d.ts.map +1 -1
  198. package/dist/components/text-link.js +1 -2
  199. package/dist/components/textarea.d.ts +5 -2
  200. package/dist/components/textarea.d.ts.map +1 -1
  201. package/dist/components/textarea.js +12 -4
  202. package/dist/components/toast.d.ts +2 -0
  203. package/dist/components/toast.d.ts.map +1 -1
  204. package/dist/components/toast.js +4 -0
  205. package/dist/components/toggle-group.d.ts +3 -1
  206. package/dist/components/toggle-group.d.ts.map +1 -1
  207. package/dist/components/toggle-group.js +3 -6
  208. package/dist/components/toggle.js +1 -1
  209. package/dist/components/tooltip.d.ts +12 -12
  210. package/dist/components/tooltip.d.ts.map +1 -1
  211. package/dist/components/tooltip.js +9 -9
  212. package/dist/components/trend-badge.js +2 -2
  213. package/dist/components/update-card.js +1 -1
  214. package/dist/components/usage-meter.d.ts +1 -1
  215. package/dist/components/usage-meter.d.ts.map +1 -1
  216. package/dist/components/usage-meter.js +9 -7
  217. package/dist/components/waiting-line.js +1 -1
  218. package/dist/index.d.ts +3 -7
  219. package/dist/index.d.ts.map +1 -1
  220. package/dist/index.js +3 -7
  221. package/dist/internal/agent-orb-motion.d.ts +4 -1
  222. package/dist/internal/agent-orb-motion.d.ts.map +1 -1
  223. package/dist/internal/agent-orb-motion.js +9 -7
  224. package/dist/internal/agent-orb.d.ts +4 -1
  225. package/dist/internal/agent-orb.d.ts.map +1 -1
  226. package/dist/internal/agent-orb.js +7 -1
  227. package/dist/internal/assistant-ui/attachment.d.ts.map +1 -1
  228. package/dist/internal/assistant-ui/attachment.js +15 -15
  229. package/dist/internal/assistant-ui/follow-up-suggestions.js +1 -1
  230. package/dist/internal/assistant-ui/markdown-text.js +26 -25
  231. package/dist/internal/assistant-ui/reasoning.d.ts.map +1 -1
  232. package/dist/internal/assistant-ui/reasoning.js +13 -16
  233. package/dist/internal/assistant-ui/thread.d.ts.map +1 -1
  234. package/dist/internal/assistant-ui/thread.js +22 -16
  235. package/dist/internal/assistant-ui/tool-fallback.d.ts.map +1 -1
  236. package/dist/internal/assistant-ui/tool-fallback.js +29 -21
  237. package/dist/internal/assistant-ui/tool-group.d.ts.map +1 -1
  238. package/dist/internal/assistant-ui/tool-group.js +6 -7
  239. package/dist/internal/assistant-ui/tooltip-icon-button.js +1 -1
  240. package/dist/internal/badge-frame.d.ts +15 -0
  241. package/dist/internal/badge-frame.d.ts.map +1 -0
  242. package/dist/internal/badge-frame.js +44 -0
  243. package/dist/internal/button-classes.d.ts +20 -0
  244. package/dist/internal/button-classes.d.ts.map +1 -0
  245. package/dist/internal/button-classes.js +74 -0
  246. package/dist/internal/catalog-list.d.ts.map +1 -1
  247. package/dist/internal/catalog-list.js +8 -4
  248. package/dist/internal/collection-rail.js +3 -3
  249. package/dist/internal/form-drawer-frame.d.ts +10 -0
  250. package/dist/internal/form-drawer-frame.d.ts.map +1 -0
  251. package/dist/internal/form-drawer-frame.js +9 -0
  252. package/dist/internal/form-submit.d.ts +2 -1
  253. package/dist/internal/form-submit.d.ts.map +1 -1
  254. package/dist/internal/settings-save.d.ts.map +1 -1
  255. package/dist/internal/settings-save.js +4 -2
  256. package/dist/internal/status-mark.d.ts.map +1 -1
  257. package/dist/internal/status-mark.js +4 -1
  258. package/dist/internal/tab-row.d.ts +10 -0
  259. package/dist/internal/tab-row.d.ts.map +1 -0
  260. package/dist/internal/tab-row.js +2 -0
  261. package/dist/internal/table-compact-line.d.ts +1 -1
  262. package/dist/internal/table-compact-line.d.ts.map +1 -1
  263. package/dist/internal/table-compact-line.js +1 -1
  264. package/dist/internal/tooltip-content-frame.d.ts +14 -0
  265. package/dist/internal/tooltip-content-frame.d.ts.map +1 -0
  266. package/dist/internal/tooltip-content-frame.js +11 -0
  267. package/dist/lib/css-variables.d.ts +8 -0
  268. package/dist/lib/css-variables.d.ts.map +1 -0
  269. package/dist/lib/css-variables.js +4 -0
  270. package/dist/lib/merge-tokens.generated.js +1 -1
  271. package/dist/lib/use-copy-to-clipboard.d.ts.map +1 -1
  272. package/dist/lib/use-copy-to-clipboard.js +1 -2
  273. package/dist/showcase/samples.js +1 -1
  274. package/dist/showcase/specimens/choices.d.ts.map +1 -1
  275. package/dist/showcase/specimens/choices.js +3 -2
  276. package/dist/showcase/specimens/compositions.d.ts.map +1 -1
  277. package/dist/showcase/specimens/compositions.js +7 -5
  278. package/dist/showcase/specimens/data.d.ts.map +1 -1
  279. package/dist/showcase/specimens/data.js +1 -1
  280. package/dist/showcase/specimens/feedback.d.ts.map +1 -1
  281. package/dist/showcase/specimens/feedback.js +1 -2
  282. package/dist/showcase/specimens/index.d.ts +2 -0
  283. package/dist/showcase/specimens/index.d.ts.map +1 -1
  284. package/dist/showcase/specimens/inputs.d.ts +1 -0
  285. package/dist/showcase/specimens/inputs.d.ts.map +1 -1
  286. package/dist/showcase/specimens/inputs.js +5 -2
  287. package/dist/showcase/specimens/menus.d.ts +1 -0
  288. package/dist/showcase/specimens/menus.d.ts.map +1 -1
  289. package/dist/showcase/specimens/menus.js +7 -2
  290. package/dist/showcase/specimens/navigation.d.ts.map +1 -1
  291. package/dist/showcase/specimens/navigation.js +6 -5
  292. package/dist/showcase/specimens/status.d.ts.map +1 -1
  293. package/dist/showcase/specimens/status.js +4 -4
  294. package/dist/showcase/usage.generated.d.ts +1 -1
  295. package/dist/showcase/usage.generated.d.ts.map +1 -1
  296. package/dist/showcase/usage.generated.js +38 -16
  297. package/dist/standalone/index.js +1 -1
  298. package/dist/standalone/orb-page.js +10 -8
  299. package/dist/styles/components.css +1 -1
  300. package/package.json +32 -33
  301. package/dist/eslint/index.js +0 -224
  302. package/dist/eslint/rules/badge-is-static-label.js +0 -93
  303. package/dist/eslint/rules/button-icon-slot.js +0 -102
  304. package/dist/eslint/rules/button-link-for-navigation.js +0 -146
  305. package/dist/eslint/rules/button-no-aria-label-with-text.js +0 -75
  306. package/dist/eslint/rules/button-type-roles-only-in-buttons.js +0 -83
  307. package/dist/eslint/rules/choice-control-has-a-name.js +0 -98
  308. package/dist/eslint/rules/choice-group-in-fieldset.js +0 -86
  309. package/dist/eslint/rules/command-shortcut-single-owner.js +0 -118
  310. package/dist/eslint/rules/confirm-dialog-words.js +0 -63
  311. package/dist/eslint/rules/data-table-one-unsized-column.js +0 -96
  312. package/dist/eslint/rules/destructive-action-placement.js +0 -69
  313. package/dist/eslint/rules/disabled-choice-says-why.js +0 -71
  314. package/dist/eslint/rules/disabled-control-appearance.js +0 -75
  315. package/dist/eslint/rules/ellipsis-opens-a-dialog.js +0 -110
  316. package/dist/eslint/rules/empty-state-owner.js +0 -145
  317. package/dist/eslint/rules/input-group-owners.js +0 -155
  318. package/dist/eslint/rules/jsx-class-utility.js +0 -200
  319. package/dist/eslint/rules/kbd-children-no-modifier-text.js +0 -128
  320. package/dist/eslint/rules/literal-text.js +0 -41
  321. package/dist/eslint/rules/menu-owner.js +0 -75
  322. package/dist/eslint/rules/middle-truncate-placement.js +0 -69
  323. package/dist/eslint/rules/no-app-feature-component.js +0 -100
  324. package/dist/eslint/rules/no-bare-button-label.js +0 -55
  325. package/dist/eslint/rules/no-choice-control-wrapper.js +0 -97
  326. package/dist/eslint/rules/no-comment.js +0 -77
  327. package/dist/eslint/rules/no-control-in-read-only-surface.js +0 -106
  328. package/dist/eslint/rules/no-decorative-icon.js +0 -164
  329. package/dist/eslint/rules/no-deprecated-zod-api.js +0 -215
  330. package/dist/eslint/rules/no-global-patch.js +0 -115
  331. package/dist/eslint/rules/no-hand-built-busy.js +0 -104
  332. package/dist/eslint/rules/no-hand-built-theme-picker.js +0 -144
  333. package/dist/eslint/rules/no-hand-built-tile.js +0 -41
  334. package/dist/eslint/rules/no-hand-formatted-date.js +0 -87
  335. package/dist/eslint/rules/no-hand-stacked-avatars.js +0 -67
  336. package/dist/eslint/rules/no-nested-collapse.js +0 -60
  337. package/dist/eslint/rules/no-package-component-redefinition.js +0 -175
  338. package/dist/eslint/rules/no-search-in-form.js +0 -47
  339. package/dist/eslint/rules/no-shadcn-appearance-override.js +0 -305
  340. package/dist/eslint/rules/no-shell-prompt-in-snippet.js +0 -74
  341. package/dist/eslint/rules/no-stock-error-words.js +0 -56
  342. package/dist/eslint/rules/no-subject-derived-expectation.js +0 -183
  343. package/dist/eslint/rules/no-tooltip-around-labelled-field.js +0 -72
  344. package/dist/eslint/rules/no-weight-class-on-strong-in-type-role.js +0 -57
  345. package/dist/eslint/rules/no-write-in-subscription.js +0 -140
  346. package/dist/eslint/rules/one-submenu-level.js +0 -43
  347. package/dist/eslint/rules/one-toast-per-flow.js +0 -91
  348. package/dist/eslint/rules/option-count.js +0 -167
  349. package/dist/eslint/rules/page-command-link-not-render.js +0 -43
  350. package/dist/eslint/rules/page-composition.js +0 -232
  351. package/dist/eslint/rules/progress-needs-name.js +0 -57
  352. package/dist/eslint/rules/rendered-import.js +0 -121
  353. package/dist/eslint/rules/server-action-discipline.js +0 -233
  354. package/dist/eslint/rules/settings-limit-in-footer.js +0 -94
  355. package/dist/eslint/rules/skeleton-in-region.js +0 -80
  356. package/dist/eslint/rules/sortable-heading-uses-sort.js +0 -79
  357. package/dist/eslint/rules/table-cell-inset-owner.js +0 -107
  358. package/dist/eslint/rules/tabs-selection-is-controlled.js +0 -47
  359. package/dist/eslint/rules/test-files-from-one-definition.js +0 -67
  360. package/dist/eslint/rules/tooltip-only-arrow.js +0 -38
  361. package/dist/eslint/rules/tooltip-trigger-accessible-name.js +0 -114
  362. package/dist/eslint/rules/workspace-declares-imports.js +0 -163
  363. package/dist/eslint/strict-design-system.js +0 -250
@@ -1,232 +0,0 @@
1
- /*
2
- * Standard tool checked first: `no-restricted-imports` bans a module or a
3
- * name, but a part may be imported for a type or by its owner, and what is
4
- * wrong is rendering it; `no-restricted-syntax` matches an element by the name
5
- * the file wrote, which an import alias (`PageHeader as Header`) slips past.
6
- * This rule joins the import binding to the element that renders it.
7
- */
8
- import {
9
- findBelow,
10
- isRenderedChild,
11
- packageModules,
12
- partOf,
13
- renderedImports,
14
- staticAttribute,
15
- walk,
16
- } from "./rendered-import.js";
17
-
18
- const PART_FIX = {
19
- PageHeader: "PageHeader belongs to the product's chrome or a composition's `title`.",
20
- ReadingColumn: "A page's column is its composition's: SettingsPage, or the product's page frame.",
21
- TableCard:
22
- "A list of records is CollectionPage or RecordList; a comparison is a DataTable, which brings its card.",
23
- CollectionToolbar: "The toolbar is CollectionPage's: `search`, `filters`, `sort`.",
24
- CollectionSort: "The toolbar is CollectionPage's: `search`, `filters`, `sort`.",
25
- };
26
-
27
- const PACKAGE_MODULE = {
28
- PageHeader: "page-header",
29
- ReadingColumn: "reading-column",
30
- TableCard: "table-card",
31
- CollectionToolbar: "collection-toolbar",
32
- CollectionSort: "collection-sort",
33
- DataTable: "data-table",
34
- FormDrawer: "form-drawer",
35
- SettingsSection: "settings-section",
36
- TabsTrigger: "tabs",
37
- TabsLink: "tabs",
38
- Badge: "badge",
39
- StatusBadge: "status-badge",
40
- Tag: "tag",
41
- };
42
-
43
- const TABS = new Set(["TabsTrigger", "TabsLink"]);
44
- const COUNT_MARKS = new Set(["Badge", "StatusBadge", "Tag"]);
45
- const countName = (name) =>
46
- /(?:^|_)(?:count|total|length|size|unread|number)$/i.test(name) ||
47
- /[a-z0-9](?:Count|Total|Length|Size|Unread)$/.test(name);
48
-
49
- function exportersOf(modules) {
50
- const parts = new Map();
51
- const add = (name, from, part) => parts.set(name, [...(parts.get(name) ?? []), { from, part }]);
52
- for (const [part, module] of Object.entries(PACKAGE_MODULE)) {
53
- for (const from of packageModules(module)) add(part, from, part);
54
- for (const adapter of modules[part] ?? []) {
55
- // oxlint-disable-next-line anti-slop/no-runtime-typeof -- a composition's adapter list holds a module path or a { name, from } pair; only typeof tells them apart.
56
- if (typeof adapter === "string") add(part, adapter, part);
57
- else add(adapter.name, adapter.from, part);
58
- }
59
- }
60
- return parts;
61
- }
62
-
63
- const isObjectKey = (node) =>
64
- node.parent.type === "Property" && node.parent.key === node && !node.parent.computed;
65
-
66
- function readsACount(expression, visitorKeys) {
67
- // oxlint-disable-next-line anti-slop/no-runtime-typeof -- ESTree types Literal.value as string | number | boolean | RegExp | bigint | null; only typeof picks the number.
68
- if (expression.type === "Literal" && typeof expression.value === "number") return true;
69
- if (expression.type === "Identifier" && countName(expression.name)) return true;
70
- let found = false;
71
- walk(expression, visitorKeys, (node) => {
72
- if (node.type === "JSXElement" || node.type === "JSXFragment") return false;
73
- if (node.type === "Identifier" && countName(node.name) && !isObjectKey(node)) {
74
- found = true;
75
- return "stop";
76
- }
77
- return true;
78
- });
79
- return found;
80
- }
81
-
82
- export const pageComposition = {
83
- meta: {
84
- type: "problem",
85
- docs: {
86
- description: "A product page renders a composition, never the parts a composition owns.",
87
- },
88
- schema: [
89
- {
90
- type: "object",
91
- properties: {
92
- allow: {
93
- type: "array",
94
- items: { enum: Object.keys(PART_FIX) },
95
- uniqueItems: true,
96
- },
97
- modules: {
98
- type: "object",
99
- propertyNames: { enum: Object.keys(PACKAGE_MODULE) },
100
- additionalProperties: {
101
- type: "array",
102
- items: {
103
- oneOf: [
104
- { type: "string" },
105
- {
106
- type: "object",
107
- properties: { from: { type: "string" }, name: { type: "string" } },
108
- required: ["from", "name"],
109
- additionalProperties: false,
110
- },
111
- ],
112
- },
113
- },
114
- },
115
- },
116
- additionalProperties: false,
117
- },
118
- ],
119
- defaultOptions: [{ allow: [], modules: {} }],
120
- messages: {
121
- part: "{{fix}}",
122
- "entity-table": "Rows that name themselves are CollectionPage or RecordList.",
123
- "catalog-drawer": "A catalogue is CatalogDrawer.",
124
- "settings-form": "A settings section saves through its `save`; no form around or inside it.",
125
- "step-marker":
126
- "A sequence of steps is NumberedSteps, which owns the marker, the current step and the step's action.",
127
- "tab-count": "A tab names its view; the count belongs to the list's footer.",
128
- },
129
- },
130
- create(context) {
131
- const [{ allow, modules }] = context.options;
132
- const allowed = new Set(allow);
133
- const parts = exportersOf(modules);
134
- const importOf = renderedImports(context);
135
- const { visitorKeys } = context.sourceCode;
136
- const partRendered = (openingElement) =>
137
- partOf(importOf(openingElement.name), parts, context.filename);
138
- const isForm = (node) =>
139
- node.type === "JSXOpeningElement" &&
140
- node.name.type === "JSXIdentifier" &&
141
- node.name.name === "form";
142
-
143
- function checkSettingsSection(element) {
144
- const opening = element.openingElement;
145
- if (findBelow(element, visitorKeys, isForm)) {
146
- context.report({ node: opening, messageId: "settings-form" });
147
- return;
148
- }
149
- const footer = opening.attributes.find(
150
- (attribute) => attribute.type === "JSXAttribute" && attribute.name.name === "footer",
151
- );
152
- const submits =
153
- footer?.value &&
154
- findBelow(
155
- footer.value,
156
- visitorKeys,
157
- (node) => node.type === "JSXOpeningElement" && staticAttribute(node, "type") === "submit",
158
- );
159
- if (submits) context.report({ node: opening, messageId: "settings-form" });
160
- }
161
-
162
- const showsACount = (node) =>
163
- (node.type === "JSXOpeningElement" && COUNT_MARKS.has(partRendered(node))) ||
164
- (node.type === "JSXExpressionContainer" &&
165
- isRenderedChild(node) &&
166
- readsACount(node.expression, visitorKeys));
167
-
168
- function checkTab(element) {
169
- for (const child of element.children) {
170
- const offence = showsACount(child) ? child : findBelow(child, visitorKeys, showsACount);
171
- if (offence) {
172
- context.report({ node: offence, messageId: "tab-count" });
173
- return;
174
- }
175
- }
176
- }
177
-
178
- return {
179
- JSXElement(element) {
180
- const opening = element.openingElement;
181
- if (isForm(opening)) {
182
- const section = findBelow(
183
- element,
184
- visitorKeys,
185
- (node) => node.type === "JSXOpeningElement" && partRendered(node) === "SettingsSection",
186
- );
187
- if (section) context.report({ node: opening, messageId: "settings-form" });
188
- return;
189
- }
190
- const part = partRendered(opening);
191
- if (!part) return;
192
- if (Object.hasOwn(PART_FIX, part)) {
193
- if (!allowed.has(part)) {
194
- context.report({ node: opening, messageId: "part", data: { fix: PART_FIX[part] } });
195
- }
196
- } else if (part === "DataTable") {
197
- if (staticAttribute(opening, "showColumnHeaders") === false) {
198
- context.report({ node: opening, messageId: "entity-table" });
199
- }
200
- } else if (part === "FormDrawer") {
201
- if (staticAttribute(opening, "job") === "catalog") {
202
- context.report({ node: opening, messageId: "catalog-drawer" });
203
- }
204
- } else if (part === "SettingsSection") {
205
- checkSettingsSection(element);
206
- } else if (TABS.has(part)) {
207
- checkTab(element);
208
- }
209
- },
210
- JSXExpressionContainer(container) {
211
- if (!isRenderedChild(container)) return;
212
- const { expression } = container;
213
- const addsOne =
214
- expression.type === "BinaryExpression" &&
215
- expression.operator === "+" &&
216
- expression.right.type === "Literal" &&
217
- expression.right.value === 1;
218
- if (!addsOne) return;
219
- for (let current = container.parent; current; current = current.parent) {
220
- if (
221
- current.type === "JSXElement" &&
222
- current.openingElement.name.type === "JSXIdentifier" &&
223
- current.openingElement.name.name === "li"
224
- ) {
225
- context.report({ node: container, messageId: "step-marker" });
226
- return;
227
- }
228
- }
229
- },
230
- };
231
- },
232
- };
@@ -1,57 +0,0 @@
1
- /*
2
- * Standard tool checked first: `jsx-a11y` has no rule for a progressbar's name,
3
- * and a `no-restricted-syntax` selector cannot tell that the package's Progress
4
- * is meant rather than a local one, nor look for a ProgressLabel anywhere in
5
- * its children. Those need the import bindings and the element's subtree read
6
- * together.
7
- */
8
- import { findBelow, packageModules, partOf, renderedImports } from "./rendered-import.js";
9
-
10
- const partsIn = (module, names) =>
11
- names.map((name) => [
12
- name,
13
- [...packageModules(module), `@/components/${module}`].map((from) => ({ from, part: name })),
14
- ]);
15
-
16
- const PROGRESS = new Map(partsIn("progress", ["Progress"]));
17
- const PROGRESS_LABEL = new Map(partsIn("progress", ["ProgressLabel"]));
18
-
19
- const NAMING_ATTRIBUTES = new Set(["aria-label", "aria-labelledby"]);
20
-
21
- export const progressNeedsName = {
22
- meta: {
23
- type: "problem",
24
- docs: {
25
- description:
26
- "A Progress is named by an aria-label, an aria-labelledby or a ProgressLabel among its children.",
27
- },
28
- schema: [],
29
- messages: {
30
- unnamed:
31
- "Name the work: add <ProgressLabel> (Uploading files) or aria-label, and print the count in ProgressValue.",
32
- },
33
- },
34
- create(context) {
35
- const importOf = renderedImports(context);
36
- const isPart = (parts, openingElement) =>
37
- Boolean(partOf(importOf(openingElement.name), parts, context.filename));
38
-
39
- return {
40
- JSXElement(node) {
41
- const { openingElement } = node;
42
- if (!isPart(PROGRESS, openingElement)) return;
43
- const namedByAttribute = openingElement.attributes.some(
44
- (attribute) =>
45
- attribute.type === "JSXSpreadAttribute" || NAMING_ATTRIBUTES.has(attribute.name.name),
46
- );
47
- if (namedByAttribute) return;
48
- const label = findBelow(
49
- node,
50
- context.sourceCode.visitorKeys,
51
- (child) => child.type === "JSXElement" && isPart(PROGRESS_LABEL, child.openingElement),
52
- );
53
- if (!label) context.report({ node: openingElement, messageId: "unnamed" });
54
- },
55
- };
56
- },
57
- };
@@ -1,121 +0,0 @@
1
- import { dirname, resolve, sep } from "node:path";
2
-
3
- export const PACKAGE_BARREL = "@nadicodeai/ui";
4
-
5
- export const packageModules = (module) => [`@nadicodeai/ui/components/${module}`, PACKAGE_BARREL];
6
-
7
- const withoutExtension = (path) => path.replace(/\.[cm]?[jt]sx?$/, "").replace(/\/index$/, "");
8
-
9
- export function sourceMatches(source, specifier, filename) {
10
- if (source === specifier) return true;
11
- if (!specifier.startsWith("@/") || !source.startsWith(".")) return false;
12
- const target = withoutExtension(resolve(dirname(filename), source).split(sep).join("/"));
13
- return target.endsWith(`/src/${withoutExtension(specifier.slice(2))}`);
14
- }
15
-
16
- /**
17
- * Reads the file's value imports once and returns, for a JSX element's name,
18
- * the `{ source, name }` of the export it renders, or null. A namespace import
19
- * renders `<ns.Part>` as the export `Part`.
20
- */
21
- export function renderedImports(context) {
22
- const named = new Map();
23
- const namespaces = new Map();
24
- for (const statement of context.sourceCode.ast.body) {
25
- if (statement.type !== "ImportDeclaration" || statement.importKind === "type") continue;
26
- const source = statement.source.value;
27
- for (const specifier of statement.specifiers) {
28
- if (specifier.importKind === "type") continue;
29
- if (specifier.type === "ImportSpecifier") {
30
- const name = specifier.imported.name ?? specifier.imported.value;
31
- named.set(specifier.local.name, { source, name });
32
- } else if (specifier.type === "ImportNamespaceSpecifier") {
33
- namespaces.set(specifier.local.name, source);
34
- } else {
35
- named.set(specifier.local.name, { source, name: "default" });
36
- }
37
- }
38
- }
39
- return (elementName) => {
40
- if (elementName.type === "JSXIdentifier") return named.get(elementName.name) ?? null;
41
- if (
42
- elementName.type === "JSXMemberExpression" &&
43
- elementName.object.type === "JSXIdentifier" &&
44
- namespaces.has(elementName.object.name)
45
- ) {
46
- return { source: namespaces.get(elementName.object.name), name: elementName.property.name };
47
- }
48
- return null;
49
- };
50
- }
51
-
52
- /**
53
- * The part an import renders, when `parts` (a Map from an export's name to the
54
- * `{ from, part }` modules that export it, a product's adapter under its own
55
- * name included) lists that export under a matching specifier.
56
- */
57
- export function partOf(imported, parts, filename) {
58
- if (!imported) return null;
59
- const exporter = parts
60
- .get(imported.name)
61
- ?.find(({ from }) => sourceMatches(imported.source, from, filename));
62
- return exporter?.part ?? null;
63
- }
64
-
65
- export function writtenName(elementName) {
66
- if (elementName.type === "JSXIdentifier") return elementName.name;
67
- if (elementName.type === "JSXMemberExpression") return elementName.property.name;
68
- return null;
69
- }
70
-
71
- /** An attribute's value when the source states it: `true` for a bare attribute, else a literal string, number or boolean. */
72
- export function staticAttribute(openingElement, attributeName) {
73
- const attribute = openingElement.attributes.find(
74
- (candidate) => candidate.type === "JSXAttribute" && candidate.name.name === attributeName,
75
- );
76
- if (!attribute) return undefined;
77
- const { value } = attribute;
78
- if (value === null) return true;
79
- if (value.type === "Literal") return value.value;
80
- if (value.type === "JSXExpressionContainer") {
81
- const { expression } = value;
82
- if (expression.type === "Literal") return expression.value;
83
- if (expression.type === "TemplateLiteral" && expression.expressions.length === 0) {
84
- return expression.quasis[0].value.cooked;
85
- }
86
- }
87
- return undefined;
88
- }
89
-
90
- export const isRenderedChild = (node) =>
91
- node.parent?.type === "JSXElement" || node.parent?.type === "JSXFragment";
92
-
93
- /**
94
- * Visits every node under `node`, depth first, `node` excluded. `visit`
95
- * returning `false` skips that node's subtree; returning `"stop"` ends the walk.
96
- */
97
- export function walk(node, visitorKeys, visit) {
98
- const keys = visitorKeys[node.type] ?? [];
99
- for (const key of keys) {
100
- const value = node[key];
101
- for (const child of Array.isArray(value) ? value : [value]) {
102
- // oxlint-disable-next-line anti-slop/no-runtime-typeof -- walks the generic AST, where only an object whose type is a string is a node.
103
- if (!child || typeof child.type !== "string") continue;
104
- const verdict = visit(child);
105
- if (verdict === "stop") return "stop";
106
- if (verdict === false) continue;
107
- if (walk(child, visitorKeys, visit) === "stop") return "stop";
108
- }
109
- }
110
- return undefined;
111
- }
112
-
113
- export function findBelow(node, visitorKeys, matches) {
114
- let found = null;
115
- walk(node, visitorKeys, (child) => {
116
- if (!matches(child)) return true;
117
- found = child;
118
- return "stop";
119
- });
120
- return found;
121
- }
@@ -1,233 +0,0 @@
1
- /**
2
- * A Next.js server action is a POST endpoint with a function's syntax: anything
3
- * on the internet can call it with any arguments. So an exported server action
4
- * must resolve its viewer before it acts, and must parse whatever the client
5
- * sent before it trusts it.
6
- *
7
- * Standard tools checked first. `@next/eslint-plugin-next` is already on
8
- * through `eslint-config-next`, and none of its rules read a server action's
9
- * body — the set is script, head, font and image hygiene. `typescript-eslint`
10
- * can type the argument but cannot require that a call happened. The tier-1
11
- * option — a `defineAction` wrapper that makes an unvalidated or unauthorized
12
- * action untypeable — was tried and rejected for this change: Next binds the
13
- * exported function itself, so adopting it rewrites every exported action in
14
- * the same diff that retires the guard.
15
- *
16
- * The rule reads one function at a time — no whole-tree scan, no cross-file
17
- * dataflow, no exception ledger. A deliberately public action carries an inline
18
- * `eslint-disable-next-line` with its reason, which is reviewed in the diff that
19
- * adds it rather than in a list somewhere else.
20
- */
21
-
22
- /** Every helper that resolves the viewer and refuses an unauthorized one. */
23
- const AUTH_CALLS = new Set([
24
- "actionActor",
25
- "accountActionContext",
26
- "companyActionContext",
27
- "getPortalSession",
28
- "installationActionContext",
29
- "pageActor",
30
- "requirePortalAdmin",
31
- "requirePortalViewer",
32
- "requireViewerForPage",
33
- "resolveDeviceApprover",
34
- "resolvePortalAccessState",
35
- ]);
36
-
37
- /**
38
- * A schema's `parse`/`safeParse`. `JSON.parse` turns a string into an object
39
- * and asserts nothing about its shape, so it is not validation and is excluded
40
- * by name.
41
- */
42
- const VALIDATION_CALLS = new Set(["parse", "safeParse"]);
43
-
44
- const FUNCTION_TYPES = new Set([
45
- "FunctionDeclaration",
46
- "FunctionExpression",
47
- "ArrowFunctionExpression",
48
- ]);
49
-
50
- function hasUseServer(body) {
51
- return (body ?? []).some(
52
- (statement) =>
53
- statement.type === "ExpressionStatement" &&
54
- statement.expression.type === "Literal" &&
55
- statement.expression.value === "use server",
56
- );
57
- }
58
-
59
- function calleeName(node) {
60
- if (node.callee.type === "Identifier") return node.callee.name;
61
- if (node.callee.type === "MemberExpression" && node.callee.property.type === "Identifier") {
62
- if (node.callee.object.type === "Identifier" && node.callee.object.name === "JSON") {
63
- return undefined;
64
- }
65
- return node.callee.property.name;
66
- }
67
- return undefined;
68
- }
69
-
70
- /**
71
- * How a module-level function reaches the outside, which is what decides
72
- * whether Next binds it as a POST endpoint. All five spellings bind the same
73
- * way, so the rule reads all five: `export async function a`, `export default
74
- * async function a`, `export const a = async () => {}`, `export const a =
75
- * async function () {}`, and a bare declaration later named in `export { a }`.
76
- * Returns the identifier to report on, or undefined when the function is not a
77
- * module-level export.
78
- */
79
- function exportedIdentifier(node) {
80
- const parent = node.parent;
81
- if (!parent) return undefined;
82
- // export [default] async function a() {}
83
- if (parent.type === "ExportNamedDeclaration" || parent.type === "ExportDefaultDeclaration") {
84
- return node.id ?? node;
85
- }
86
- // export const a = async () => {} / async function () {}
87
- if (parent.type === "VariableDeclarator" && parent.id.type === "Identifier") {
88
- const declaration = parent.parent;
89
- if (declaration?.parent?.type === "ExportNamedDeclaration") return parent.id;
90
- // A bare `const a = async () => {}` that a later `export { a }` names.
91
- if (declaration?.parent?.type === "Program") return parent.id;
92
- return undefined;
93
- }
94
- // A bare `async function a() {}` that a later `export { a }` names.
95
- if (parent.type === "Program") return node.id ?? undefined;
96
- return undefined;
97
- }
98
-
99
- function inServerActionCode(node, moduleIsServer) {
100
- if (moduleIsServer) return true;
101
- for (let ancestor = node.parent; ancestor; ancestor = ancestor.parent) {
102
- if (FUNCTION_TYPES.has(ancestor.type) && hasUseServer(ancestor.body?.body)) return true;
103
- }
104
- return false;
105
- }
106
-
107
- function inlineRejectionHandler(node) {
108
- const handler = node.arguments[0];
109
- if (node.callee.type !== "MemberExpression" || node.callee.property.type !== "Identifier") {
110
- return undefined;
111
- }
112
- if (node.callee.property.name !== "catch" || !FUNCTION_TYPES.has(handler?.type)) return undefined;
113
- return handler;
114
- }
115
-
116
- function dropsTheError(sourceCode, node, bindings) {
117
- if (bindings.length === 0) return true;
118
- return sourceCode
119
- .getDeclaredVariables(node)
120
- .filter((variable) =>
121
- variable.defs.some((def) => def.type === "CatchClause" || def.type === "Parameter"),
122
- )
123
- .every((variable) => variable.references.length === 0);
124
- }
125
-
126
- /** @type {import("eslint").Rule.RuleModule} */
127
- export const serverActionDiscipline = {
128
- meta: {
129
- type: "problem",
130
- docs: {
131
- description:
132
- "An exported server action resolves its viewer and parses its client input, and server-action code never drops an error it catches.",
133
- },
134
- schema: [],
135
- messages: {
136
- missingAuth:
137
- "Server action '{{name}}' calls no viewer check. Resolve the viewer first (actionActor, requirePortalViewer, companyActionContext, …), or disable this rule inline with the reason it is public.",
138
- missingValidation:
139
- "Server action '{{name}}' takes client input and never parses it. Run the Zod schema's parse/safeParse before trusting the argument.",
140
- droppedError:
141
- "This catch drops the error it handles, so an outage reaches the form as an ordinary answer and leaves no log line. Name the error and translate it, answer the form's unavailable state with actionUnavailable, which logs it once, or rethrow it.",
142
- },
143
- },
144
- create(context) {
145
- const { sourceCode } = context;
146
- const moduleIsServer = hasUseServer(sourceCode.ast.body);
147
- /**
148
- * One frame per function, holding only the calls written in that function's
149
- * own body. A call parked inside a nested callback is not a call the action
150
- * itself makes, so it does not count for the action.
151
- * @type {Array<Set<string>>}
152
- */
153
- const callStack = [];
154
- /**
155
- * Candidates are judged at `Program:exit` rather than when each function
156
- * closes, because `export { a }` can name a declaration written above it.
157
- * @type {Array<{ identifier: object, name: string, calls: Set<string>, hasParams: boolean, inlineExported: boolean }>}
158
- */
159
- const candidates = [];
160
- /** Names an `export { … }` specifier list makes public. @type {Set<string>} */
161
- const specifierExports = new Set();
162
-
163
- function enter() {
164
- callStack.push(new Set());
165
- }
166
-
167
- function exit(node) {
168
- const calls = callStack.pop() ?? new Set();
169
- if (!node.async) return;
170
- if (!(moduleIsServer || hasUseServer(node.body?.body))) return;
171
- const identifier = exportedIdentifier(node);
172
- if (!identifier) return;
173
- const parent = node.parent;
174
- const inlineExported =
175
- parent?.type === "ExportNamedDeclaration" ||
176
- parent?.type === "ExportDefaultDeclaration" ||
177
- parent?.parent?.parent?.type === "ExportNamedDeclaration";
178
- candidates.push({
179
- identifier,
180
- name: identifier.name ?? "default",
181
- calls,
182
- hasParams: node.params.length > 0,
183
- inlineExported,
184
- });
185
- }
186
-
187
- return {
188
- FunctionDeclaration: enter,
189
- "FunctionDeclaration:exit": exit,
190
- FunctionExpression: enter,
191
- "FunctionExpression:exit": exit,
192
- ArrowFunctionExpression: enter,
193
- "ArrowFunctionExpression:exit": exit,
194
- ExportNamedDeclaration(node) {
195
- for (const specifier of node.specifiers ?? []) {
196
- const local = specifier.local;
197
- if (local?.type === "Identifier") specifierExports.add(local.name);
198
- }
199
- },
200
- CatchClause(node) {
201
- const bindings = node.param ? [node.param] : [];
202
- if (inServerActionCode(node, moduleIsServer) && dropsTheError(sourceCode, node, bindings)) {
203
- context.report({ node, messageId: "droppedError" });
204
- }
205
- },
206
- CallExpression(node) {
207
- const handler = inlineRejectionHandler(node);
208
- if (
209
- handler &&
210
- inServerActionCode(node, moduleIsServer) &&
211
- dropsTheError(sourceCode, handler, handler.params)
212
- ) {
213
- context.report({ node: handler, messageId: "droppedError" });
214
- }
215
- const name = calleeName(node);
216
- if (!name) return;
217
- callStack.at(-1)?.add(name);
218
- },
219
- "Program:exit"() {
220
- for (const candidate of candidates) {
221
- if (!candidate.inlineExported && !specifierExports.has(candidate.name)) continue;
222
- const { identifier, name, calls, hasParams } = candidate;
223
- if (![...AUTH_CALLS].some((call) => calls.has(call))) {
224
- context.report({ node: identifier, messageId: "missingAuth", data: { name } });
225
- }
226
- if (hasParams && ![...VALIDATION_CALLS].some((call) => calls.has(call))) {
227
- context.report({ node: identifier, messageId: "missingValidation", data: { name } });
228
- }
229
- }
230
- },
231
- };
232
- },
233
- };