@nocobase/portal-template-default 1.0.8 → 1.0.10

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 (262) hide show
  1. package/.env.example +5 -0
  2. package/.github/workflows/publish.yml +106 -0
  3. package/.gitignore +33 -0
  4. package/AGENTS.md +19 -0
  5. package/README.MD +61 -180
  6. package/package.json +3 -5
  7. package/scripts/build-html.mjs +12 -0
  8. package/src/App.css +2 -0
  9. package/src/App.tsx +35 -186
  10. package/src/app/development.tsx +154 -0
  11. package/src/app/extension.ts +5 -0
  12. package/src/app/extensions.tsx +18 -4
  13. package/src/app/routes.tsx +65 -0
  14. package/src/components/access-control/acl-bootstrap.tsx +11 -3
  15. package/src/components/access-control/acl-gate.tsx +7 -6
  16. package/src/components/access-control/can-access.tsx +17 -0
  17. package/src/components/access-control/navigate-to-accessible-resource.tsx +8 -3
  18. package/src/components/app-shell/sidebar.tsx +21 -5
  19. package/src/components/auth/auto-redirect-provider.tsx +2 -1
  20. package/src/extensions/nocobase-acl/README.md +74 -0
  21. package/{registry → src/extensions}/nocobase-acl/components/acl-boundary.tsx +22 -25
  22. package/{registry → src/extensions}/nocobase-acl/components/role-switcher.tsx +26 -23
  23. package/src/extensions/nocobase-acl/demo/acl-preview-provider.tsx +81 -0
  24. package/{registry → src/extensions}/nocobase-acl/demo/boundary-api.tsx +58 -0
  25. package/{registry → src/extensions}/nocobase-acl/demo/index.tsx +243 -105
  26. package/{registry → src/extensions}/nocobase-acl/demo/prompt-generator.tsx +3 -1
  27. package/src/extensions/nocobase-acl/extension.tsx +73 -0
  28. package/{registry → src/extensions}/nocobase-ai/README.md +21 -5
  29. package/{registry → src/extensions}/nocobase-ai/components/chat/chat-composer.tsx +3 -1
  30. package/{registry → src/extensions}/nocobase-ai/components/chat/chat-messages.tsx +38 -3
  31. package/{registry → src/extensions}/nocobase-ai/components/chat/tool-call-card.tsx +1 -9
  32. package/{registry → src/extensions}/nocobase-ai/components/index.ts +5 -0
  33. package/src/extensions/nocobase-ai/components/surfaces/chat-dialog.tsx +17 -0
  34. package/src/extensions/nocobase-ai/components/surfaces/chat-side-panel.tsx +35 -0
  35. package/src/extensions/nocobase-ai/components/surfaces/chat-surface.tsx +108 -0
  36. package/{registry → src/extensions}/nocobase-ai/components/triggers/ai-chat-floating-trigger.tsx +2 -2
  37. package/{registry → src/extensions}/nocobase-ai/demo/configuration-gate.tsx +4 -1
  38. package/{registry → src/extensions}/nocobase-ai/demo/container-showcase.tsx +2 -2
  39. package/{registry → src/extensions}/nocobase-ai/demo/floating.tsx +45 -15
  40. package/{registry → src/extensions}/nocobase-ai/demo/index.tsx +116 -74
  41. package/{registry → src/extensions}/nocobase-ai/demo/prompt-generator.tsx +3 -3
  42. package/{registry → src/extensions}/nocobase-ai/demo/tool-cards.tsx +2 -3
  43. package/src/extensions/nocobase-ai/extension.tsx +160 -0
  44. package/{registry → src/extensions}/nocobase-ai/global-ai-chat.tsx +12 -28
  45. package/{registry → src/extensions}/nocobase-ai/providers/ai-provider.tsx +14 -1
  46. package/{registry → src/extensions}/nocobase-ai/providers/chat-context.tsx +1 -0
  47. package/{registry → src/extensions}/nocobase-ai/providers/chat-provider.tsx +29 -8
  48. package/{registry → src/extensions}/nocobase-ai/providers/use-chat-attachments.ts +6 -1
  49. package/{registry → src/extensions}/nocobase-ai/providers/use-chat-work-context.ts +4 -1
  50. package/{registry → src/extensions}/nocobase-route-surfaces/README.md +2 -1
  51. package/{registry → src/extensions}/nocobase-route-surfaces/demo/index.tsx +33 -7
  52. package/{registry → src/extensions}/nocobase-route-surfaces/demo/scenarios.ts +20 -14
  53. package/{registry → src/extensions}/nocobase-route-surfaces/extension.tsx +55 -52
  54. package/src/{pages/users → extensions/nocobase-users-example}/create.tsx +4 -4
  55. package/src/{pages/users → extensions/nocobase-users-example}/edit.tsx +5 -9
  56. package/src/extensions/nocobase-users-example/extension.tsx +109 -0
  57. package/src/{pages/users → extensions/nocobase-users-example}/form-context.ts +11 -9
  58. package/src/extensions/nocobase-users-example/layout.tsx +16 -0
  59. package/src/{pages/users → extensions/nocobase-users-example}/list.tsx +1 -1
  60. package/src/extensions/nocobase-users-example/locales/en-US.ts +77 -0
  61. package/src/extensions/nocobase-users-example/locales/index.ts +8 -0
  62. package/src/extensions/nocobase-users-example/locales/zh-CN.ts +73 -0
  63. package/src/extensions/nocobase-users-example/optional-ai.ts +67 -0
  64. package/src/{pages/users → extensions/nocobase-users-example}/role-detail.tsx +4 -15
  65. package/src/{pages/users → extensions/nocobase-users-example}/routes.ts +1 -4
  66. package/src/extensions/nocobase-users-example/show.tsx +266 -0
  67. package/src/lib/nocobase/acl/context.ts +32 -0
  68. package/src/lib/nocobase/acl/evaluator.ts +186 -0
  69. package/src/lib/nocobase/acl/hooks.ts +40 -0
  70. package/src/lib/nocobase/acl/index.ts +4 -1
  71. package/src/lib/nocobase/acl/menu.ts +6 -6
  72. package/src/lib/nocobase/acl/record-permissions.ts +37 -7
  73. package/src/lib/nocobase/acl/store-provider.tsx +14 -0
  74. package/src/lib/nocobase/acl/store.ts +92 -86
  75. package/src/lib/nocobase/acl/types.ts +52 -32
  76. package/src/lib/nocobase/auth-session.ts +128 -0
  77. package/src/lib/nocobase/client.ts +48 -50
  78. package/src/locales/en-US.ts +0 -78
  79. package/src/locales/index.ts +2 -7
  80. package/src/locales/zh-CN.ts +0 -74
  81. package/src/providers/access-control.ts +43 -8
  82. package/src/providers/auth-callback.ts +1 -0
  83. package/src/providers/auth.ts +24 -26
  84. package/src/providers/constants.ts +0 -4
  85. package/src/providers/data.ts +14 -26
  86. package/src/providers/runtime-config.ts +40 -0
  87. package/src/vite-env.d.ts +9 -0
  88. package/tests/auth-session-regression.mjs +101 -0
  89. package/tsconfig.json +2 -1
  90. package/vite.config.ts +9 -1
  91. package/registry/nocobase-acl/README.md +0 -19
  92. package/registry/nocobase-acl/extension.tsx +0 -71
  93. package/registry/nocobase-acl/tests/acl-regression.mjs +0 -314
  94. package/registry/nocobase-ai/components/surfaces/chat-dialog.tsx +0 -31
  95. package/registry/nocobase-ai/components/surfaces/chat-side-panel.tsx +0 -74
  96. package/registry/nocobase-ai/extension.tsx +0 -158
  97. package/registry/nocobase-ai/tests/business-report-regression.mjs +0 -70
  98. package/registry/nocobase-ai/tests/form-filler-regression.mjs +0 -129
  99. package/registry/nocobase-ai/tests/frontend-tool-regression.mjs +0 -128
  100. package/registry/nocobase-ai/tests/page-context-regression.mjs +0 -48
  101. package/registry/nocobase-ai/tests/stream-regression.mjs +0 -207
  102. package/registry/nocobase-auth-cas/README.md +0 -6
  103. package/registry/nocobase-auth-cas/cas-sign-in-button.tsx +0 -41
  104. package/registry/nocobase-auth-cas/demo.tsx +0 -20
  105. package/registry/nocobase-auth-cas/extension.tsx +0 -46
  106. package/registry/nocobase-auth-cas/index.ts +0 -1
  107. package/registry/nocobase-auth-cas/use-cas-sign-in.ts +0 -29
  108. package/registry/nocobase-auth-dingtalk/README.md +0 -7
  109. package/registry/nocobase-auth-dingtalk/auto-login-provider.tsx +0 -154
  110. package/registry/nocobase-auth-dingtalk/demo.tsx +0 -23
  111. package/registry/nocobase-auth-dingtalk/dingtalk-sign-in-button.tsx +0 -45
  112. package/registry/nocobase-auth-dingtalk/extension.tsx +0 -55
  113. package/registry/nocobase-auth-dingtalk/index.ts +0 -4
  114. package/registry/nocobase-auth-dingtalk/use-dingtalk-sign-in.ts +0 -64
  115. package/registry/nocobase-auth-ldap/README.md +0 -6
  116. package/registry/nocobase-auth-ldap/demo.tsx +0 -21
  117. package/registry/nocobase-auth-ldap/extension.tsx +0 -46
  118. package/registry/nocobase-auth-ldap/index.ts +0 -1
  119. package/registry/nocobase-auth-ldap/ldap-sign-in-form.tsx +0 -69
  120. package/registry/nocobase-auth-ldap/use-ldap-sign-in.ts +0 -5
  121. package/registry/nocobase-auth-oidc/README.md +0 -11
  122. package/registry/nocobase-auth-oidc/auto-redirect-provider.tsx +0 -13
  123. package/registry/nocobase-auth-oidc/demo.tsx +0 -20
  124. package/registry/nocobase-auth-oidc/extension.tsx +0 -51
  125. package/registry/nocobase-auth-oidc/index.ts +0 -1
  126. package/registry/nocobase-auth-oidc/oidc-sign-in-button.tsx +0 -44
  127. package/registry/nocobase-auth-oidc/use-oidc-sign-in.ts +0 -58
  128. package/registry/nocobase-auth-saml/README.md +0 -6
  129. package/registry/nocobase-auth-saml/auto-redirect-provider.tsx +0 -13
  130. package/registry/nocobase-auth-saml/demo.tsx +0 -20
  131. package/registry/nocobase-auth-saml/extension.tsx +0 -51
  132. package/registry/nocobase-auth-saml/index.ts +0 -1
  133. package/registry/nocobase-auth-saml/saml-sign-in-button.tsx +0 -43
  134. package/registry/nocobase-auth-saml/use-saml-sign-in.ts +0 -51
  135. package/registry/nocobase-auth-sms/README.md +0 -7
  136. package/registry/nocobase-auth-sms/demo.tsx +0 -25
  137. package/registry/nocobase-auth-sms/extension.tsx +0 -46
  138. package/registry/nocobase-auth-sms/index.ts +0 -1
  139. package/registry/nocobase-auth-sms/sms-sign-in-form.tsx +0 -107
  140. package/registry/nocobase-auth-sms/use-sms-sign-in.ts +0 -88
  141. package/registry/nocobase-auth-wecom/README.md +0 -6
  142. package/registry/nocobase-auth-wecom/auto-login-provider.tsx +0 -21
  143. package/registry/nocobase-auth-wecom/demo.tsx +0 -23
  144. package/registry/nocobase-auth-wecom/extension.tsx +0 -49
  145. package/registry/nocobase-auth-wecom/index.ts +0 -1
  146. package/registry/nocobase-auth-wecom/use-wecom-sign-in.ts +0 -62
  147. package/registry/nocobase-auth-wecom/wecom-sign-in-button.tsx +0 -70
  148. package/registry/nocobase-i18n/README.md +0 -44
  149. package/registry/nocobase-i18n/components/index.ts +0 -1
  150. package/registry/nocobase-i18n/components/language-switcher.tsx +0 -138
  151. package/registry/nocobase-i18n/demo/index.tsx +0 -209
  152. package/registry/nocobase-i18n/demo/prompt-generator.tsx +0 -132
  153. package/registry/nocobase-i18n/extension.tsx +0 -45
  154. package/registry/nocobase-i18n/index.ts +0 -7
  155. package/registry/nocobase-i18n/locales/en-US.ts +0 -39
  156. package/registry/nocobase-i18n/locales/index.ts +0 -8
  157. package/registry/nocobase-i18n/locales/zh-CN.ts +0 -35
  158. package/registry/nocobase-i18n/provider.tsx +0 -44
  159. package/registry/nocobase-i18n/server-resources.ts +0 -166
  160. package/registry/nocobase-i18n/tests/i18n-regression.mjs +0 -51
  161. package/registry.config.json +0 -302
  162. package/scripts/registry.mjs +0 -164
  163. package/src/components/access-control/resource-access-guard.tsx +0 -21
  164. package/src/components/access-control/use-acl-runtime.ts +0 -28
  165. package/src/lib/nocobase/acl/action.ts +0 -147
  166. package/src/pages/users/layout.tsx +0 -15
  167. package/src/pages/users/show.tsx +0 -278
  168. /package/{registry → src/extensions}/nocobase-acl/components/index.ts +0 -0
  169. /package/{registry → src/extensions}/nocobase-acl/components/role-options.ts +0 -0
  170. /package/{registry → src/extensions}/nocobase-acl/demo/components.tsx +0 -0
  171. /package/{registry → src/extensions}/nocobase-acl/demo/lazy-route.tsx +0 -0
  172. /package/{registry → src/extensions}/nocobase-acl/demo/role-switcher-prompt-generator.tsx +0 -0
  173. /package/{registry → src/extensions}/nocobase-acl/demo/scenario-section.tsx +0 -0
  174. /package/{registry → src/extensions}/nocobase-acl/index.ts +0 -0
  175. /package/{registry → src/extensions}/nocobase-ai/adapters/react-hook-form.ts +0 -0
  176. /package/{registry → src/extensions}/nocobase-ai/assets/nocobase-ai-chat.svg +0 -0
  177. /package/{registry → src/extensions}/nocobase-ai/components/ai-root-provider.tsx +0 -0
  178. /package/{registry → src/extensions}/nocobase-ai/components/chat/ai-employee-avatar.tsx +0 -0
  179. /package/{registry → src/extensions}/nocobase-ai/components/chat/chat-attachment.tsx +0 -0
  180. /package/{registry → src/extensions}/nocobase-ai/components/chat/chat-compact.tsx +0 -0
  181. /package/{registry → src/extensions}/nocobase-ai/components/chat/chat-empty-state.tsx +0 -0
  182. /package/{registry → src/extensions}/nocobase-ai/components/chat/chat-header.tsx +0 -0
  183. /package/{registry → src/extensions}/nocobase-ai/components/chat/chat-history-dialog.tsx +0 -0
  184. /package/{registry → src/extensions}/nocobase-ai/components/chat/chat-message.tsx +0 -0
  185. /package/{registry → src/extensions}/nocobase-ai/components/chat/chat-window.tsx +0 -0
  186. /package/{registry → src/extensions}/nocobase-ai/components/chat/conversation-list.tsx +0 -0
  187. /package/{registry → src/extensions}/nocobase-ai/components/chat/markdown-message.tsx +0 -0
  188. /package/{registry → src/extensions}/nocobase-ai/components/chat/model-select-options.tsx +0 -0
  189. /package/{registry → src/extensions}/nocobase-ai/components/chat/reasoning-panel.tsx +0 -0
  190. /package/{registry → src/extensions}/nocobase-ai/components/chat/sub-agent-conversation.tsx +0 -0
  191. /package/{registry → src/extensions}/nocobase-ai/components/chat/user-prompt-editor.tsx +0 -0
  192. /package/{registry → src/extensions}/nocobase-ai/components/chat/work-context-chip.tsx +0 -0
  193. /package/{registry → src/extensions}/nocobase-ai/components/page-elements/ai-form.tsx +0 -0
  194. /package/{registry → src/extensions}/nocobase-ai/components/page-elements/page-element-provider.tsx +0 -0
  195. /package/{registry → src/extensions}/nocobase-ai/components/surfaces/chat-inline.tsx +0 -0
  196. /package/{registry → src/extensions}/nocobase-ai/components/surfaces/chat-page.tsx +0 -0
  197. /package/{registry → src/extensions}/nocobase-ai/components/surfaces/chat-side-panel-layout.tsx +0 -0
  198. /package/{registry → src/extensions}/nocobase-ai/components/surfaces/chat-surface-actions.tsx +0 -0
  199. /package/{registry → src/extensions}/nocobase-ai/components/tools/builtin-tool-renderers.tsx +0 -0
  200. /package/{registry → src/extensions}/nocobase-ai/components/tools/business-report-dialog.tsx +0 -0
  201. /package/{registry → src/extensions}/nocobase-ai/components/tools/business-report-renderer.tsx +0 -0
  202. /package/{registry → src/extensions}/nocobase-ai/components/tools/business-report-utils.tsx +0 -0
  203. /package/{registry → src/extensions}/nocobase-ai/components/tools/chart-renderer.tsx +0 -0
  204. /package/{registry → src/extensions}/nocobase-ai/components/tools/echarts-preview.tsx +0 -0
  205. /package/{registry → src/extensions}/nocobase-ai/components/tools/echarts-runtime-advanced.ts +0 -0
  206. /package/{registry → src/extensions}/nocobase-ai/components/tools/echarts-runtime-common.ts +0 -0
  207. /package/{registry → src/extensions}/nocobase-ai/components/tools/echarts-runtime-components.ts +0 -0
  208. /package/{registry → src/extensions}/nocobase-ai/components/tools/echarts-runtime-hierarchy.ts +0 -0
  209. /package/{registry → src/extensions}/nocobase-ai/components/tools/echarts-runtime.ts +0 -0
  210. /package/{registry → src/extensions}/nocobase-ai/components/tools/sub-agent-renderer.tsx +0 -0
  211. /package/{registry → src/extensions}/nocobase-ai/components/tools/suggestions-renderer.tsx +0 -0
  212. /package/{registry → src/extensions}/nocobase-ai/components/tools/tool-renderer-provider.tsx +0 -0
  213. /package/{registry → src/extensions}/nocobase-ai/components/tools/tool-renderer-utils.ts +0 -0
  214. /package/{registry → src/extensions}/nocobase-ai/components/tools/workflow-renderer.tsx +0 -0
  215. /package/{registry → src/extensions}/nocobase-ai/components/triggers/ai-employee-shortcut.tsx +0 -0
  216. /package/{registry → src/extensions}/nocobase-ai/demo/interaction-showcase.tsx +0 -0
  217. /package/{registry → src/extensions}/nocobase-ai/demo/page-context-prompt-generator.tsx +0 -0
  218. /package/{registry → src/extensions}/nocobase-ai/demo/page-context.tsx +0 -0
  219. /package/{registry → src/extensions}/nocobase-ai/demo/page-element-showcase.tsx +0 -0
  220. /package/{registry → src/extensions}/nocobase-ai/demo/prompt-card.tsx +0 -0
  221. /package/{registry → src/extensions}/nocobase-ai/demo/shortcut.tsx +0 -0
  222. /package/{registry → src/extensions}/nocobase-ai/index.ts +0 -0
  223. /package/{registry → src/extensions}/nocobase-ai/providers/avatars.ts +0 -0
  224. /package/{registry → src/extensions}/nocobase-ai/providers/chat-controller.ts +0 -0
  225. /package/{registry → src/extensions}/nocobase-ai/providers/chat-message-utils.ts +0 -0
  226. /package/{registry → src/extensions}/nocobase-ai/providers/chat-reducer.ts +0 -0
  227. /package/{registry → src/extensions}/nocobase-ai/providers/chat-task-utils.ts +0 -0
  228. /package/{registry → src/extensions}/nocobase-ai/providers/chat-transport.ts +0 -0
  229. /package/{registry → src/extensions}/nocobase-ai/providers/form-registry.tsx +0 -0
  230. /package/{registry → src/extensions}/nocobase-ai/providers/frontend-tool-registry.tsx +0 -0
  231. /package/{registry → src/extensions}/nocobase-ai/providers/index.ts +0 -0
  232. /package/{registry → src/extensions}/nocobase-ai/providers/model.ts +0 -0
  233. /package/{registry → src/extensions}/nocobase-ai/providers/page-context.tsx +0 -0
  234. /package/{registry → src/extensions}/nocobase-ai/providers/stream-coalescer.ts +0 -0
  235. /package/{registry → src/extensions}/nocobase-ai/providers/stream-event-utils.ts +0 -0
  236. /package/{registry → src/extensions}/nocobase-ai/providers/stream-parser.ts +0 -0
  237. /package/{registry → src/extensions}/nocobase-ai/providers/sub-agent-stream.ts +0 -0
  238. /package/{registry → src/extensions}/nocobase-ai/providers/types.ts +0 -0
  239. /package/{registry → src/extensions}/nocobase-ai/providers/ui-message-stream.ts +0 -0
  240. /package/{registry → src/extensions}/nocobase-ai/providers/use-automatic-tool-approval.ts +0 -0
  241. /package/{registry → src/extensions}/nocobase-ai/providers/use-chat-message-actions.ts +0 -0
  242. /package/{registry → src/extensions}/nocobase-ai/providers/use-chat-runtime.ts +0 -0
  243. /package/{registry → src/extensions}/nocobase-ai/providers/use-conversation-catalog.ts +0 -0
  244. /package/{registry → src/extensions}/nocobase-ai/providers/use-conversation-history.ts +0 -0
  245. /package/{registry → src/extensions}/nocobase-ai/services/index.ts +0 -0
  246. /package/{registry → src/extensions}/nocobase-ai/services/nocobase-ai-service.ts +0 -0
  247. /package/{registry → src/extensions}/nocobase-ai/services/types.ts +0 -0
  248. /package/{registry → src/extensions}/nocobase-route-surfaces/demo/lazy-route.tsx +0 -0
  249. /package/{registry → src/extensions}/nocobase-route-surfaces/demo/prompt-generator.tsx +0 -0
  250. /package/{registry → src/extensions}/nocobase-route-surfaces/index.ts +0 -0
  251. /package/{registry → src/extensions}/nocobase-route-surfaces/route-dialog.tsx +0 -0
  252. /package/{registry → src/extensions}/nocobase-route-surfaces/route-drawer.tsx +0 -0
  253. /package/{registry → src/extensions}/nocobase-route-surfaces/route-page.tsx +0 -0
  254. /package/{registry → src/extensions}/nocobase-route-surfaces/route-surface-context.ts +0 -0
  255. /package/{registry → src/extensions}/nocobase-route-surfaces/use-refine-unsaved-changes.tsx +0 -0
  256. /package/{registry → src/extensions}/nocobase-route-surfaces/use-route-surface-close.ts +0 -0
  257. /package/{registry → src/extensions}/nocobase-route-surfaces/use-route-surface-state.ts +0 -0
  258. /package/src/{pages/users → extensions/nocobase-users-example}/form-fields.tsx +0 -0
  259. /package/src/{pages/users → extensions/nocobase-users-example}/index.ts +0 -0
  260. /package/src/{pages/users → extensions/nocobase-users-example}/role-badges.tsx +0 -0
  261. /package/src/{pages/users → extensions/nocobase-users-example}/role-utils.ts +0 -0
  262. /package/src/{pages/users → extensions/nocobase-users-example}/types.ts +0 -0
@@ -1,129 +0,0 @@
1
- import assert from "node:assert/strict";
2
- import { createServer } from "vite";
3
-
4
- const server = await createServer({
5
- appType: "custom",
6
- logLevel: "silent",
7
- server: { middlewareMode: true },
8
- });
9
-
10
- try {
11
- const { AIFormRegistry, createFormFillerInvoker } =
12
- await server.ssrLoadModule(
13
- "/registry/nocobase-ai/providers/form-registry.tsx"
14
- );
15
-
16
- const registry = new AIFormRegistry();
17
- const applied = [];
18
- const unregister = registry.register({
19
- id: "lead-form",
20
- title: "Lead form",
21
- fields: [
22
- { name: "company", type: "string" },
23
- {
24
- name: "priority",
25
- type: "string",
26
- enum: ["normal", "high"],
27
- },
28
- { name: "owner", type: "string", readonly: true },
29
- ],
30
- getValues: () => ({}),
31
- setValues: (values) => applied.push(values),
32
- });
33
- const invoke = createFormFillerInvoker(registry);
34
-
35
- const allowedContext = { allowedFormIds: ["lead-form"] };
36
- assert.deepEqual(
37
- await invoke(
38
- {
39
- form: "lead-form",
40
- data: { company: "Acme", priority: "high" },
41
- },
42
- allowedContext
43
- ),
44
- {
45
- status: "success",
46
- content:
47
- 'Filled "Lead form". Please review the values and submit the form manually.',
48
- appliedFields: ["company", "priority"],
49
- skippedFields: [],
50
- }
51
- );
52
- assert.deepEqual(applied, [{ company: "Acme", priority: "high" }]);
53
-
54
- assert.deepEqual(
55
- await invoke(
56
- {
57
- form: "lead-form",
58
- data: { company: 42, owner: "Ada", unknown: true },
59
- },
60
- allowedContext
61
- ),
62
- {
63
- status: "error",
64
- content: 'No valid editable fields were provided for "Lead form".',
65
- appliedFields: [],
66
- skippedFields: [
67
- {
68
- name: "company",
69
- reason: "invalid",
70
- message: "Expected a string.",
71
- },
72
- {
73
- name: "owner",
74
- reason: "readonly",
75
- message: "This field is read-only.",
76
- },
77
- {
78
- name: "unknown",
79
- reason: "undeclared",
80
- message: "This field is not declared by the target form.",
81
- },
82
- ],
83
- }
84
- );
85
-
86
- assert.throws(
87
- () =>
88
- registry.register({
89
- id: "lead-form",
90
- title: "Duplicate lead form",
91
- fields: [],
92
- getValues: () => ({}),
93
- setValues: () => undefined,
94
- }),
95
- /already registered/
96
- );
97
-
98
- unregister();
99
- assert.deepEqual(
100
- await invoke(
101
- { form: "lead-form", data: { company: "Acme" } },
102
- allowedContext
103
- ),
104
- {
105
- status: "error",
106
- content: 'The target form "lead-form" is not available on this page.',
107
- appliedFields: [],
108
- skippedFields: [],
109
- }
110
- );
111
-
112
- assert.deepEqual(
113
- await invoke(
114
- { form: "lead-form", data: { company: "Acme" } },
115
- { allowedFormIds: [] }
116
- ),
117
- {
118
- status: "error",
119
- content:
120
- 'The target form "lead-form" is not available in this conversation context.',
121
- appliedFields: [],
122
- skippedFields: [],
123
- }
124
- );
125
-
126
- console.log("AI Form filler regression tests passed");
127
- } finally {
128
- await server.close();
129
- }
@@ -1,128 +0,0 @@
1
- import assert from "node:assert/strict";
2
- import { createServer } from "vite";
3
-
4
- const server = await createServer({
5
- appType: "custom",
6
- logLevel: "silent",
7
- server: { middlewareMode: true },
8
- });
9
-
10
- try {
11
- const { AIFrontendToolRegistry, createFrontendToolInvokers } =
12
- await server.ssrLoadModule(
13
- "/registry/nocobase-ai/providers/frontend-tool-registry.tsx"
14
- );
15
-
16
- const registry = new AIFrontendToolRegistry();
17
- const calls = [];
18
- const unregister = registry.register("quote-card", {
19
- name: "update_quote_discount",
20
- description: "Update the quote discount preview.",
21
- permission: "ASK",
22
- inputSchema: {
23
- type: "object",
24
- properties: { discountPercent: { type: "number" } },
25
- },
26
- execute: (args) => {
27
- calls.push(args);
28
- return { updated: true };
29
- },
30
- });
31
- const [manifest] = registry.list("quote-card");
32
- assert.deepEqual(manifest, {
33
- id: "quote-card:update_quote_discount",
34
- blockUid: "quote-card",
35
- name: "update_quote_discount",
36
- title: undefined,
37
- description: "Update the quote discount preview.",
38
- permission: "ASK",
39
- inputSchema: {
40
- type: "object",
41
- properties: { discountPercent: { type: "number" } },
42
- },
43
- });
44
-
45
- const invokers = createFrontendToolInvokers(registry);
46
- const allowedContext = { allowedFrontendToolIds: [manifest.id] };
47
- assert.deepEqual(
48
- await invokers.loadFrontendTool({ toolId: manifest.id }, allowedContext),
49
- manifest
50
- );
51
- assert.deepEqual(
52
- await invokers.executeFrontendTool(
53
- { toolId: manifest.id, args: { discountPercent: 12 } },
54
- allowedContext
55
- ),
56
- { updated: true }
57
- );
58
- assert.deepEqual(calls, [{ discountPercent: 12 }]);
59
-
60
- assert.throws(
61
- () =>
62
- registry.register("quote-card", {
63
- name: "update_quote_discount",
64
- description: "Duplicate Tool",
65
- execute: () => undefined,
66
- }),
67
- /already registered/
68
- );
69
- assert.throws(
70
- () =>
71
- registry.register("quote-card", {
72
- name: "invalid_schema",
73
- description: "Invalid schema",
74
- inputSchema: [],
75
- execute: () => undefined,
76
- }),
77
- /inputSchema must be an object/
78
- );
79
-
80
- const unregisterInvalidResult = registry.register("quote-card", {
81
- name: "invalid_result",
82
- description: "Return an invalid result.",
83
- execute: () => ({ value: 1n }),
84
- });
85
- assert.deepEqual(
86
- await invokers.executeFrontendTool(
87
- { toolId: "quote-card:invalid_result", args: {} },
88
- {
89
- allowedFrontendToolIds: ["quote-card:invalid_result"],
90
- }
91
- ),
92
- {
93
- status: "error",
94
- content:
95
- 'Frontend Tool "quote-card:invalid_result" returned a non-serializable result',
96
- }
97
- );
98
- unregisterInvalidResult();
99
-
100
- unregister();
101
- assert.deepEqual(
102
- await invokers.executeFrontendTool(
103
- { toolId: manifest.id, args: {} },
104
- allowedContext
105
- ),
106
- {
107
- status: "error",
108
- content:
109
- 'Frontend Tool "quote-card:update_quote_discount" is unavailable',
110
- }
111
- );
112
-
113
- assert.deepEqual(
114
- await invokers.executeFrontendTool(
115
- { toolId: manifest.id, args: {} },
116
- { allowedFrontendToolIds: [] }
117
- ),
118
- {
119
- status: "error",
120
- content:
121
- 'Frontend Tool "quote-card:update_quote_discount" is not available in this conversation context',
122
- }
123
- );
124
-
125
- console.log("AI frontend Tool regression tests passed");
126
- } finally {
127
- await server.close();
128
- }
@@ -1,48 +0,0 @@
1
- import assert from "node:assert/strict";
2
- import { createServer } from "vite";
3
-
4
- const server = await createServer({
5
- appType: "custom",
6
- logLevel: "silent",
7
- server: { middlewareMode: true },
8
- });
9
-
10
- try {
11
- const {
12
- createAIPageContextReference,
13
- getAIWorkContextRequiredTools,
14
- mergeAIRequiredTools,
15
- } = await server.ssrLoadModule(
16
- "/registry/nocobase-ai/providers/page-context.tsx"
17
- );
18
-
19
- const formContext = createAIPageContextReference({
20
- id: "lead-form",
21
- title: "Lead form",
22
- kind: "form",
23
- });
24
- assert.deepEqual(formContext, {
25
- type: "page-element",
26
- id: "lead-form",
27
- title: "Lead form",
28
- kind: "form",
29
- });
30
- assert.deepEqual(getAIWorkContextRequiredTools([formContext]), [
31
- "formFiller",
32
- ]);
33
- assert.deepEqual(
34
- mergeAIRequiredTools(
35
- { skills: ["lead-review"], tools: ["inspect-record"] },
36
- ["formFiller", "inspect-record"]
37
- ),
38
- {
39
- skills: ["lead-review"],
40
- tools: ["inspect-record", "formFiller"],
41
- }
42
- );
43
- assert.equal(mergeAIRequiredTools(undefined, []), undefined);
44
-
45
- console.log("AI page context regression tests passed");
46
- } finally {
47
- await server.close();
48
- }
@@ -1,207 +0,0 @@
1
- import assert from "node:assert/strict";
2
- import { createServer } from "vite";
3
-
4
- const wait = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
5
-
6
- const encodeSSE = (events) => {
7
- const encoder = new TextEncoder();
8
- return new ReadableStream({
9
- start(controller) {
10
- controller.enqueue(
11
- encoder.encode(
12
- events.map((event) => `data: ${JSON.stringify(event)}\n\n`).join("")
13
- )
14
- );
15
- controller.close();
16
- },
17
- });
18
- };
19
-
20
- const collectStream = async (stream) => {
21
- const result = [];
22
- for await (const chunk of stream) result.push(chunk);
23
- return result;
24
- };
25
-
26
- const server = await createServer({
27
- appType: "custom",
28
- logLevel: "silent",
29
- server: { middlewareMode: true },
30
- });
31
-
32
- try {
33
- const { StreamCoalescer } = await server.ssrLoadModule(
34
- "/registry/nocobase-ai/providers/stream-coalescer.ts"
35
- );
36
- const { NocoBaseChatTransport } = await server.ssrLoadModule(
37
- "/registry/nocobase-ai/providers/chat-transport.ts"
38
- );
39
- const { SubAgentStreamAccumulator } = await server.ssrLoadModule(
40
- "/registry/nocobase-ai/providers/sub-agent-stream.ts"
41
- );
42
- const { NocoBaseClient } = await server.ssrLoadModule(
43
- "/src/lib/nocobase/client.ts"
44
- );
45
-
46
- {
47
- const client = new NocoBaseClient("http://localhost:13001/api");
48
- assert.equal(
49
- client.resolveUrl("/files/main/main/aiFiles/5.png?preview=1"),
50
- "http://localhost:13001/files/main/main/aiFiles/5.png?preview=1"
51
- );
52
- assert.equal(
53
- client.resolveUrl("https://cdn.example.com/file.png"),
54
- "https://cdn.example.com/file.png"
55
- );
56
- }
57
-
58
- {
59
- const flushed = [];
60
- const coalescer = new StreamCoalescer({
61
- interval: 20,
62
- maxSize: 100,
63
- getSize: (value) => value.length,
64
- merge: (current, incoming) => current + incoming,
65
- onFlush: (key, value) => flushed.push([key, value]),
66
- });
67
- coalescer.push("message", "hello ");
68
- coalescer.push("message", "world");
69
- await wait(35);
70
- assert.deepEqual(flushed, [["message", "hello world"]]);
71
- }
72
-
73
- {
74
- const seedMessage = {
75
- id: "assistant-existing",
76
- role: "assistant",
77
- parts: [
78
- {
79
- type: "data-subAgent",
80
- id: "sub-1",
81
- data: {
82
- sessionId: "sub-1",
83
- username: "viz",
84
- status: "pending",
85
- messages: [
86
- {
87
- id: "sub-message",
88
- role: "assistant",
89
- parts: [{ type: "text", text: "saved ", state: "done" }],
90
- },
91
- ],
92
- },
93
- },
94
- ],
95
- };
96
- const accumulator = new SubAgentStreamAccumulator(seedMessage);
97
- const [chunk] = accumulator.process({
98
- type: "content",
99
- body: "tail",
100
- from: "sub-agent",
101
- sessionId: "sub-1",
102
- username: "viz",
103
- });
104
- assert.equal(chunk.data.messages.length, 1);
105
- assert.equal(chunk.data.messages[0].parts[0].text, "saved tail");
106
- }
107
-
108
- {
109
- const toolCallId = "report-tool";
110
- const events = [
111
- ...Array.from({ length: 100 }, (_, index) => ({
112
- type: "tool_call_chunks",
113
- from: "main-agent",
114
- sessionId: "conversation-1",
115
- body: [
116
- index === 0
117
- ? {
118
- id: toolCallId,
119
- name: "businessReportGenerator",
120
- args: "{",
121
- }
122
- : { id: toolCallId, args: "x" },
123
- ],
124
- })),
125
- { type: "stream_end", from: "main-agent", sessionId: "conversation-1" },
126
- ];
127
- const service = {
128
- resumeConversationStream: async () => encodeSSE(events),
129
- };
130
- const transport = new NocoBaseChatTransport({
131
- service,
132
- getContext: () => ({
133
- sessionId: "conversation-1",
134
- employee: { username: "atlas", nickname: "Atlas" },
135
- model: { value: "test", label: "Test" },
136
- }),
137
- });
138
- transport.prepareConversationResume([
139
- { id: "assistant-existing", role: "assistant", parts: [] },
140
- ]);
141
- const chunks = await collectStream(await transport.reconnectToStream());
142
- const start = chunks.find((chunk) => chunk.type === "start");
143
- const toolDeltas = chunks.filter(
144
- (chunk) => chunk.type === "tool-input-delta"
145
- );
146
- assert.equal(start.messageId, undefined);
147
- assert.ok(toolDeltas.length < 10, `received ${toolDeltas.length} deltas`);
148
- assert.equal(
149
- toolDeltas.reduce((size, chunk) => size + chunk.inputTextDelta.length, 0),
150
- 100
151
- );
152
- }
153
-
154
- {
155
- let request;
156
- const service = {
157
- sendMessagesStream: async (input) => {
158
- request = input;
159
- return encodeSSE([
160
- { type: "content", body: "ok", from: "main-agent" },
161
- { type: "stream_end", from: "main-agent" },
162
- ]);
163
- },
164
- };
165
- const transport = new NocoBaseChatTransport({
166
- service,
167
- getContext: () => ({
168
- sessionId: "conversation-context",
169
- employee: { username: "mira", nickname: "Mira" },
170
- model: { value: "test", label: "Test" },
171
- }),
172
- });
173
- await collectStream(
174
- await transport.sendMessages({
175
- messages: [
176
- {
177
- id: "user-context",
178
- role: "user",
179
- metadata: {
180
- workContext: [
181
- {
182
- type: "page-element",
183
- id: "customer-form",
184
- title: "Customer form",
185
- content: { values: { name: "Northwind" } },
186
- },
187
- ],
188
- },
189
- parts: [{ type: "text", text: "Review this form" }],
190
- },
191
- ],
192
- })
193
- );
194
- assert.deepEqual(request.messages[0].workContext, [
195
- {
196
- type: "page-element",
197
- id: "customer-form",
198
- title: "Customer form",
199
- content: { values: { name: "Northwind" } },
200
- },
201
- ]);
202
- }
203
-
204
- console.log("AI stream regression tests passed");
205
- } finally {
206
- await server.close();
207
- }
@@ -1,6 +0,0 @@
1
- # NocoBase CAS authentication
2
-
3
- Adds the CAS sign-in button, callback error handling, and `useCasSignIn` for
4
- application-owned login UI.
5
-
6
- Installing the Registry also adds a CAS component Demo under Authentication.
@@ -1,41 +0,0 @@
1
- import { LogIn } from "lucide-react";
2
-
3
- import type { AuthenticatorComponentProps } from "@/components/auth/types";
4
- import {
5
- Alert,
6
- AlertDescription,
7
- AlertTitle,
8
- } from "@/components/ui/alert";
9
- import { Button } from "@/components/ui/button";
10
- import { resolveTranslatableText } from "@/lib/i18n";
11
-
12
- import { useCasSignIn } from "./use-cas-sign-in";
13
-
14
- export default function CasSignInButton({
15
- authenticator,
16
- onSignIn,
17
- }: AuthenticatorComponentProps & { onSignIn?: () => void }) {
18
- const { signIn, error } = useCasSignIn(authenticator);
19
-
20
- return (
21
- <div className="space-y-3">
22
- {error && (
23
- <Alert variant="destructive">
24
- <AlertTitle>CAS sign-in failed</AlertTitle>
25
- <AlertDescription>{error.message}</AlertDescription>
26
- </Alert>
27
- )}
28
- <Button
29
- type="button"
30
- variant="outline"
31
- className="w-full"
32
- onClick={onSignIn ?? signIn}
33
- >
34
- <LogIn />
35
- {resolveTranslatableText(
36
- authenticator.title || authenticator.authTypeTitle || "CAS"
37
- )}
38
- </Button>
39
- </div>
40
- );
41
- }
@@ -1,20 +0,0 @@
1
- import { AuthMethodDemo } from "@/components/auth/demo";
2
- import CasSignInButton from "./cas-sign-in-button";
3
-
4
- const authenticator = {
5
- name: "company-cas",
6
- authType: "CAS",
7
- title: "Company CAS",
8
- };
9
-
10
- export function CasAuthDemoPage() {
11
- return (
12
- <AuthMethodDemo
13
- authType="CAS"
14
- methodName="Central Authentication Service"
15
- description="Redirect users to a CAS server and capture the returned NocoBase token without coupling the page UI to CAS."
16
- >
17
- <CasSignInButton authenticator={authenticator} onSignIn={() => {}} />
18
- </AuthMethodDemo>
19
- );
20
- }
@@ -1,46 +0,0 @@
1
- import { lazy } from "react";
2
- import { KeyRound } from "lucide-react";
3
- import { Route } from "react-router";
4
-
5
- import type { AppExtension } from "@/app/extension";
6
- import { AuthDemoRoute } from "@/components/auth/demo";
7
-
8
- const CasSignInButton = lazy(() => import("./cas-sign-in-button"));
9
- const CasAuthDemoPage = lazy(() =>
10
- import("./demo").then((module) => ({ default: module.CasAuthDemoPage }))
11
- );
12
-
13
- const casAuthExtension: AppExtension = {
14
- id: "nocobase-auth-cas",
15
- resources: [
16
- {
17
- name: "auth-cas-demo",
18
- list: "/auth/cas",
19
- meta: {
20
- parent: "auth-components",
21
- label: "CAS",
22
- icon: <KeyRound />,
23
- acl: { type: "authenticated" },
24
- },
25
- },
26
- ],
27
- routes: (
28
- <Route
29
- path="/auth/cas"
30
- element={
31
- <AuthDemoRoute>
32
- <CasAuthDemoPage />
33
- </AuthDemoRoute>
34
- }
35
- />
36
- ),
37
- authAdapters: [
38
- {
39
- authType: "CAS",
40
- placement: "button",
41
- Component: CasSignInButton,
42
- },
43
- ],
44
- };
45
-
46
- export default casAuthExtension;
@@ -1 +0,0 @@
1
- export { useCasSignIn } from "./use-cas-sign-in";
@@ -1,29 +0,0 @@
1
- import { useCallback, useMemo } from "react";
2
- import { useSearchParams } from "react-router";
3
-
4
- import type { Authenticator } from "@/components/auth/types";
5
- import { nocobaseClient } from "@/lib/nocobase/client";
6
-
7
- export function useCasSignIn(authenticator: Authenticator) {
8
- const [searchParams] = useSearchParams();
9
- const callbackError = useMemo(() => {
10
- if (searchParams.get("authenticator") !== authenticator.name) {
11
- return undefined;
12
- }
13
- const message = searchParams.get("error");
14
- return message ? new Error(message) : undefined;
15
- }, [authenticator.name, searchParams]);
16
-
17
- const signIn = useCallback(() => {
18
- const redirect =
19
- searchParams.get("to") || searchParams.get("redirect") || "/";
20
- const url = nocobaseClient.buildUrl("cas:login", {
21
- authenticator: authenticator.name,
22
- __appName: "main",
23
- redirect,
24
- });
25
- window.location.replace(url.toString());
26
- }, [authenticator.name, searchParams]);
27
-
28
- return { signIn, error: callbackError };
29
- }
@@ -1,7 +0,0 @@
1
- # NocoBase DingTalk authentication
2
-
3
- Adds DingTalk OAuth sign-in, callback errors, OAuth or micro-app automatic
4
- login, and `useDingtalkSignIn` for custom UI. The DingTalk JSAPI is loaded only
5
- inside a DingTalk browser during internal automatic login.
6
-
7
- Installing the Registry also adds a DingTalk component Demo under Authentication.