@jini-ai/admin 0.3.10 → 0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (387) hide show
  1. package/CHANGELOG.md +64 -0
  2. package/README.md +71 -37
  3. package/dist/agent-plugins/adapters/http.d.ts +7 -0
  4. package/dist/agent-plugins/adapters/http.js +15 -0
  5. package/dist/agent-plugins/adapters/memory.d.ts +8 -0
  6. package/dist/agent-plugins/adapters/memory.js +25 -0
  7. package/dist/agent-plugins/agent-plugins.module.d.ts +51 -0
  8. package/dist/agent-plugins/agent-plugins.module.js +6 -0
  9. package/dist/agent-plugins/conformance/agent-plugins-api.conformance.d.ts +12 -0
  10. package/dist/agent-plugins/conformance/agent-plugins-api.conformance.js +47 -0
  11. package/dist/agent-plugins/controllers/agent-plugin-files.controller.d.ts +20 -0
  12. package/dist/agent-plugins/controllers/agent-plugin-files.controller.js +43 -0
  13. package/dist/agent-plugins/controllers/agent-plugins.controller.d.ts +25 -0
  14. package/dist/agent-plugins/controllers/agent-plugins.controller.js +91 -0
  15. package/dist/agent-plugins/index.d.ts +9 -0
  16. package/dist/agent-plugins/index.js +9 -0
  17. package/dist/agent-plugins/messages.en.d.ts +23 -0
  18. package/dist/agent-plugins/messages.en.js +14 -0
  19. package/dist/agent-plugins/models.d.ts +57 -0
  20. package/dist/agent-plugins/models.js +2 -0
  21. package/dist/agent-plugins/package-file-tree.d.ts +111 -0
  22. package/dist/agent-plugins/package-file-tree.js +188 -0
  23. package/dist/agent-plugins/ports.d.ts +18 -0
  24. package/dist/agent-plugins/ports.js +3 -0
  25. package/dist/agent-plugins/react/components/AgentPluginFileContent.d.ts +5 -0
  26. package/dist/agent-plugins/react/components/AgentPluginFileContent.js +9 -0
  27. package/dist/agent-plugins/react/components/AgentPluginFileTree.d.ts +9 -0
  28. package/dist/agent-plugins/react/components/AgentPluginFileTree.js +7 -0
  29. package/dist/agent-plugins/react/components/AgentPluginInspector.d.ts +3 -0
  30. package/dist/agent-plugins/react/components/AgentPluginInspector.js +11 -0
  31. package/dist/agent-plugins/react/components/AgentPluginListPanel.d.ts +6 -0
  32. package/dist/agent-plugins/react/components/AgentPluginListPanel.js +10 -0
  33. package/dist/agent-plugins/react/components/AgentPluginRow.d.ts +5 -0
  34. package/dist/agent-plugins/react/components/AgentPluginRow.js +8 -0
  35. package/dist/agent-plugins/react/hooks/AgentPluginInspector.hooks.d.ts +90 -0
  36. package/dist/agent-plugins/react/hooks/AgentPluginInspector.hooks.js +74 -0
  37. package/dist/agent-plugins/react/hooks/AgentPluginPanel.hooks.d.ts +67 -0
  38. package/dist/agent-plugins/react/hooks/AgentPluginPanel.hooks.js +50 -0
  39. package/dist/agent-plugins/react/hooks/AgentPluginRow.hooks.d.ts +55 -0
  40. package/dist/agent-plugins/react/hooks/AgentPluginRow.hooks.js +6 -0
  41. package/dist/agent-plugins/react/hooks/AgentPluginsPage.hooks.d.ts +64 -0
  42. package/dist/agent-plugins/react/hooks/AgentPluginsPage.hooks.js +39 -0
  43. package/dist/agent-plugins/react/hooks/AgentPluginsPorts.hooks.d.ts +7 -0
  44. package/dist/agent-plugins/react/hooks/AgentPluginsPorts.hooks.js +10 -0
  45. package/dist/agent-plugins/react/hooks/MarketplaceTab.hooks.d.ts +6 -0
  46. package/dist/agent-plugins/react/hooks/MarketplaceTab.hooks.js +3 -0
  47. package/dist/agent-plugins/react/index.d.ts +245 -0
  48. package/dist/agent-plugins/react/index.js +13 -0
  49. package/dist/agent-plugins/react/pages/AgentPluginsPage.d.ts +4 -0
  50. package/dist/agent-plugins/react/pages/AgentPluginsPage.js +12 -0
  51. package/dist/agent-plugins/react/tabs/DownloadedTab.d.ts +4 -0
  52. package/dist/agent-plugins/react/tabs/DownloadedTab.js +5 -0
  53. package/dist/agent-plugins/react/tabs/InstalledTab.d.ts +4 -0
  54. package/dist/agent-plugins/react/tabs/InstalledTab.js +5 -0
  55. package/dist/agent-plugins/react/tabs/MarketplaceTab.d.ts +4 -0
  56. package/dist/agent-plugins/react/tabs/MarketplaceTab.js +10 -0
  57. package/dist/agent-plugins/rules.d.ts +33 -0
  58. package/dist/agent-plugins/rules.js +51 -0
  59. package/dist/browser/index.d.ts +1 -0
  60. package/dist/browser/index.js +1 -0
  61. package/dist/browser/navigation.d.ts +19 -4
  62. package/dist/browser/navigation.js +8 -8
  63. package/dist/browser/shell-navigation.d.ts +12 -0
  64. package/dist/browser/shell-navigation.js +16 -0
  65. package/dist/contracts/credential-management.d.ts +8 -0
  66. package/dist/contracts/credential-management.js +3 -0
  67. package/dist/contracts/media-picker.d.ts +17 -0
  68. package/dist/contracts/media-picker.js +5 -0
  69. package/dist/contracts/playground-render-target.d.ts +18 -0
  70. package/dist/contracts/playground-render-target.js +3 -0
  71. package/dist/core/entities/rules.d.ts +138 -0
  72. package/dist/core/entities/rules.js +344 -0
  73. package/dist/core/gated/types.d.ts +8 -4
  74. package/dist/core/index.d.ts +4 -0
  75. package/dist/core/index.js +1 -0
  76. package/dist/core/manifest/rules.d.ts +24 -6
  77. package/dist/core/manifest/rules.js +12 -11
  78. package/dist/core/module/controller-store.d.ts +22 -0
  79. package/dist/core/module/controller-store.js +32 -0
  80. package/dist/core/module/create-admin.d.ts +41 -0
  81. package/dist/core/module/create-admin.js +187 -0
  82. package/dist/core/module/define-admin-module.d.ts +4 -0
  83. package/dist/core/module/define-admin-module.js +5 -0
  84. package/dist/core/module/index.d.ts +7 -0
  85. package/dist/core/module/index.js +4 -0
  86. package/dist/core/module/token.d.ts +19 -0
  87. package/dist/core/module/token.js +4 -0
  88. package/dist/core/module/types.d.ts +79 -0
  89. package/dist/core/module/types.js +2 -0
  90. package/dist/core/permissions/rules.d.ts +4 -1
  91. package/dist/core/permissions/rules.js +1 -1
  92. package/dist/core/ports/analytics.d.ts +1 -1
  93. package/dist/core/ports/auth.d.ts +2 -2
  94. package/dist/core/ports/comments.d.ts +9 -6
  95. package/dist/core/ports/database.d.ts +13 -10
  96. package/dist/core/ports/entities.d.ts +302 -0
  97. package/dist/core/ports/entities.js +47 -0
  98. package/dist/core/ports/forms.d.ts +19 -7
  99. package/dist/core/ports/identity.d.ts +42 -17
  100. package/dist/core/ports/index.d.ts +2 -0
  101. package/dist/core/ports/integrations.d.ts +9 -4
  102. package/dist/core/ports/media.d.ts +16 -7
  103. package/dist/core/ports/members.d.ts +7 -3
  104. package/dist/core/ports/menus.d.ts +46 -0
  105. package/dist/core/ports/menus.js +43 -0
  106. package/dist/core/ports/plugins.d.ts +3 -2
  107. package/dist/core/ports/recovery.d.ts +10 -6
  108. package/dist/core/ports/redirects.d.ts +17 -7
  109. package/dist/core/ports/seo.d.ts +12 -6
  110. package/dist/core/ports/shell.d.ts +41 -0
  111. package/dist/core/ports/shell.js +2 -0
  112. package/dist/core/ports/workspace.d.ts +3 -3
  113. package/dist/core/routing/rules.d.ts +17 -4
  114. package/dist/core/routing/rules.js +4 -4
  115. package/dist/core/transport/errors.d.ts +13 -4
  116. package/dist/core/transport/errors.js +4 -4
  117. package/dist/core/transport/http.d.ts +26 -16
  118. package/dist/core/transport/http.js +59 -21
  119. package/dist/core/transport/types.d.ts +26 -14
  120. package/dist/core/transport/types.js +9 -7
  121. package/dist/media/adapters/http.d.ts +27 -0
  122. package/dist/media/adapters/http.js +44 -0
  123. package/dist/media/adapters/memory.d.ts +11 -0
  124. package/dist/media/adapters/memory.js +139 -0
  125. package/dist/media/conformance/media-api.conformance.d.ts +7 -0
  126. package/dist/media/conformance/media-api.conformance.js +69 -0
  127. package/dist/media/controllers/edit-media.controller.d.ts +24 -0
  128. package/dist/media/controllers/edit-media.controller.js +69 -0
  129. package/dist/media/controllers/library.controller.d.ts +47 -0
  130. package/dist/media/controllers/library.controller.js +173 -0
  131. package/dist/media/controllers/providers.controller.d.ts +30 -0
  132. package/dist/media/controllers/providers.controller.js +76 -0
  133. package/dist/media/html-attributes.d.ts +82 -0
  134. package/dist/media/html-attributes.js +204 -0
  135. package/dist/media/index.d.ts +12 -0
  136. package/dist/media/index.js +9 -0
  137. package/dist/media/media.module.d.ts +83 -0
  138. package/dist/media/media.module.js +43 -0
  139. package/dist/media/messages.en.d.ts +24 -0
  140. package/dist/media/messages.en.js +24 -0
  141. package/dist/media/models.d.ts +72 -0
  142. package/dist/media/models.js +2 -0
  143. package/dist/media/ports.d.ts +75 -0
  144. package/dist/media/ports.js +7 -0
  145. package/dist/media/react/components/EditMediaPanel.d.ts +3 -0
  146. package/dist/media/react/components/EditMediaPanel.js +10 -0
  147. package/dist/media/react/components/MediaCard.d.ts +5 -0
  148. package/dist/media/react/components/MediaCard.js +17 -0
  149. package/dist/media/react/components/MediaDialog.d.ts +3 -0
  150. package/dist/media/react/components/MediaDialog.js +8 -0
  151. package/dist/media/react/components/MediaGrid.d.ts +4 -0
  152. package/dist/media/react/components/MediaGrid.js +8 -0
  153. package/dist/media/react/components/MediaIcons.d.ts +14 -0
  154. package/dist/media/react/components/MediaIcons.js +39 -0
  155. package/dist/media/react/components/MediaLightbox.d.ts +5 -0
  156. package/dist/media/react/components/MediaLightbox.js +12 -0
  157. package/dist/media/react/components/MediaPicker.d.ts +3 -0
  158. package/dist/media/react/components/MediaPicker.js +10 -0
  159. package/dist/media/react/components/MediaPurgeDialog.d.ts +4 -0
  160. package/dist/media/react/components/MediaPurgeDialog.js +9 -0
  161. package/dist/media/react/components/UploadButton.d.ts +3 -0
  162. package/dist/media/react/components/UploadButton.js +8 -0
  163. package/dist/media/react/hooks/EditMediaPanel.hooks.d.ts +84 -0
  164. package/dist/media/react/hooks/EditMediaPanel.hooks.js +105 -0
  165. package/dist/media/react/hooks/LibraryTab.hooks.d.ts +103 -0
  166. package/dist/media/react/hooks/LibraryTab.hooks.js +164 -0
  167. package/dist/media/react/hooks/MediaCard.hooks.d.ts +83 -0
  168. package/dist/media/react/hooks/MediaCard.hooks.js +66 -0
  169. package/dist/media/react/hooks/MediaDialog.hooks.d.ts +10 -0
  170. package/dist/media/react/hooks/MediaDialog.hooks.js +20 -0
  171. package/dist/media/react/hooks/MediaGrid.hooks.d.ts +29 -0
  172. package/dist/media/react/hooks/MediaGrid.hooks.js +6 -0
  173. package/dist/media/react/hooks/MediaLightbox.hooks.d.ts +27 -0
  174. package/dist/media/react/hooks/MediaLightbox.hooks.js +64 -0
  175. package/dist/media/react/hooks/MediaPage.hooks.d.ts +68 -0
  176. package/dist/media/react/hooks/MediaPage.hooks.js +68 -0
  177. package/dist/media/react/hooks/MediaPicker.hooks.d.ts +26 -0
  178. package/dist/media/react/hooks/MediaPicker.hooks.js +25 -0
  179. package/dist/media/react/hooks/MediaPorts.hooks.d.ts +9 -0
  180. package/dist/media/react/hooks/MediaPorts.hooks.js +10 -0
  181. package/dist/media/react/hooks/MediaPurgeDialog.hooks.d.ts +22 -0
  182. package/dist/media/react/hooks/MediaPurgeDialog.hooks.js +6 -0
  183. package/dist/media/react/hooks/ProvidersTab.hooks.d.ts +55 -0
  184. package/dist/media/react/hooks/ProvidersTab.hooks.js +124 -0
  185. package/dist/media/react/hooks/UploadButton.hooks.d.ts +48 -0
  186. package/dist/media/react/hooks/UploadButton.hooks.js +118 -0
  187. package/dist/media/react/index.d.ts +373 -0
  188. package/dist/media/react/index.js +73 -0
  189. package/dist/media/react/pages/MediaPage.d.ts +4 -0
  190. package/dist/media/react/pages/MediaPage.js +9 -0
  191. package/dist/media/react/tabs/LibraryTab.d.ts +4 -0
  192. package/dist/media/react/tabs/LibraryTab.js +14 -0
  193. package/dist/media/react/tabs/ProvidersTab.d.ts +4 -0
  194. package/dist/media/react/tabs/ProvidersTab.js +10 -0
  195. package/dist/media/rules.d.ts +86 -0
  196. package/dist/media/rules.js +134 -0
  197. package/dist/media/styles.d.ts +4 -0
  198. package/dist/media/styles.js +5 -0
  199. package/dist/playground/adapters/http.d.ts +8 -0
  200. package/dist/playground/adapters/http.js +4 -0
  201. package/dist/playground/adapters/memory.d.ts +10 -0
  202. package/dist/playground/adapters/memory.js +30 -0
  203. package/dist/playground/conformance/playground-api.conformance.d.ts +6 -0
  204. package/dist/playground/conformance/playground-api.conformance.js +37 -0
  205. package/dist/playground/controllers/playground.controller.d.ts +16 -0
  206. package/dist/playground/controllers/playground.controller.js +21 -0
  207. package/dist/playground/index.d.ts +7 -0
  208. package/dist/playground/index.js +5 -0
  209. package/dist/playground/messages.en.d.ts +9 -0
  210. package/dist/playground/messages.en.js +2 -0
  211. package/dist/playground/models.d.ts +4 -0
  212. package/dist/playground/models.js +2 -0
  213. package/dist/playground/playground.module.d.ts +31 -0
  214. package/dist/playground/playground.module.js +5 -0
  215. package/dist/playground/ports.d.ts +5 -0
  216. package/dist/playground/ports.js +2 -0
  217. package/dist/playground/react/components/Canvas.d.ts +3 -0
  218. package/dist/playground/react/components/Canvas.js +9 -0
  219. package/dist/playground/react/hooks/Canvas.hooks.d.ts +7 -0
  220. package/dist/playground/react/hooks/Canvas.hooks.js +18 -0
  221. package/dist/playground/react/hooks/PlaygroundPage.hooks.d.ts +8 -0
  222. package/dist/playground/react/hooks/PlaygroundPage.hooks.js +2 -0
  223. package/dist/playground/react/hooks/PlaygroundPorts.hooks.d.ts +4 -0
  224. package/dist/playground/react/hooks/PlaygroundPorts.hooks.js +6 -0
  225. package/dist/playground/react/index.d.ts +136 -0
  226. package/dist/playground/react/index.js +10 -0
  227. package/dist/playground/react/pages/PlaygroundPage.d.ts +4 -0
  228. package/dist/playground/react/pages/PlaygroundPage.js +11 -0
  229. package/dist/playground/react/tabs/CanvasTab.d.ts +4 -0
  230. package/dist/playground/react/tabs/CanvasTab.js +5 -0
  231. package/dist/playground/rules.d.ts +4 -0
  232. package/dist/playground/rules.js +2 -0
  233. package/dist/react/OverlayHost.d.ts +3 -0
  234. package/dist/react/OverlayHost.hooks.d.ts +9 -0
  235. package/dist/react/OverlayHost.hooks.js +9 -0
  236. package/dist/react/OverlayHost.js +7 -0
  237. package/dist/react/bind-react.d.ts +46 -0
  238. package/dist/react/bind-react.js +28 -0
  239. package/dist/react/components/ConfirmButton/ConfirmButton.hooks.d.ts +7 -1
  240. package/dist/react/components/ConfirmButton/ConfirmButton.hooks.js +2 -2
  241. package/dist/react/components/ConfirmButton/ConfirmButton.js +1 -1
  242. package/dist/react/components/ConfirmDialog/ConfirmDialog.hooks.d.ts +17 -3
  243. package/dist/react/components/ConfirmDialog/ConfirmDialog.hooks.js +3 -3
  244. package/dist/react/components/ConfirmDialog/ConfirmDialog.js +5 -5
  245. package/dist/react/components/InteractiveHtmlEditor/InteractiveHtmlEditor.d.ts +6 -2
  246. package/dist/react/components/InteractiveHtmlEditor/InteractiveHtmlEditor.js +7 -8
  247. package/dist/react/components/RowMenu/RowMenu.d.ts +5 -1
  248. package/dist/react/components/RowMenu/RowMenu.hooks.d.ts +8 -2
  249. package/dist/react/components/RowMenu/RowMenu.hooks.js +4 -4
  250. package/dist/react/components/RowMenu/RowMenu.js +6 -6
  251. package/dist/react/components/Sidebar.d.ts +1 -1
  252. package/dist/react/components/Sidebar.js +103 -7
  253. package/dist/react/entities/EntityCell.d.ts +10 -0
  254. package/dist/react/entities/EntityCell.js +18 -0
  255. package/dist/react/entities/EntityDetail.d.ts +3 -0
  256. package/dist/react/entities/EntityDetail.js +21 -0
  257. package/dist/react/entities/EntityEdit.d.ts +13 -0
  258. package/dist/react/entities/EntityEdit.js +45 -0
  259. package/dist/react/entities/EntityField.d.ts +20 -0
  260. package/dist/react/entities/EntityField.js +31 -0
  261. package/dist/react/entities/EntityIndex.d.ts +3 -0
  262. package/dist/react/entities/EntityIndex.js +10 -0
  263. package/dist/react/entities/EntityList.d.ts +7 -0
  264. package/dist/react/entities/EntityList.js +52 -0
  265. package/dist/react/entities/contribution.d.ts +26 -0
  266. package/dist/react/entities/contribution.js +78 -0
  267. package/dist/react/entities/index.d.ts +10 -0
  268. package/dist/react/entities/index.js +7 -0
  269. package/dist/react/entities/rules.d.ts +179 -0
  270. package/dist/react/entities/rules.js +224 -0
  271. package/dist/react/entities/types.d.ts +45 -0
  272. package/dist/react/entities/types.js +5 -0
  273. package/dist/react/entities/use-entity-edit.d.ts +35 -0
  274. package/dist/react/entities/use-entity-edit.js +98 -0
  275. package/dist/react/entities/use-entity-read.d.ts +22 -0
  276. package/dist/react/entities/use-entity-read.js +27 -0
  277. package/dist/react/hooks/use-nav-sections.d.ts +12 -3
  278. package/dist/react/hooks/use-nav-sections.js +14 -14
  279. package/dist/react/hooks/use-sidebar-rail.d.ts +7 -1
  280. package/dist/react/hooks/use-sidebar-rail.js +11 -11
  281. package/dist/react/index.d.ts +3 -1
  282. package/dist/react/index.js +5 -2
  283. package/dist/react/overlays.d.ts +38 -0
  284. package/dist/react/overlays.js +46 -0
  285. package/dist/react/shell/AdminShell.d.ts +9 -0
  286. package/dist/react/shell/AdminShell.hooks.d.ts +15 -0
  287. package/dist/react/shell/AdminShell.hooks.js +27 -0
  288. package/dist/react/shell/AdminShell.js +46 -0
  289. package/dist/react/shell/index.d.ts +7 -0
  290. package/dist/react/shell/index.js +5 -0
  291. package/dist/react/shell/model.d.ts +25 -0
  292. package/dist/react/shell/model.js +29 -0
  293. package/dist/react/shell/theme-ports.d.ts +28 -0
  294. package/dist/react/shell/theme-ports.js +2 -0
  295. package/dist/react/shell/types.d.ts +99 -0
  296. package/dist/react/shell/types.js +2 -0
  297. package/dist/react/shell/use-admin-shell-session.d.ts +13 -0
  298. package/dist/react/shell/use-admin-shell-session.js +81 -0
  299. package/dist/react/shell/use-admin-theme.d.ts +16 -0
  300. package/dist/react/shell/use-admin-theme.js +57 -0
  301. package/dist/react/types.d.ts +6 -4
  302. package/dist/react/types.js +2 -2
  303. package/dist/react/use-controller.d.ts +16 -0
  304. package/dist/react/use-controller.js +18 -0
  305. package/dist/security/adapters/http.d.ts +16 -0
  306. package/dist/security/adapters/http.js +94 -0
  307. package/dist/security/adapters/memory.d.ts +15 -0
  308. package/dist/security/adapters/memory.js +60 -0
  309. package/dist/security/conformance/security-api.conformance.d.ts +10 -0
  310. package/dist/security/conformance/security-api.conformance.js +48 -0
  311. package/dist/security/controllers/access-tokens.controller.d.ts +44 -0
  312. package/dist/security/controllers/access-tokens.controller.js +112 -0
  313. package/dist/security/controllers/other-credentials.controller.d.ts +30 -0
  314. package/dist/security/controllers/other-credentials.controller.js +74 -0
  315. package/dist/security/controllers/root-key.controller.d.ts +35 -0
  316. package/dist/security/controllers/root-key.controller.js +90 -0
  317. package/dist/security/index.d.ts +9 -0
  318. package/dist/security/index.js +9 -0
  319. package/dist/security/messages.en.d.ts +12 -0
  320. package/dist/security/messages.en.js +2 -0
  321. package/dist/security/models.d.ts +112 -0
  322. package/dist/security/models.js +2 -0
  323. package/dist/security/ports.d.ts +58 -0
  324. package/dist/security/ports.js +5 -0
  325. package/dist/security/react/components/CredentialEditor.d.ts +3 -0
  326. package/dist/security/react/components/CredentialEditor.js +8 -0
  327. package/dist/security/react/hooks/AccessTokensTab.hooks.d.ts +94 -0
  328. package/dist/security/react/hooks/AccessTokensTab.hooks.js +36 -0
  329. package/dist/security/react/hooks/CredentialEditor.hooks.d.ts +49 -0
  330. package/dist/security/react/hooks/CredentialEditor.hooks.js +14 -0
  331. package/dist/security/react/hooks/RootKeyTab.hooks.d.ts +29 -0
  332. package/dist/security/react/hooks/RootKeyTab.hooks.js +19 -0
  333. package/dist/security/react/hooks/SecurityPage.hooks.d.ts +17 -0
  334. package/dist/security/react/hooks/SecurityPage.hooks.js +12 -0
  335. package/dist/security/react/hooks/SecurityPorts.hooks.d.ts +9 -0
  336. package/dist/security/react/hooks/SecurityPorts.hooks.js +6 -0
  337. package/dist/security/react/index.d.ts +215 -0
  338. package/dist/security/react/index.js +10 -0
  339. package/dist/security/react/pages/SecurityPage.d.ts +4 -0
  340. package/dist/security/react/pages/SecurityPage.js +10 -0
  341. package/dist/security/react/tabs/AccessTokensTab.d.ts +4 -0
  342. package/dist/security/react/tabs/AccessTokensTab.js +10 -0
  343. package/dist/security/react/tabs/RootKeyTab.d.ts +4 -0
  344. package/dist/security/react/tabs/RootKeyTab.js +9 -0
  345. package/dist/security/rules.d.ts +57 -0
  346. package/dist/security/rules.js +63 -0
  347. package/dist/security/security.module.d.ts +45 -0
  348. package/dist/security/security.module.js +5 -0
  349. package/dist/source-control/adapters/http.d.ts +6 -0
  350. package/dist/source-control/adapters/http.js +13 -0
  351. package/dist/source-control/adapters/memory.d.ts +9 -0
  352. package/dist/source-control/adapters/memory.js +35 -0
  353. package/dist/source-control/conformance/source-control-api.conformance.d.ts +10 -0
  354. package/dist/source-control/conformance/source-control-api.conformance.js +48 -0
  355. package/dist/source-control/controllers/source-control.controller.d.ts +26 -0
  356. package/dist/source-control/controllers/source-control.controller.js +70 -0
  357. package/dist/source-control/index.d.ts +8 -0
  358. package/dist/source-control/index.js +6 -0
  359. package/dist/source-control/messages.en.d.ts +11 -0
  360. package/dist/source-control/messages.en.js +2 -0
  361. package/dist/source-control/models.d.ts +70 -0
  362. package/dist/source-control/models.js +2 -0
  363. package/dist/source-control/ports.d.ts +23 -0
  364. package/dist/source-control/ports.js +3 -0
  365. package/dist/source-control/react/components/SourceControlCredentialFields.d.ts +5 -0
  366. package/dist/source-control/react/components/SourceControlCredentialFields.js +8 -0
  367. package/dist/source-control/react/components/SourceControlProviderRow.d.ts +5 -0
  368. package/dist/source-control/react/components/SourceControlProviderRow.js +7 -0
  369. package/dist/source-control/react/hooks/ProvidersTab.hooks.d.ts +45 -0
  370. package/dist/source-control/react/hooks/ProvidersTab.hooks.js +20 -0
  371. package/dist/source-control/react/hooks/SourceControlCredentialFields.hooks.d.ts +43 -0
  372. package/dist/source-control/react/hooks/SourceControlCredentialFields.hooks.js +9 -0
  373. package/dist/source-control/react/hooks/SourceControlPage.hooks.d.ts +17 -0
  374. package/dist/source-control/react/hooks/SourceControlPage.hooks.js +5 -0
  375. package/dist/source-control/react/hooks/SourceControlPorts.hooks.d.ts +9 -0
  376. package/dist/source-control/react/hooks/SourceControlPorts.hooks.js +6 -0
  377. package/dist/source-control/react/index.d.ts +156 -0
  378. package/dist/source-control/react/index.js +10 -0
  379. package/dist/source-control/react/pages/SourceControlPage.d.ts +4 -0
  380. package/dist/source-control/react/pages/SourceControlPage.js +11 -0
  381. package/dist/source-control/react/tabs/ProvidersTab.d.ts +4 -0
  382. package/dist/source-control/react/tabs/ProvidersTab.js +10 -0
  383. package/dist/source-control/rules.d.ts +32 -0
  384. package/dist/source-control/rules.js +35 -0
  385. package/dist/source-control/source-control.module.d.ts +36 -0
  386. package/dist/source-control/source-control.module.js +6 -0
  387. package/package.json +207 -4
@@ -0,0 +1,179 @@
1
+ import type { AdminEntityDescriptor, AdminEntityRelationField, AdminEntityRowData } from '../../core/ports/entities.js';
2
+ import type { EntityRegistryPort } from './types.js';
3
+ export declare const RELATION_OPTION_LIMIT = 100;
4
+ export declare function resolveEntityPageSize({ raw }: {
5
+ readonly raw: string | null;
6
+ }): number;
7
+ /** Every `relation` field on a descriptor, narrowed so `target` is reachable. */
8
+ export declare function relationFieldsOf({ descriptor }: {
9
+ readonly descriptor: AdminEntityDescriptor;
10
+ }): readonly AdminEntityRelationField[];
11
+ /** One relation target's options: what a picker offers, and what a list cell resolves against. */
12
+ export interface RelationOptions {
13
+ /** Target rows as `{ id, title }`, in the target's own list order. */
14
+ readonly options: readonly {
15
+ readonly id: string;
16
+ readonly title: string;
17
+ }[];
18
+ /** `id` -> title, for O(1) cell rendering. */
19
+ readonly titles: Readonly<Record<string, string>>;
20
+ /** True when the target has more rows than {@link RELATION_OPTION_LIMIT} loaded here. */
21
+ readonly truncated: boolean;
22
+ }
23
+ /** Relation target name -> its loaded options. Keyed by TARGET, not by field, so two fields
24
+ * pointing at the same entity share one load. */
25
+ export type RelationIndex = Readonly<Record<string, RelationOptions>>;
26
+ /**
27
+ * Loads the option set for every distinct relation target on `descriptor`.
28
+ *
29
+ * @param descriptor The entity whose relations need resolving.
30
+ * @param registry Host registry resolving a target's existing entity port.
31
+ * @returns One entry per distinct target. A target that does not resolve is simply absent, and the
32
+ * screens then render the raw stored id marked as unresolved rather than a blank cell.
33
+ * @complexity One `list` call per distinct target, in parallel; O(targets × limit) rows held.
34
+ * @overallScore 100
35
+ */
36
+ export declare function loadRelationIndex({ descriptor, registry }: {
37
+ readonly descriptor: AdminEntityDescriptor;
38
+ readonly registry: EntityRegistryPort;
39
+ }): Promise<RelationIndex>;
40
+ /**
41
+ * A row's human-readable title, or `null` when the descriptor's `titleField` does not resolve to a
42
+ * string on this row.
43
+ *
44
+ * Returns `null` rather than `String(value)` so a caller falls back to the id: a picker rendering
45
+ * the literal text `undefined` for every row is the exact symptom of a mistyped `titleField`, and
46
+ * `createAdminEntityRegistry` already rejects that at startup — this guards the remaining case,
47
+ * where the field is declared correctly and one row simply has no value for it.
48
+ *
49
+ * @complexity Time O(1).
50
+ * @overallScore 100
51
+ */
52
+ export declare function readTitle({ row, titleField }: {
53
+ readonly row: AdminEntityRowData;
54
+ readonly titleField: string;
55
+ }): string | null;
56
+ /**
57
+ * The draft a "new" form starts from.
58
+ *
59
+ * Every declared field is present and absent-valued, so each control is controlled from the first
60
+ * render rather than switching from uncontrolled on the first keystroke.
61
+ *
62
+ * The one exception is a REQUIRED boolean, which starts `false`. A checkbox has no third state: an
63
+ * untouched one already means "no", so leaving it absent would block Save on a field the operator
64
+ * has, visibly, already answered — they would have to tick it and untick it to satisfy a
65
+ * requirement the unticked box was expressing all along. An OPTIONAL boolean stays absent, because
66
+ * there "unanswered" and "no" are genuinely different and only the descriptor's author knows which
67
+ * the row means.
68
+ *
69
+ * @complexity Time O(fields).
70
+ * @overallScore 100
71
+ */
72
+ export declare function emptyDraft({ descriptor }: {
73
+ readonly descriptor: AdminEntityDescriptor;
74
+ }): Record<string, unknown>;
75
+ /**
76
+ * The draft an editor opens with: {@link emptyDraft}'s floor, with `row`'s declared values on top.
77
+ *
78
+ * Layered rather than `{ ...row }`, and that is the whole point. A row is not guaranteed to carry
79
+ * every declared field — an app that adds a `required` field to its descriptor has existing rows
80
+ * that predate it — and a draft built from the row alone inherits that gap. For a required BOOLEAN
81
+ * the consequence is {@link emptyDraft}'s trap arriving by the other door: the checkbox renders
82
+ * unticked, which already says "no", while Save stays blocked on "required and still empty" until
83
+ * the operator ticks and unticks it. The create path was fixed for this and the edit path was not,
84
+ * because they built their drafts in two different places.
85
+ *
86
+ * Only DECLARED fields are copied. A key the descriptor does not mention is invisible to every
87
+ * control on the screen, so carrying it into the draft would send a value back on save that nobody
88
+ * could see or edit — and `eraseEntityPort` reports it as an `undeclared-field` violation on the
89
+ * way in, so it is already known to be adapter drift rather than data.
90
+ *
91
+ * `id` is never copied: it is implicit on every row and `createAdminEntityRegistry` rejects any
92
+ * descriptor that declares it as a field.
93
+ *
94
+ * @param descriptor The entity being edited.
95
+ * @param row An existing row, or `null` when creating.
96
+ * @returns A draft with one entry per declared field.
97
+ * @complexity Time O(fields).
98
+ * @overallScore 100
99
+ */
100
+ export declare function draftForRow({ descriptor, row }: {
101
+ readonly descriptor: AdminEntityDescriptor;
102
+ readonly row: AdminEntityRowData | null;
103
+ }): Record<string, unknown>;
104
+ /** Field names whose declared `required: true` is not satisfied by `draft`. */
105
+ export declare function missingRequiredFields({ descriptor, draft }: {
106
+ readonly descriptor: AdminEntityDescriptor;
107
+ readonly draft: Record<string, unknown>;
108
+ }): readonly string[];
109
+ /** One draft entry that holds a value its declared kind does not accept. */
110
+ export interface DraftFieldProblem {
111
+ readonly field: string;
112
+ /** Reads as "servings: is not an integer" — the port's own wording, not a second opinion. */
113
+ readonly detail: string;
114
+ }
115
+ /**
116
+ * Every draft entry whose value its declared kind would reject.
117
+ *
118
+ * Delegates to `describeAdminEntityFieldMismatch` — the SAME function `eraseEntityPort` uses to
119
+ * report adapter violations — rather than re-deciding here what an `integer` accepts. Two
120
+ * validators for one rule drift, and the drift is silent in the worst direction: the form saves a
121
+ * value the port then reports as a violation, so the write succeeds and the console blames the
122
+ * adapter for something the editor produced.
123
+ *
124
+ * The values this actually catches all come from `<input type="number">`, whose `step` constrains
125
+ * the spinner and not the parsed value: `1.5` typed into an `integer`, and `1e400` typed into a
126
+ * `real`, which becomes `Infinity` and then `null` the moment it meets `JSON.stringify`. Neither
127
+ * field kind appears in any descriptor the browser acceptance run renders, so neither has ever been
128
+ * seen to fail.
129
+ *
130
+ * Blank entries are skipped: an absent value is {@link missingRequiredFields}' business when the
131
+ * field is required, and legitimate when it is not.
132
+ *
133
+ * @complexity Time O(fields), plus the bounded JSON walk for each `json` field.
134
+ * @overallScore 100
135
+ */
136
+ export declare function invalidDraftFields({ descriptor, draft }: {
137
+ readonly descriptor: AdminEntityDescriptor;
138
+ readonly draft: Record<string, unknown>;
139
+ }): readonly DraftFieldProblem[];
140
+ /**
141
+ * ISO-8601 -> the `YYYY-MM-DDTHH:mm:ss` an `<input type="datetime-local" step="1">` requires, read
142
+ * as UTC.
143
+ *
144
+ * Read and written as UTC on purpose, and labelled as such next to the control. Converting to the
145
+ * viewer's local zone would round-trip a stored instant through two conversions and shift it by the
146
+ * offset whenever the two disagree about DST — a bug that is invisible for anyone in UTC and
147
+ * corrupts timestamps for everyone else.
148
+ *
149
+ * Seconds are carried, not trimmed. The obvious `slice(0, 16)` matches the `datetime-local`
150
+ * default, and quietly rewrites `10:30:45Z` to `10:30:00Z` on the next Save of a row nobody meant
151
+ * to change the time of. Every timestamp in the dummy app is `00:00:00.000Z`, so the truncation is
152
+ * a no-op in the only data the acceptance run sees.
153
+ *
154
+ * @param value A stored field value, of any type.
155
+ * @returns The control's value, or `""` for anything not a parseable timestamp — which is what an
156
+ * empty `datetime-local` shows anyway.
157
+ * @complexity Time O(1).
158
+ * @overallScore 100
159
+ */
160
+ export declare function toDatetimeLocalValue({ value }: {
161
+ readonly value: unknown;
162
+ }): string;
163
+ /**
164
+ * The inverse: a `datetime-local` control's value -> a stored ISO-8601 instant, read as UTC.
165
+ *
166
+ * The `Z` is what makes it UTC — without it the string is parsed in the browser's local zone, which
167
+ * is the half of the round trip that silently shifts an instant for everyone not in UTC.
168
+ *
169
+ * @param raw The control's `value`, `YYYY-MM-DDTHH:mm` or `YYYY-MM-DDTHH:mm:ss`.
170
+ * @returns `undefined` for an empty control; the ISO instant otherwise; and `raw` UNCHANGED when it
171
+ * will not parse — so `invalidDraftFields` reports it and Save is blocked, rather than this
172
+ * throwing a `RangeError` through a render or silently discarding what the operator typed.
173
+ * @complexity Time O(1).
174
+ * @overallScore 100
175
+ */
176
+ export declare function fromDatetimeLocalValue({ raw }: {
177
+ readonly raw: string;
178
+ }): string | undefined;
179
+ //# sourceMappingURL=rules.d.ts.map
@@ -0,0 +1,224 @@
1
+ import { describeAdminEntityFieldMismatch } from '../../core/entities/rules.js';
2
+ export const RELATION_OPTION_LIMIT = 100;
3
+ export function resolveEntityPageSize({ raw }) {
4
+ const parsed = Number(raw);
5
+ return Number.isInteger(parsed) && parsed > 0 ? Math.min(parsed, 200) : 25;
6
+ }
7
+ /** Every `relation` field on a descriptor, narrowed so `target` is reachable. */
8
+ export function relationFieldsOf({ descriptor }) {
9
+ return descriptor.fields.filter((field) => field.kind === "relation");
10
+ }
11
+ /**
12
+ * Loads the option set for every distinct relation target on `descriptor`.
13
+ *
14
+ * @param descriptor The entity whose relations need resolving.
15
+ * @param registry Host registry resolving a target's existing entity port.
16
+ * @returns One entry per distinct target. A target that does not resolve is simply absent, and the
17
+ * screens then render the raw stored id marked as unresolved rather than a blank cell.
18
+ * @complexity One `list` call per distinct target, in parallel; O(targets × limit) rows held.
19
+ * @overallScore 100
20
+ */
21
+ export async function loadRelationIndex({ descriptor, registry }) {
22
+ const targets = [...new Set(relationFieldsOf({ descriptor }).map((field) => field.target))];
23
+ const loaded = await Promise.all(targets.map(async (target) => {
24
+ const port = registry.getEntity({ name: target });
25
+ if (port === null)
26
+ return null;
27
+ const page = await port.list({ limit: RELATION_OPTION_LIMIT });
28
+ const { titleField } = port.descriptor;
29
+ const options = page.items.map((row) => ({
30
+ id: row.id,
31
+ title: readTitle({ row, titleField }) ?? row.id,
32
+ }));
33
+ const titles = Object.create(null);
34
+ for (const option of options)
35
+ titles[option.id] = option.title;
36
+ // Truncation is read from `nextCursor`, not from `total`: `total` is optional on the port,
37
+ // so an adapter that omits it would make every over-long target silently look complete.
38
+ return [target, { options, titles, truncated: page.nextCursor !== null }];
39
+ }));
40
+ return Object.fromEntries(loaded.filter((entry) => entry !== null));
41
+ }
42
+ /**
43
+ * A row's human-readable title, or `null` when the descriptor's `titleField` does not resolve to a
44
+ * string on this row.
45
+ *
46
+ * Returns `null` rather than `String(value)` so a caller falls back to the id: a picker rendering
47
+ * the literal text `undefined` for every row is the exact symptom of a mistyped `titleField`, and
48
+ * `createAdminEntityRegistry` already rejects that at startup — this guards the remaining case,
49
+ * where the field is declared correctly and one row simply has no value for it.
50
+ *
51
+ * @complexity Time O(1).
52
+ * @overallScore 100
53
+ */
54
+ export function readTitle({ row, titleField }) {
55
+ const value = row[titleField];
56
+ return typeof value === "string" && value !== "" ? value : null;
57
+ }
58
+ /**
59
+ * The draft a "new" form starts from.
60
+ *
61
+ * Every declared field is present and absent-valued, so each control is controlled from the first
62
+ * render rather than switching from uncontrolled on the first keystroke.
63
+ *
64
+ * The one exception is a REQUIRED boolean, which starts `false`. A checkbox has no third state: an
65
+ * untouched one already means "no", so leaving it absent would block Save on a field the operator
66
+ * has, visibly, already answered — they would have to tick it and untick it to satisfy a
67
+ * requirement the unticked box was expressing all along. An OPTIONAL boolean stays absent, because
68
+ * there "unanswered" and "no" are genuinely different and only the descriptor's author knows which
69
+ * the row means.
70
+ *
71
+ * @complexity Time O(fields).
72
+ * @overallScore 100
73
+ */
74
+ export function emptyDraft({ descriptor }) {
75
+ const draft = Object.create(null);
76
+ for (const field of descriptor.fields) {
77
+ draft[field.name] = field.kind === "boolean" && field.required === true ? false : undefined;
78
+ }
79
+ return draft;
80
+ }
81
+ /**
82
+ * The draft an editor opens with: {@link emptyDraft}'s floor, with `row`'s declared values on top.
83
+ *
84
+ * Layered rather than `{ ...row }`, and that is the whole point. A row is not guaranteed to carry
85
+ * every declared field — an app that adds a `required` field to its descriptor has existing rows
86
+ * that predate it — and a draft built from the row alone inherits that gap. For a required BOOLEAN
87
+ * the consequence is {@link emptyDraft}'s trap arriving by the other door: the checkbox renders
88
+ * unticked, which already says "no", while Save stays blocked on "required and still empty" until
89
+ * the operator ticks and unticks it. The create path was fixed for this and the edit path was not,
90
+ * because they built their drafts in two different places.
91
+ *
92
+ * Only DECLARED fields are copied. A key the descriptor does not mention is invisible to every
93
+ * control on the screen, so carrying it into the draft would send a value back on save that nobody
94
+ * could see or edit — and `eraseEntityPort` reports it as an `undeclared-field` violation on the
95
+ * way in, so it is already known to be adapter drift rather than data.
96
+ *
97
+ * `id` is never copied: it is implicit on every row and `createAdminEntityRegistry` rejects any
98
+ * descriptor that declares it as a field.
99
+ *
100
+ * @param descriptor The entity being edited.
101
+ * @param row An existing row, or `null` when creating.
102
+ * @returns A draft with one entry per declared field.
103
+ * @complexity Time O(fields).
104
+ * @overallScore 100
105
+ */
106
+ export function draftForRow({ descriptor, row }) {
107
+ const draft = emptyDraft({ descriptor });
108
+ if (row === null)
109
+ return draft;
110
+ for (const field of descriptor.fields) {
111
+ // Skips `undefined` rather than spreading: an adapter that patches a row with an absent value
112
+ // leaves an OWN key holding `undefined`, and spreading that would overwrite the `false` floor a
113
+ // required boolean depends on.
114
+ const value = row[field.name];
115
+ if (value !== undefined)
116
+ draft[field.name] = value;
117
+ }
118
+ return draft;
119
+ }
120
+ /**
121
+ * Whether `value` reads as "the operator has not filled this in".
122
+ *
123
+ * `false` and `0` are deliberately NOT blank — they are answers, and a truthiness test that calls
124
+ * them empty is the single most reliable way to make a boolean or a numeric field unsaveable at its
125
+ * most ordinary value. `""` is blank for every kind whose control is a text box, but NOT for
126
+ * `json`, where the JSON string `""` is a legitimate stored value that is visibly on screen.
127
+ *
128
+ * @complexity Time O(1).
129
+ * @overallScore 100
130
+ */
131
+ function isBlankDraftValue(field, value) {
132
+ if (value === undefined)
133
+ return true;
134
+ return field.kind !== "json" && value === "";
135
+ }
136
+ /** Field names whose declared `required: true` is not satisfied by `draft`. */
137
+ export function missingRequiredFields({ descriptor, draft }) {
138
+ return descriptor.fields
139
+ .filter((field) => field.required === true)
140
+ .filter((field) => isBlankDraftValue(field, draft[field.name]))
141
+ .map((field) => field.name);
142
+ }
143
+ /**
144
+ * Every draft entry whose value its declared kind would reject.
145
+ *
146
+ * Delegates to `describeAdminEntityFieldMismatch` — the SAME function `eraseEntityPort` uses to
147
+ * report adapter violations — rather than re-deciding here what an `integer` accepts. Two
148
+ * validators for one rule drift, and the drift is silent in the worst direction: the form saves a
149
+ * value the port then reports as a violation, so the write succeeds and the console blames the
150
+ * adapter for something the editor produced.
151
+ *
152
+ * The values this actually catches all come from `<input type="number">`, whose `step` constrains
153
+ * the spinner and not the parsed value: `1.5` typed into an `integer`, and `1e400` typed into a
154
+ * `real`, which becomes `Infinity` and then `null` the moment it meets `JSON.stringify`. Neither
155
+ * field kind appears in any descriptor the browser acceptance run renders, so neither has ever been
156
+ * seen to fail.
157
+ *
158
+ * Blank entries are skipped: an absent value is {@link missingRequiredFields}' business when the
159
+ * field is required, and legitimate when it is not.
160
+ *
161
+ * @complexity Time O(fields), plus the bounded JSON walk for each `json` field.
162
+ * @overallScore 100
163
+ */
164
+ export function invalidDraftFields({ descriptor, draft }) {
165
+ const problems = [];
166
+ for (const field of descriptor.fields) {
167
+ const value = draft[field.name];
168
+ if (isBlankDraftValue(field, value))
169
+ continue;
170
+ const detail = describeAdminEntityFieldMismatch(field, value);
171
+ if (detail !== null)
172
+ problems.push({ field: field.name, detail });
173
+ }
174
+ return problems;
175
+ }
176
+ /**
177
+ * ISO-8601 -> the `YYYY-MM-DDTHH:mm:ss` an `<input type="datetime-local" step="1">` requires, read
178
+ * as UTC.
179
+ *
180
+ * Read and written as UTC on purpose, and labelled as such next to the control. Converting to the
181
+ * viewer's local zone would round-trip a stored instant through two conversions and shift it by the
182
+ * offset whenever the two disagree about DST — a bug that is invisible for anyone in UTC and
183
+ * corrupts timestamps for everyone else.
184
+ *
185
+ * Seconds are carried, not trimmed. The obvious `slice(0, 16)` matches the `datetime-local`
186
+ * default, and quietly rewrites `10:30:45Z` to `10:30:00Z` on the next Save of a row nobody meant
187
+ * to change the time of. Every timestamp in the dummy app is `00:00:00.000Z`, so the truncation is
188
+ * a no-op in the only data the acceptance run sees.
189
+ *
190
+ * @param value A stored field value, of any type.
191
+ * @returns The control's value, or `""` for anything not a parseable timestamp — which is what an
192
+ * empty `datetime-local` shows anyway.
193
+ * @complexity Time O(1).
194
+ * @overallScore 100
195
+ */
196
+ export function toDatetimeLocalValue({ value }) {
197
+ if (typeof value !== "string" || value === "")
198
+ return "";
199
+ const parsed = new Date(value);
200
+ if (Number.isNaN(parsed.getTime()))
201
+ return "";
202
+ const iso = parsed.toISOString();
203
+ return iso.endsWith('.000Z') ? iso.slice(0, 19) : iso.slice(0, 23);
204
+ }
205
+ /**
206
+ * The inverse: a `datetime-local` control's value -> a stored ISO-8601 instant, read as UTC.
207
+ *
208
+ * The `Z` is what makes it UTC — without it the string is parsed in the browser's local zone, which
209
+ * is the half of the round trip that silently shifts an instant for everyone not in UTC.
210
+ *
211
+ * @param raw The control's `value`, `YYYY-MM-DDTHH:mm` or `YYYY-MM-DDTHH:mm:ss`.
212
+ * @returns `undefined` for an empty control; the ISO instant otherwise; and `raw` UNCHANGED when it
213
+ * will not parse — so `invalidDraftFields` reports it and Save is blocked, rather than this
214
+ * throwing a `RangeError` through a render or silently discarding what the operator typed.
215
+ * @complexity Time O(1).
216
+ * @overallScore 100
217
+ */
218
+ export function fromDatetimeLocalValue({ raw }) {
219
+ if (raw === "")
220
+ return undefined;
221
+ const parsed = new Date(`${raw}Z`);
222
+ return Number.isNaN(parsed.getTime()) ? raw : parsed.toISOString();
223
+ }
224
+ //# sourceMappingURL=rules.js.map
@@ -0,0 +1,45 @@
1
+ import type { AdminErasedEntityPort } from '../../core/ports/entities.js';
2
+ /** Resolves existing entity ports; replace the registry object when registrations change. */
3
+ export interface EntityRegistryPort {
4
+ listEntities(args: Record<string, never>): readonly AdminErasedEntityPort[];
5
+ getEntity(args: {
6
+ readonly name: string;
7
+ }): AdminErasedEntityPort | null;
8
+ }
9
+ export type EntityTranslate = (args: {
10
+ readonly key: string;
11
+ }) => string;
12
+ export interface EntityRouteTarget {
13
+ readonly entity?: string;
14
+ readonly id?: string;
15
+ readonly view?: 'list' | 'detail' | 'edit' | 'create';
16
+ }
17
+ export interface EntityRoutesPort {
18
+ href(args: EntityRouteTarget): string;
19
+ navigate(args: EntityRouteTarget): void;
20
+ }
21
+ export interface EntityScreenDependencies {
22
+ readonly registry: EntityRegistryPort;
23
+ readonly translate: EntityTranslate;
24
+ readonly routes: EntityRoutesPort;
25
+ }
26
+ export interface EntityListProps extends EntityScreenDependencies {
27
+ readonly entityName: string;
28
+ readonly limitParam: string | null;
29
+ }
30
+ export interface EntityDetailProps extends EntityScreenDependencies {
31
+ readonly entityName: string;
32
+ readonly id: string;
33
+ }
34
+ export interface EntityEditProps extends EntityScreenDependencies {
35
+ readonly entityName: string;
36
+ /** Null creates a row. All string ids, including `new`, refer to saved rows. */
37
+ readonly id: string | null;
38
+ }
39
+ /** Stable source templates let host dictionaries translate copy before values are inserted. */
40
+ export declare function entityText({ translate, key, values }: {
41
+ readonly translate: EntityTranslate;
42
+ readonly key: string;
43
+ readonly values: Readonly<Record<string, string | number>>;
44
+ }): string;
45
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1,5 @@
1
+ /** Stable source templates let host dictionaries translate copy before values are inserted. */
2
+ export function entityText({ translate, key, values }) {
3
+ return translate({ key: key }).replace(/\{([\w]+)\}/g, (match, name) => Object.hasOwn(values, name) ? String(values[name]) : match);
4
+ }
5
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1,35 @@
1
+ import type { AdminErasedEntityPort } from '../../core/ports/entities.js';
2
+ import type { EntityEditProps } from './types.js';
3
+ interface FormState {
4
+ readonly draft: Record<string, unknown>;
5
+ readonly jsonText: Readonly<Record<string, string>>;
6
+ readonly jsonErrors: Readonly<Record<string, boolean>>;
7
+ }
8
+ /** Called by the editor mounted for one entity/id pair. */
9
+ export declare function useEntityEdit({ port, ...props }: EntityEditProps & {
10
+ readonly port: AdminErasedEntityPort;
11
+ }): {
12
+ form: FormState | null;
13
+ read: {
14
+ readonly data: import("./use-entity-read.js").EntityLoadedRow | undefined;
15
+ readonly error: unknown;
16
+ readonly loading: boolean;
17
+ };
18
+ missing: readonly string[];
19
+ invalid: readonly import("./rules.js").DraftFieldProblem[];
20
+ canSave: boolean;
21
+ pending: "remove" | "save" | null;
22
+ failed: boolean;
23
+ setField: ({ name, value }: {
24
+ readonly name: string;
25
+ readonly value: unknown;
26
+ }) => void;
27
+ setJsonText: ({ name, text }: {
28
+ readonly name: string;
29
+ readonly text: string;
30
+ }) => void;
31
+ save: () => Promise<void>;
32
+ remove: () => Promise<void>;
33
+ };
34
+ export {};
35
+ //# sourceMappingURL=use-entity-edit.d.ts.map
@@ -0,0 +1,98 @@
1
+ import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
2
+ import { draftForRow, invalidDraftFields, missingRequiredFields } from './rules.js';
3
+ import { loadEntityRow, useEntityRead } from './use-entity-read.js';
4
+ /** Called by the editor mounted for one entity/id pair. */
5
+ export function useEntityEdit({ port, ...props }) {
6
+ const { entityName, id, registry, routes } = props;
7
+ const scope = useMemo(() => ({ port, registry, entityName, id }), [port, registry, entityName, id]);
8
+ const currentScope = useRef(scope);
9
+ currentScope.current = scope;
10
+ const mounted = useRef(true);
11
+ useEffect(() => { mounted.current = true; return () => { mounted.current = false; }; }, []);
12
+ const load = useCallback(() => loadEntityRow({ registry, entityName, id }), [registry, entityName, id, port]);
13
+ const read = useEntityRead({ load, scope });
14
+ const [formState, setFormState] = useState(null);
15
+ const [mutation, setMutation] = useState({ scope, kind: null, failed: false });
16
+ const busy = useRef(false);
17
+ let form = formState?.scope === scope ? formState.form : null;
18
+ if (read.data && form === null && (id === null || read.data.row !== null)) {
19
+ const draft = draftForRow({ descriptor: port.descriptor, row: read.data.row });
20
+ const jsonText = Object.create(null);
21
+ for (const field of port.descriptor.fields) {
22
+ if (field.kind === 'json')
23
+ jsonText[field.name] = draft[field.name] === undefined ? '' : JSON.stringify(draft[field.name], null, 2);
24
+ }
25
+ form = { draft, jsonText, jsonErrors: {} };
26
+ setFormState({ scope, form });
27
+ }
28
+ const draft = form?.draft ?? {};
29
+ const missing = missingRequiredFields({ descriptor: port.descriptor, draft });
30
+ const invalid = invalidDraftFields({ descriptor: port.descriptor, draft });
31
+ const pending = mutation.scope === scope ? mutation.kind : null;
32
+ const canSave = form !== null && missing.length === 0 && invalid.length === 0 &&
33
+ !Object.values(form.jsonErrors).some(Boolean) && pending === null;
34
+ function setField({ name, value }) {
35
+ if (!port.descriptor.fields.some((field) => field.name === name) || busy.current)
36
+ return;
37
+ setFormState((state) => state?.scope === scope ? { scope, form: { ...state.form, draft: { ...state.form.draft, [name]: value } } } : state);
38
+ }
39
+ function setJsonText({ name, text }) {
40
+ if (!port.descriptor.fields.some((field) => field.name === name && field.kind === 'json') || busy.current)
41
+ return;
42
+ let value;
43
+ let failed = false;
44
+ try {
45
+ value = text.trim() === '' ? undefined : JSON.parse(text);
46
+ }
47
+ catch {
48
+ failed = true;
49
+ }
50
+ setFormState((state) => state?.scope === scope ? {
51
+ scope,
52
+ form: {
53
+ draft: failed ? state.form.draft : { ...state.form.draft, [name]: value },
54
+ jsonText: { ...state.form.jsonText, [name]: text },
55
+ jsonErrors: { ...state.form.jsonErrors, [name]: failed },
56
+ },
57
+ } : state);
58
+ }
59
+ async function mutate({ kind }) {
60
+ if (busy.current || (kind === 'save' && !canSave) || (kind === 'remove' && (id === null || port.remove === undefined)))
61
+ return;
62
+ busy.current = true;
63
+ setMutation({ scope, kind, failed: false });
64
+ const active = () => mounted.current && currentScope.current === scope;
65
+ try {
66
+ if (kind === 'remove' && id !== null && port.remove) {
67
+ await port.remove(id);
68
+ if (active())
69
+ routes.navigate({ entity: entityName });
70
+ }
71
+ else {
72
+ // A create omits blanks; an update carries explicit undefined to clear optional fields.
73
+ const saved = id === null
74
+ ? await port.create(Object.fromEntries(Object.entries(draft).filter(([, value]) => value !== undefined)))
75
+ : await port.update(id, draft);
76
+ if (active())
77
+ routes.navigate({ entity: entityName, id: saved.id });
78
+ }
79
+ if (active())
80
+ setMutation({ scope, kind: null, failed: false });
81
+ }
82
+ catch {
83
+ if (active())
84
+ setMutation({ scope, kind: null, failed: true });
85
+ }
86
+ finally {
87
+ busy.current = false;
88
+ }
89
+ }
90
+ return {
91
+ form, read, missing, invalid, canSave, pending,
92
+ failed: mutation.scope === scope && mutation.failed,
93
+ setField, setJsonText,
94
+ save: () => mutate({ kind: 'save' }),
95
+ remove: () => mutate({ kind: 'remove' }),
96
+ };
97
+ }
98
+ //# sourceMappingURL=use-entity-edit.js.map
@@ -0,0 +1,22 @@
1
+ import type { AdminEntityRowData } from '../../core/ports/entities.js';
2
+ import type { EntityRegistryPort } from './types.js';
3
+ import { type RelationIndex } from './rules.js';
4
+ /** Per-mount async state; stale results are discarded and reads never overwrite another route. */
5
+ export declare function useEntityRead<T>({ load, scope }: {
6
+ readonly load: () => Promise<T>;
7
+ readonly scope: unknown;
8
+ }): {
9
+ readonly data: T | undefined;
10
+ readonly error: unknown;
11
+ readonly loading: boolean;
12
+ };
13
+ export interface EntityLoadedRow {
14
+ readonly row: AdminEntityRowData | null;
15
+ readonly relations: RelationIndex;
16
+ }
17
+ export declare function loadEntityRow({ registry, entityName, id }: {
18
+ readonly registry: EntityRegistryPort;
19
+ readonly entityName: string;
20
+ readonly id: string | null;
21
+ }): Promise<EntityLoadedRow>;
22
+ //# sourceMappingURL=use-entity-read.d.ts.map
@@ -0,0 +1,27 @@
1
+ import { useEffect, useState } from 'react';
2
+ import { loadRelationIndex } from './rules.js';
3
+ /** Per-mount async state; stale results are discarded and reads never overwrite another route. */
4
+ export function useEntityRead({ load, scope }) {
5
+ const [state, setState] = useState({ scope, load });
6
+ useEffect(() => {
7
+ let active = true;
8
+ setState({ scope, load });
9
+ Promise.resolve().then(load).then((data) => { if (active)
10
+ setState({ scope, load, data }); }, (error) => { if (active)
11
+ setState({ scope, load, error }); });
12
+ return () => { active = false; };
13
+ }, [load, scope]);
14
+ const current = state.scope === scope && state.load === load ? state : null;
15
+ return { data: current?.data, error: current?.error, loading: current === null || (!Object.hasOwn(current, 'data') && !Object.hasOwn(current, 'error')) };
16
+ }
17
+ export async function loadEntityRow({ registry, entityName, id }) {
18
+ const port = registry.getEntity({ name: entityName });
19
+ if (port === null)
20
+ return { row: null, relations: {} };
21
+ const [row, relations] = await Promise.all([
22
+ id === null ? Promise.resolve(null) : port.get(id),
23
+ loadRelationIndex({ descriptor: port.descriptor, registry }),
24
+ ]);
25
+ return { row, relations };
26
+ }
27
+ //# sourceMappingURL=use-entity-read.js.map
@@ -33,8 +33,12 @@ export declare const DEFAULT_NAV_SECTIONS_STORAGE_KEY = "jini-admin-nav-sections
33
33
  export type NavSectionState = Readonly<Record<string, boolean>>;
34
34
  export interface NavSections {
35
35
  /** `true` when the section should render its items. Any label with no stored preference is open. */
36
- isOpen: (groupLabel: string) => boolean;
37
- toggle: (groupLabel: string) => void;
36
+ isOpen: (requiredArgs: {
37
+ readonly groupLabel: string;
38
+ }) => boolean;
39
+ toggle: (requiredArgs: {
40
+ readonly groupLabel: string;
41
+ }) => void;
38
42
  }
39
43
  /**
40
44
  * @param storageKey `localStorage` key to persist under. Defaults to
@@ -46,5 +50,10 @@ export interface NavSections {
46
50
  * handful, fixed by the host's panel manifest, never user-variable.
47
51
  * @overallScore 100
48
52
  */
49
- export declare function useNavSections(storageKey?: string): NavSections;
53
+ export declare function useNavSections({ storage, events }: {
54
+ readonly storage: Pick<Storage, 'getItem' | 'setItem'>;
55
+ readonly events: Pick<Window, 'addEventListener' | 'removeEventListener'>;
56
+ }, { storageKey }?: {
57
+ readonly storageKey?: string;
58
+ }): NavSections;
50
59
  //# sourceMappingURL=use-nav-sections.d.ts.map