openxiangda 1.0.19 → 1.0.21

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.
@@ -74,6 +74,8 @@ When the user provides a root domain such as `https://yida.wisejob.cn/`, use it
74
74
  - Before publishing to another platform, verify the workspace is bound for that profile. Resource IDs from one profile must not be reused for another profile.
75
75
  - Use `openxiangda app snapshot <APP_XXX> --profile <name> --json` for diagnosis before changing an existing app.
76
76
  - Run write commands that update `.openxiangda/state.json` sequentially within the same workspace. Read-only commands can run in parallel.
77
+ - JS_CODE is backend-executed workflow/automation logic, not frontend page code. Use it when logic must run after a backend trigger such as fixed cron schedules, form date-field schedules, form submit/update/delete/field-change events, or workflow approval/process events.
78
+ - Use JS_CODE for cross-form data queries, create/update/batch update operations, process termination, platform API calls, external HTTP calls, and complex orchestration that the frontend cannot handle reliably. Do not use it for simple UI interactions, ordinary form validation, or display-only page behavior.
77
79
  - For workflow/automation JS_CODE nodes, prefer V2 `runtimeMode: "trusted_node"`. AI-authored source must be TypeScript under `sy-lowcode-app-workspace/src/js-code-nodes/<scriptCode>/index.ts`. `pnpm build-js-code --script <scriptCode>` runs TypeScript validation before bundling, and `sourceFile.localPath` should point to the TS source; the CLI builds, uploads, and replaces it with snapshot metadata during validate/create.
78
80
 
79
81
  ## Subskills
@@ -7,6 +7,8 @@ Automations have two files:
7
7
 
8
8
  ## Trigger Config
9
9
 
10
+ Automation can run from form events, workflow events, fixed cron schedules, or a form date field reaching a configured time. Pair these triggers with JS_CODE when the action must execute on the backend and cannot be handled reliably by a frontend page.
11
+
10
12
  Form submit trigger:
11
13
 
12
14
  ```json
@@ -96,7 +98,7 @@ Supported node types:
96
98
 
97
99
  ## JS_CODE V2
98
100
 
99
- Use trusted Node JS_CODE nodes for AI/admin automation logic:
101
+ Use trusted Node JS_CODE nodes for AI/admin automation logic that runs after an automation trigger. Typical cases include scheduled data cleanup, date-field reminders, cross-form synchronization after submit/update/delete, workflow-completed follow-up writes, calling internal platform APIs, calling external HTTP services, and other backend-only orchestration.
100
102
 
101
103
  ```json
102
104
  {
@@ -117,7 +119,45 @@ Use trusted Node JS_CODE nodes for AI/admin automation logic:
117
119
 
118
120
  Author source in `sy-lowcode-app-workspace/src/js-code-nodes/<scriptCode>/index.ts`. AI-authored source must be TypeScript. Build with `pnpm build-js-code --script <scriptCode>`; the command runs TypeScript validation before bundling. During validate/create, the CLI uploads the generated bundle, replaces `sourceFile.localPath` with `{ bucketName, objectName, sha256, ... }`, and the backend verifies sha256 before execution.
119
121
 
120
- Scripts may use `module.exports = async (ctx) => {}`, `require`, `process`, `Buffer`, legacy `methods.*`, arbitrary HTTP, and `platform.api` for `/openxiangda-api/v1`.
122
+ The backend runs the snapshot in the trusted Node runtime, applies the node timeout (`30000` ms by default), stores execution logs, and writes the returned value to the node output and `variables.node_<nodeId>`. Scripts may use `export default async function (ctx) {}`, `module.exports = async (ctx) => {}`, `require`, `process`, `Buffer`, arbitrary HTTP, and `platform.api` for `/openxiangda-api/v1`.
123
+
124
+ Runtime context includes `ctx.triggerEvent`, `ctx.formData`, `ctx.workflowData`, `ctx.operator`, `ctx.app`, `ctx.variables`, and `ctx.node`. Data/process bridge methods include `ctx.methods.queryOneData`, `queryManyData`, `updateOneData`, `updateDataByFormInstanceId`, `updateManyData`, `createOneData`, `terminateProcess`, and `getAllParentDepartments`.
125
+
126
+ Example `src/js-code-nodes/scheduled_reconcile/index.ts`:
127
+
128
+ ```ts
129
+ export default async function scheduledReconcile(ctx) {
130
+ const appType = ctx.app.appType;
131
+ const current = ctx.formData?.current || {};
132
+ const formInstanceId =
133
+ ctx.triggerEvent?.data?.formInstanceId ||
134
+ current.formInstanceId ||
135
+ current.formInstId;
136
+
137
+ const pendingRows = await ctx.methods.queryManyData(appType, "FORM_ORDER", {
138
+ status: "pending",
139
+ });
140
+
141
+ if (formInstanceId) {
142
+ await ctx.methods.updateDataByFormInstanceId(
143
+ appType,
144
+ "FORM_ORDER",
145
+ formInstanceId,
146
+ { last_checked_at: new Date().toISOString() }
147
+ );
148
+ }
149
+
150
+ const log = await ctx.methods.createOneData(appType, "FORM_JOB_LOG", {
151
+ trigger_type: ctx.triggerEvent?.type || "scheduled",
152
+ processed_count: Array.isArray(pendingRows) ? pendingRows.length : 0,
153
+ });
154
+
155
+ return {
156
+ processedCount: pendingRows?.length || 0,
157
+ logId: log?.formInstanceId || log?.id,
158
+ };
159
+ }
160
+ ```
121
161
 
122
162
  Validation rules:
123
163
 
@@ -0,0 +1,66 @@
1
+ # App Shell Code Page Entries
2
+
3
+ Use this pattern for formal user-facing entries such as admin consoles, PC
4
+ portals, and mobile portals.
5
+
6
+ ## Required Page Config
7
+
8
+ `page.config.ts` must declare app-shell entry metadata:
9
+
10
+ ```ts
11
+ export default {
12
+ code: "instrument_admin",
13
+ name: "Instrument Admin",
14
+ route: { pathKey: "instrument_admin" },
15
+ entry: {
16
+ mode: "app-shell",
17
+ hidePlatformNav: true,
18
+ defaultRoute: "dashboard",
19
+ },
20
+ };
21
+ ```
22
+
23
+ Rules:
24
+
25
+ - `entry.mode` is `"app-shell"` for formal product entries.
26
+ - `entry.hidePlatformNav` is `true` unless the page is explicitly a debugging or maintenance page.
27
+ - `entry.defaultRoute` is required and must point at the shell home route.
28
+ - Plain one-off custom pages may omit `entry` or use `mode: "plain-page"`.
29
+
30
+ ## Expected File Shape
31
+
32
+ Prefer this layout for app-shell pages:
33
+
34
+ ```text
35
+ src/pages/<pageCode>/
36
+ page.config.ts
37
+ index.tsx
38
+ <Feature>Shell.tsx
39
+ routes.ts
40
+ modules/
41
+ __tests__/
42
+ ```
43
+
44
+ `routes.ts` should export the route registry, default route, route parser, and
45
+ route builder. Page components should navigate through that helper or
46
+ `openxiangda/runtime` navigation.
47
+
48
+ ## Navigation Rules
49
+
50
+ - Use the standard query key `route` for shell routes.
51
+ - Use `navigation.pushRoute(route, query?)` and
52
+ `navigation.replaceRoute(route, query?)` for same-page shell navigation.
53
+ - Do not scatter hardcoded `/view/...&isRenderNav=false` URLs through page code.
54
+ - Menu links should bind the formal app-shell code page only. Native forms,
55
+ workflows, and view pages can stay as development resources or permission
56
+ targets, but should not become the product navigation shell.
57
+
58
+ ## Tests
59
+
60
+ Add focused contract tests for:
61
+
62
+ - `page.config.ts` includes `entry.mode: "app-shell"`.
63
+ - default route fallback.
64
+ - unknown route fallback.
65
+ - preserving and deleting business query values.
66
+ - preserving hidden platform nav when the route helper updates the URL.
@@ -52,6 +52,8 @@ Supported node types:
52
52
 
53
53
  ## JS_CODE V2
54
54
 
55
+ Workflow JS_CODE runs on the backend when the process reaches the `js_code` node. Use it before or after approval nodes, in branches, or before ending a process when the workflow needs server-side side effects such as cross-form synchronization, audit record creation, external API calls, or terminating a related process. Do not use it for frontend-only behavior.
56
+
55
57
  Inline:
56
58
 
57
59
  ```json
@@ -87,7 +89,43 @@ File snapshot:
87
89
  }
88
90
  ```
89
91
 
90
- AI-authored JS_CODE source must be TypeScript under `sy-lowcode-app-workspace/src/js-code-nodes/<scriptCode>/index.ts`. When validating or creating, the CLI runs `pnpm build-js-code --script <scriptCode>`, which runs TypeScript validation first, bundles to `dist/js-code-nodes/<scriptCode>/index.cjs`, uploads the bundle, and replaces `sourceFile.localPath` with immutable snapshot metadata. Scripts can export `export default async function (ctx) {}` or `module.exports = async (ctx) => {}` and call `ctx.platform.api` or global `platform.api`; the default API namespace is `/openxiangda-api/v1`.
92
+ AI-authored JS_CODE source must be TypeScript under `sy-lowcode-app-workspace/src/js-code-nodes/<scriptCode>/index.ts`. When validating or creating, the CLI runs `pnpm build-js-code --script <scriptCode>`, which runs TypeScript validation first, bundles to `dist/js-code-nodes/<scriptCode>/index.cjs`, uploads the bundle, and replaces `sourceFile.localPath` with immutable snapshot metadata. The backend verifies snapshot `sha256`, runs it in the trusted Node runtime, applies the node timeout (`30000` ms by default), stores execution logs, and writes the returned value to the node output and `variables.node_<nodeId>`.
93
+
94
+ Scripts can export `export default async function (ctx) {}` or `module.exports = async (ctx) => {}`. The runtime exposes `ctx.triggerEvent`, `ctx.formData`, `ctx.workflowData`, `ctx.operator`, `ctx.app`, `ctx.variables`, `ctx.methods.*`, `ctx.platform.api.*`, `ctx.utils`, `require`, `process`, and `Buffer`. Use `ctx.methods.queryOneData/queryManyData/updateOneData/updateDataByFormInstanceId/updateManyData/createOneData/terminateProcess/getAllParentDepartments` for platform-side data and process operations.
95
+
96
+ Example `src/js-code-nodes/sync_customer/index.ts`:
97
+
98
+ ```ts
99
+ export default async function syncCustomer(ctx) {
100
+ const current = ctx.formData?.current || {};
101
+ const appType = ctx.app.appType;
102
+ const customerNo = current.customer_no;
103
+
104
+ const customer = await ctx.methods.queryOneData(appType, "FORM_CUSTOMER", {
105
+ customer_no: customerNo,
106
+ });
107
+
108
+ if (customer) {
109
+ await ctx.methods.updateOneData(
110
+ appType,
111
+ "FORM_CUSTOMER",
112
+ { customer_no: customerNo },
113
+ { last_approved_at: new Date().toISOString() }
114
+ );
115
+ }
116
+
117
+ const log = await ctx.methods.createOneData(appType, "FORM_SYNC_LOG", {
118
+ source: "workflow",
119
+ customer_no: customerNo,
120
+ result: customer ? "updated" : "missing",
121
+ });
122
+
123
+ return {
124
+ customerFound: Boolean(customer),
125
+ logId: log?.formInstanceId || log?.id,
126
+ };
127
+ }
128
+ ```
91
129
 
92
130
  Field permission config lives in `flowConfig` by node ID:
93
131
 
@@ -40,6 +40,7 @@ openxiangda page publish dashboard \
40
40
  Read these references only when editing page code:
41
41
 
42
42
  - `../../references/pages/workspace-structure.md`
43
+ - `../../references/pages/app-shell.md` — formal backend / PC portal / mobile portal entry pattern. Read before creating any user-facing main entry or admin console.
43
44
  - `../../references/pages/page-sdk.md`
44
45
  - `../../references/pages/publish-flow.md`
45
46
  - `../../references/style-system.md` — three-layer style architecture, CSS namespace, and the no-hardcoded-color rule. Read before writing any page CSS or Tailwind class.
@@ -50,11 +51,14 @@ Read these references only when editing page code:
50
51
  ## Rules
51
52
 
52
53
  - Keep `pageCode` stable and use it as the local logical key.
54
+ - Formal user-facing entries such as admin consoles, PC portals, and mobile portals must be app-shell code pages. Declare `entry: { mode: "app-shell", hidePlatformNav: true, defaultRoute: "<home-route>" }` in `page.config.ts`.
53
55
  - Store live `pageId`, `routeKey`, and `legacyFormUuid` under the current profile only.
54
56
  - Use `openxiangda/runtime` for platform data access instead of hardcoding backend URLs in page code.
55
57
  - Named imports from `@ant-design/icons` are supported by the `openxiangda` workspace build proxy, which enumerates icon module exports at runtime.
56
58
  - Publish through `openxiangda workspace publish --profile <name>` unless there is a specific repair reason to call `page publish` directly.
57
59
  - Do not create custom code pages by writing platform schema directly. The source is React workspace code plus `page.config.ts`.
60
+ - Do not scatter hardcoded `/view/...&isRenderNav=false` URLs through page code. Use the runtime navigation API or the local route helper generated for the app shell.
61
+ - Platform menus should bind only the formal app-shell code page for user-facing entry points. Original forms, workflows, and native view pages may remain as development / maintenance resources or permission targets, but should not become the product navigation shell.
58
62
  - For prod, explicitly run with `--profile prod`; never rely on the current profile for release operations.
59
63
  - Follow the style system in `../../references/style-system.md`: never hardcode colors, fonts, spacing, or radii — use the platform CSS variables and the page CSS namespace.
60
64
  - For list / detail / CRUD pages, follow `../../references/architecture-patterns.md` (e.g. `DataManagementList`) before writing custom data-fetching loops.
@@ -44,7 +44,9 @@ Use `workflow pull` to inspect the live definition. Use logical workflow codes l
44
44
 
45
45
  ## JS_CODE V2
46
46
 
47
- For non-trivial logic, prefer JS_CODE V2 trusted Node scripts over large inline snippets. AI-authored JS_CODE source must be TypeScript:
47
+ JS_CODE is the backend execution escape hatch for workflow and automation. Use it when the logic must run on the server after a backend trigger, such as a fixed cron schedule, a form date-field schedule, a form submit/update/delete/field-change event, or a workflow approval/process event. It is appropriate for cross-form data queries, create/update/batch update operations, process termination, platform API calls, external HTTP calls, and complex orchestration that the frontend cannot handle reliably.
48
+
49
+ Do not use JS_CODE for simple UI interactions, ordinary form validation, display-only page behavior, or logic that belongs in a normal React code page. For non-trivial backend logic, prefer JS_CODE V2 trusted Node scripts over large inline snippets. AI-authored JS_CODE source must be TypeScript:
48
50
 
49
51
  1. Put source in `sy-lowcode-app-workspace/src/js-code-nodes/<scriptCode>/index.ts`.
50
52
  2. Run `pnpm build-js-code --script <scriptCode>`. This command runs TypeScript validation first and only bundles after `tsc` passes.
@@ -68,7 +70,14 @@ For non-trivial logic, prefer JS_CODE V2 trusted Node scripts over large inline
68
70
 
69
71
  The CLI requires `sourceFile.localPath` to point to `src/js-code-nodes/<scriptCode>/index.ts`. During validate/create it runs `pnpm build-js-code --script <scriptCode>`, uploads the generated `dist/js-code-nodes/<scriptCode>/index.cjs` to `/file/js-code-snapshot/upload`, verifies the server snapshot metadata, and replaces it with `{ bucketName, objectName, sha256, ... }`.
70
72
 
71
- Inside the TypeScript script, prefer `export default async function (ctx) {}` or `module.exports = async (ctx) => {}`. The runtime exposes Node `require`, `process`, `Buffer`, installed dependencies, arbitrary HTTP, legacy `methods.*`, and `platform.api` for `/openxiangda-api/v1`.
73
+ The backend verifies the uploaded snapshot sha256 before execution, runs it in the trusted Node runtime, applies the node timeout (`30000` ms by default), stores console/runtime logs in the execution record, and writes the returned value to the node output and `variables.node_<nodeId>`.
74
+
75
+ Inside the TypeScript script, prefer `export default async function (ctx) {}` or `module.exports = async (ctx) => {}`. The runtime exposes:
76
+
77
+ - Context: `ctx.triggerEvent`, `ctx.formData`, `ctx.workflowData`, `ctx.operator`, `ctx.app`, `ctx.variables`, and `ctx.node`.
78
+ - Data/process methods: `ctx.methods.queryOneData`, `queryManyData`, `updateOneData`, `updateDataByFormInstanceId`, `updateManyData`, `createOneData`, `terminateProcess`, and `getAllParentDepartments`.
79
+ - Platform API bridge: `ctx.platform.api.get/post/put/patch/delete/request` for `/openxiangda-api/v1`.
80
+ - Node runtime helpers: `require`, `process`, `Buffer`, `ctx.utils`, `ctx.utils.http`, and `ctx.console`.
72
81
 
73
82
  ## Automation Flow
74
83
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openxiangda",
3
- "version": "1.0.19",
3
+ "version": "1.0.21",
4
4
  "description": "OpenXiangda CLI, workspace build tools, runtime SDK, and form components.",
5
5
  "private": false,
6
6
  "bin": {
@@ -127,6 +127,13 @@ interface PageDataSourceDescriptor {
127
127
  defaultFilter?: SearchExpression | string;
128
128
  [key: string]: unknown;
129
129
  }
130
+ type CustomPageEntryMode = "app-shell" | "plain-page";
131
+ interface CustomPageEntryConfig {
132
+ mode?: CustomPageEntryMode | string;
133
+ hidePlatformNav?: boolean;
134
+ defaultRoute?: string;
135
+ [key: string]: unknown;
136
+ }
130
137
  interface PageInfo {
131
138
  id: string;
132
139
  code: string;
@@ -138,6 +145,7 @@ interface PageInfo {
138
145
  status: string;
139
146
  props: Record<string, unknown>;
140
147
  route: Record<string, unknown> | object;
148
+ entry?: CustomPageEntryConfig;
141
149
  dataSources: PageDataSourceDescriptor[];
142
150
  capabilities: Record<string, unknown>;
143
151
  version?: string;
@@ -164,6 +172,8 @@ interface PageNavigationApi {
164
172
  */
165
173
  pushPage(pageKey: string, query?: Record<string, unknown>): void;
166
174
  replacePage(pageKey: string, query?: Record<string, unknown>): void;
175
+ pushRoute(route: string, query?: Record<string, unknown>): void;
176
+ replaceRoute(route: string, query?: Record<string, unknown>): void;
167
177
  updateQuery(query: Record<string, unknown>): void;
168
178
  setHash(hash: string): void;
169
179
  back(): void;
@@ -856,4 +866,4 @@ declare const usePageRoute: () => PageRouteInfo;
856
866
 
857
867
  declare const usePageSdk: () => PageSdk;
858
868
 
859
- export { type ApiPermissionListParams, type ApproveTaskParams, type AssignPermissionsParams, type AssignRolesParams, type BatchAddUsersToRoleParams, type BatchSendNotificationByTypeParams, type ChangeUserRoleParams, type ConnectorCallParams, type ConnectorInvokeParams, type ConnectorInvokeResult, type ConnectorRequestBodyType, type ConnectorResponseType, type CreateApiPermissionParams, type CreateFormPermissionGroupDto, type CreatePagePermissionGroupDto, type CreateRoleParams, type CreateUiPermissionParams, type CreateUserParams, type CurrentUserDepartmentParents, type DataManagementConfigParams, type DataManagementFilterState, type DataPermissionConditionDto, type DataPermissionDto, type DataPermissionRuleDto, type FieldPermissionDto, type FormAdvancedSearchParams, type FormChangeRecordParams, type FormCreateParams, type FormExportParams, type FormGetDetailParams, type FormImportParams, type FormPermissionGroup, type FormRemoveParams, type FormSearchParams, type FormUpdateParams, type GetParentDepartmentsOptions, type GetProcessInstanceParams, type GetUserRolesParams, type ImportExportRecordDownloadParams, type ImportExportRecordQuery, type InstanceStatus, type NotificationChannel, type NotificationMessageRecord, type PageApiPermissionRecord, type PageApiResponse, type PageAppInfo, type PageBinaryResponse, type PageBridgeApi, type PageContext, type PageDataManagementConfig, type PageDataSourceDescriptor, type PageDepartmentInfo, type PageDepartmentRecord, type PageHttpMethod, type PageInfo, type PageListResult, type PageMessageApi, type PageModalApi, type PageNavigationApi, type PageOffsetListResult, type PagePermissionGroup, type PagePermissionInfo, PageProvider, type PageQueryValue, type PageRequestOptions, type PageRoleRecord, type PageRouteInfo, type PageScope, type PageSdk, type PageSdkError, type PageSdkMeta, type PageTransportDownloadPayload, type PageTransportRequestPayload, type PageUiPermissionRecord, type PageUiPermissionType, type PageUserInfo, type PageUserRecord, type PageUserType, type ProcessApproveAction, type QueryFormPermissionGroupDto, type QueryPagePermissionGroupDto, type RoleListParams, type RoleUsersParams, type SaveDataManagementConfigParams, type SearchComponentName, type SearchExpression, type SearchFieldKey, type SearchGroup, type SearchLogic, type SearchOperator, type SearchRule, type SearchSortItem, type SearchSystemField, type SendNotificationByTypeParams, type SendNotificationResult, type SubFormRule, type SwitchAppRoleParams, type SwitchPlatformRoleParams, type TerminateProcessInstanceParams, type TriggerCallbackTaskParams, type UiPermissionListParams, type UpdateApiPermissionParams, type UpdateFormPermissionGroupDto, type UpdatePagePermissionGroupDto, type UpdateRoleParams, type UpdateUiPermissionParams, type UpdateUserParams, type UserListParams, type UserMenuPermissionsResponse, type ValidateUserParams, type ViewFieldPermissionValue, type ViewOperationPermission, type ViewPermissionSummary, createPageSdk, createReactPage, useCurrentUser, useDataSource, useFormViewPermissions, useMessage, useModal, useNavigation, usePageContext, usePageProps, usePageRoute, usePageSdk };
869
+ export { type ApiPermissionListParams, type ApproveTaskParams, type AssignPermissionsParams, type AssignRolesParams, type BatchAddUsersToRoleParams, type BatchSendNotificationByTypeParams, type ChangeUserRoleParams, type ConnectorCallParams, type ConnectorInvokeParams, type ConnectorInvokeResult, type ConnectorRequestBodyType, type ConnectorResponseType, type CreateApiPermissionParams, type CreateFormPermissionGroupDto, type CreatePagePermissionGroupDto, type CreateRoleParams, type CreateUiPermissionParams, type CreateUserParams, type CurrentUserDepartmentParents, type CustomPageEntryConfig, type CustomPageEntryMode, type DataManagementConfigParams, type DataManagementFilterState, type DataPermissionConditionDto, type DataPermissionDto, type DataPermissionRuleDto, type FieldPermissionDto, type FormAdvancedSearchParams, type FormChangeRecordParams, type FormCreateParams, type FormExportParams, type FormGetDetailParams, type FormImportParams, type FormPermissionGroup, type FormRemoveParams, type FormSearchParams, type FormUpdateParams, type GetParentDepartmentsOptions, type GetProcessInstanceParams, type GetUserRolesParams, type ImportExportRecordDownloadParams, type ImportExportRecordQuery, type InstanceStatus, type NotificationChannel, type NotificationMessageRecord, type PageApiPermissionRecord, type PageApiResponse, type PageAppInfo, type PageBinaryResponse, type PageBridgeApi, type PageContext, type PageDataManagementConfig, type PageDataSourceDescriptor, type PageDepartmentInfo, type PageDepartmentRecord, type PageHttpMethod, type PageInfo, type PageListResult, type PageMessageApi, type PageModalApi, type PageNavigationApi, type PageOffsetListResult, type PagePermissionGroup, type PagePermissionInfo, PageProvider, type PageQueryValue, type PageRequestOptions, type PageRoleRecord, type PageRouteInfo, type PageScope, type PageSdk, type PageSdkError, type PageSdkMeta, type PageTransportDownloadPayload, type PageTransportRequestPayload, type PageUiPermissionRecord, type PageUiPermissionType, type PageUserInfo, type PageUserRecord, type PageUserType, type ProcessApproveAction, type QueryFormPermissionGroupDto, type QueryPagePermissionGroupDto, type RoleListParams, type RoleUsersParams, type SaveDataManagementConfigParams, type SearchComponentName, type SearchExpression, type SearchFieldKey, type SearchGroup, type SearchLogic, type SearchOperator, type SearchRule, type SearchSortItem, type SearchSystemField, type SendNotificationByTypeParams, type SendNotificationResult, type SubFormRule, type SwitchAppRoleParams, type SwitchPlatformRoleParams, type TerminateProcessInstanceParams, type TriggerCallbackTaskParams, type UiPermissionListParams, type UpdateApiPermissionParams, type UpdateFormPermissionGroupDto, type UpdatePagePermissionGroupDto, type UpdateRoleParams, type UpdateUiPermissionParams, type UpdateUserParams, type UserListParams, type UserMenuPermissionsResponse, type ValidateUserParams, type ViewFieldPermissionValue, type ViewOperationPermission, type ViewPermissionSummary, createPageSdk, createReactPage, useCurrentUser, useDataSource, useFormViewPermissions, useMessage, useModal, useNavigation, usePageContext, usePageProps, usePageRoute, usePageSdk };
@@ -127,6 +127,13 @@ interface PageDataSourceDescriptor {
127
127
  defaultFilter?: SearchExpression | string;
128
128
  [key: string]: unknown;
129
129
  }
130
+ type CustomPageEntryMode = "app-shell" | "plain-page";
131
+ interface CustomPageEntryConfig {
132
+ mode?: CustomPageEntryMode | string;
133
+ hidePlatformNav?: boolean;
134
+ defaultRoute?: string;
135
+ [key: string]: unknown;
136
+ }
130
137
  interface PageInfo {
131
138
  id: string;
132
139
  code: string;
@@ -138,6 +145,7 @@ interface PageInfo {
138
145
  status: string;
139
146
  props: Record<string, unknown>;
140
147
  route: Record<string, unknown> | object;
148
+ entry?: CustomPageEntryConfig;
141
149
  dataSources: PageDataSourceDescriptor[];
142
150
  capabilities: Record<string, unknown>;
143
151
  version?: string;
@@ -164,6 +172,8 @@ interface PageNavigationApi {
164
172
  */
165
173
  pushPage(pageKey: string, query?: Record<string, unknown>): void;
166
174
  replacePage(pageKey: string, query?: Record<string, unknown>): void;
175
+ pushRoute(route: string, query?: Record<string, unknown>): void;
176
+ replaceRoute(route: string, query?: Record<string, unknown>): void;
167
177
  updateQuery(query: Record<string, unknown>): void;
168
178
  setHash(hash: string): void;
169
179
  back(): void;
@@ -856,4 +866,4 @@ declare const usePageRoute: () => PageRouteInfo;
856
866
 
857
867
  declare const usePageSdk: () => PageSdk;
858
868
 
859
- export { type ApiPermissionListParams, type ApproveTaskParams, type AssignPermissionsParams, type AssignRolesParams, type BatchAddUsersToRoleParams, type BatchSendNotificationByTypeParams, type ChangeUserRoleParams, type ConnectorCallParams, type ConnectorInvokeParams, type ConnectorInvokeResult, type ConnectorRequestBodyType, type ConnectorResponseType, type CreateApiPermissionParams, type CreateFormPermissionGroupDto, type CreatePagePermissionGroupDto, type CreateRoleParams, type CreateUiPermissionParams, type CreateUserParams, type CurrentUserDepartmentParents, type DataManagementConfigParams, type DataManagementFilterState, type DataPermissionConditionDto, type DataPermissionDto, type DataPermissionRuleDto, type FieldPermissionDto, type FormAdvancedSearchParams, type FormChangeRecordParams, type FormCreateParams, type FormExportParams, type FormGetDetailParams, type FormImportParams, type FormPermissionGroup, type FormRemoveParams, type FormSearchParams, type FormUpdateParams, type GetParentDepartmentsOptions, type GetProcessInstanceParams, type GetUserRolesParams, type ImportExportRecordDownloadParams, type ImportExportRecordQuery, type InstanceStatus, type NotificationChannel, type NotificationMessageRecord, type PageApiPermissionRecord, type PageApiResponse, type PageAppInfo, type PageBinaryResponse, type PageBridgeApi, type PageContext, type PageDataManagementConfig, type PageDataSourceDescriptor, type PageDepartmentInfo, type PageDepartmentRecord, type PageHttpMethod, type PageInfo, type PageListResult, type PageMessageApi, type PageModalApi, type PageNavigationApi, type PageOffsetListResult, type PagePermissionGroup, type PagePermissionInfo, PageProvider, type PageQueryValue, type PageRequestOptions, type PageRoleRecord, type PageRouteInfo, type PageScope, type PageSdk, type PageSdkError, type PageSdkMeta, type PageTransportDownloadPayload, type PageTransportRequestPayload, type PageUiPermissionRecord, type PageUiPermissionType, type PageUserInfo, type PageUserRecord, type PageUserType, type ProcessApproveAction, type QueryFormPermissionGroupDto, type QueryPagePermissionGroupDto, type RoleListParams, type RoleUsersParams, type SaveDataManagementConfigParams, type SearchComponentName, type SearchExpression, type SearchFieldKey, type SearchGroup, type SearchLogic, type SearchOperator, type SearchRule, type SearchSortItem, type SearchSystemField, type SendNotificationByTypeParams, type SendNotificationResult, type SubFormRule, type SwitchAppRoleParams, type SwitchPlatformRoleParams, type TerminateProcessInstanceParams, type TriggerCallbackTaskParams, type UiPermissionListParams, type UpdateApiPermissionParams, type UpdateFormPermissionGroupDto, type UpdatePagePermissionGroupDto, type UpdateRoleParams, type UpdateUiPermissionParams, type UpdateUserParams, type UserListParams, type UserMenuPermissionsResponse, type ValidateUserParams, type ViewFieldPermissionValue, type ViewOperationPermission, type ViewPermissionSummary, createPageSdk, createReactPage, useCurrentUser, useDataSource, useFormViewPermissions, useMessage, useModal, useNavigation, usePageContext, usePageProps, usePageRoute, usePageSdk };
869
+ export { type ApiPermissionListParams, type ApproveTaskParams, type AssignPermissionsParams, type AssignRolesParams, type BatchAddUsersToRoleParams, type BatchSendNotificationByTypeParams, type ChangeUserRoleParams, type ConnectorCallParams, type ConnectorInvokeParams, type ConnectorInvokeResult, type ConnectorRequestBodyType, type ConnectorResponseType, type CreateApiPermissionParams, type CreateFormPermissionGroupDto, type CreatePagePermissionGroupDto, type CreateRoleParams, type CreateUiPermissionParams, type CreateUserParams, type CurrentUserDepartmentParents, type CustomPageEntryConfig, type CustomPageEntryMode, type DataManagementConfigParams, type DataManagementFilterState, type DataPermissionConditionDto, type DataPermissionDto, type DataPermissionRuleDto, type FieldPermissionDto, type FormAdvancedSearchParams, type FormChangeRecordParams, type FormCreateParams, type FormExportParams, type FormGetDetailParams, type FormImportParams, type FormPermissionGroup, type FormRemoveParams, type FormSearchParams, type FormUpdateParams, type GetParentDepartmentsOptions, type GetProcessInstanceParams, type GetUserRolesParams, type ImportExportRecordDownloadParams, type ImportExportRecordQuery, type InstanceStatus, type NotificationChannel, type NotificationMessageRecord, type PageApiPermissionRecord, type PageApiResponse, type PageAppInfo, type PageBinaryResponse, type PageBridgeApi, type PageContext, type PageDataManagementConfig, type PageDataSourceDescriptor, type PageDepartmentInfo, type PageDepartmentRecord, type PageHttpMethod, type PageInfo, type PageListResult, type PageMessageApi, type PageModalApi, type PageNavigationApi, type PageOffsetListResult, type PagePermissionGroup, type PagePermissionInfo, PageProvider, type PageQueryValue, type PageRequestOptions, type PageRoleRecord, type PageRouteInfo, type PageScope, type PageSdk, type PageSdkError, type PageSdkMeta, type PageTransportDownloadPayload, type PageTransportRequestPayload, type PageUiPermissionRecord, type PageUiPermissionType, type PageUserInfo, type PageUserRecord, type PageUserType, type ProcessApproveAction, type QueryFormPermissionGroupDto, type QueryPagePermissionGroupDto, type RoleListParams, type RoleUsersParams, type SaveDataManagementConfigParams, type SearchComponentName, type SearchExpression, type SearchFieldKey, type SearchGroup, type SearchLogic, type SearchOperator, type SearchRule, type SearchSortItem, type SearchSystemField, type SendNotificationByTypeParams, type SendNotificationResult, type SubFormRule, type SwitchAppRoleParams, type SwitchPlatformRoleParams, type TerminateProcessInstanceParams, type TriggerCallbackTaskParams, type UiPermissionListParams, type UpdateApiPermissionParams, type UpdateFormPermissionGroupDto, type UpdatePagePermissionGroupDto, type UpdateRoleParams, type UpdateUiPermissionParams, type UpdateUserParams, type UserListParams, type UserMenuPermissionsResponse, type ValidateUserParams, type ViewFieldPermissionValue, type ViewOperationPermission, type ViewPermissionSummary, createPageSdk, createReactPage, useCurrentUser, useDataSource, useFormViewPermissions, useMessage, useModal, useNavigation, usePageContext, usePageProps, usePageRoute, usePageSdk };
@@ -66,6 +66,7 @@ export function buildDirectPagePublishPayload(config, pages) {
66
66
  code: page.config.code,
67
67
  name: page.config.name,
68
68
  description: page.config.description || "",
69
+ entry: page.config.entry || {},
69
70
  route: page.config.route,
70
71
  props: page.config.props || {},
71
72
  dataSources: page.config.dataSources || [],
@@ -1,7 +1,9 @@
1
- import { describe, expect, it } from "vitest";
1
+ import { mkdtemp, mkdir, rm, writeFile } from "node:fs/promises";
2
+ import { join } from "node:path";
3
+
4
+ import { describe, expect, it, vi } from "vitest";
2
5
 
3
6
  import { getFormBundleUrl, getPageAssetUrl } from "./load-config.mjs";
4
- import { discoverPages } from "./pages.mjs";
5
7
  import { buildDirectPagePublishPayload } from "./register-payload.mjs";
6
8
 
7
9
  const config = {
@@ -25,10 +27,47 @@ const config = {
25
27
 
26
28
  describe("register payload helpers", () => {
27
29
  it("discovers publishable code pages", async () => {
28
- const pages = await discoverPages("customer-dashboard");
30
+ const workspaceRoot = await mkdtemp(
31
+ join(process.cwd(), ".tmp-openxiangda-pages-test-"),
32
+ );
33
+ const previousWorkspaceRoot = process.env.LOWCODE_WORKSPACE_ROOT;
34
+
35
+ try {
36
+ const pageDir = join(workspaceRoot, "src/pages/customer-dashboard");
37
+ await mkdir(pageDir, { recursive: true });
38
+ await writeFile(join(pageDir, "index.tsx"), "export {};\n", "utf8");
39
+ await writeFile(
40
+ join(pageDir, "App.tsx"),
41
+ "export default function App() { return null; }\n",
42
+ "utf8",
43
+ );
44
+ await writeFile(
45
+ join(pageDir, "page.config.ts"),
46
+ `export default {
47
+ code: "customer-dashboard",
48
+ name: "客户经营看板",
49
+ route: { pathKey: "customer-dashboard" },
50
+ };
51
+ `,
52
+ "utf8",
53
+ );
54
+
55
+ process.env.LOWCODE_WORKSPACE_ROOT = workspaceRoot;
56
+ vi.resetModules();
57
+ const { discoverPages } = await import("./pages.mjs");
58
+ const pages = await discoverPages("customer-dashboard");
29
59
 
30
- expect(pages).toHaveLength(1);
31
- expect(pages[0].config.code).toBe("customer-dashboard");
60
+ expect(pages).toHaveLength(1);
61
+ expect(pages[0].config.code).toBe("customer-dashboard");
62
+ } finally {
63
+ if (previousWorkspaceRoot === undefined) {
64
+ delete process.env.LOWCODE_WORKSPACE_ROOT;
65
+ } else {
66
+ process.env.LOWCODE_WORKSPACE_ROOT = previousWorkspaceRoot;
67
+ }
68
+ vi.resetModules();
69
+ await rm(workspaceRoot, { recursive: true, force: true });
70
+ }
32
71
  });
33
72
 
34
73
  it("builds stable form and page asset URLs from one build id", () => {
@@ -46,6 +85,11 @@ describe("register payload helpers", () => {
46
85
  config: {
47
86
  code: "customer-dashboard",
48
87
  name: "客户经营看板",
88
+ entry: {
89
+ mode: "app-shell",
90
+ hidePlatformNav: true,
91
+ defaultRoute: "home",
92
+ },
49
93
  route: { pathKey: "customer-dashboard" },
50
94
  props: { title: "客户经营看板" },
51
95
  dataSources: [],
@@ -56,6 +100,11 @@ describe("register payload helpers", () => {
56
100
  expect(payload.pages[0].runtime.entryUrl).toBe(
57
101
  "https://bucket.oss-cn-hangzhou.aliyuncs.com/lowcode/app-workspace/dev/1.2.3/BUILD_001/pages/customer-dashboard/index.js",
58
102
  );
103
+ expect(payload.pages[0].entry).toEqual({
104
+ mode: "app-shell",
105
+ hidePlatformNav: true,
106
+ defaultRoute: "home",
107
+ });
59
108
  expect(JSON.stringify(payload)).not.toMatch(/manifest/i);
60
109
  });
61
110
 
@@ -26,6 +26,42 @@ export interface AppWorkspaceConfig {
26
26
  };
27
27
  }
28
28
 
29
+ export type CustomPageEntryMode = "app-shell" | "plain-page";
30
+
31
+ export interface CustomPageEntryConfig {
32
+ mode?: CustomPageEntryMode | string;
33
+ hidePlatformNav?: boolean;
34
+ defaultRoute?: string;
35
+ }
36
+
37
+ export interface CustomPageConfig {
38
+ code: string;
39
+ name: string;
40
+ description?: string;
41
+ publish?: boolean;
42
+ route?: {
43
+ pathKey?: string;
44
+ allowQueryKeys?: string[];
45
+ allowHash?: boolean;
46
+ subRouteMode?: "hash" | "memory" | string;
47
+ defaultQuery?: Record<string, unknown>;
48
+ };
49
+ entry?: CustomPageEntryConfig;
50
+ props?: Record<string, unknown>;
51
+ dataSources?: Array<Record<string, unknown>>;
52
+ menu?: {
53
+ enabled?: boolean;
54
+ name?: string;
55
+ parentId?: string | null;
56
+ icon?: string | null;
57
+ };
58
+ cssIsolation?: "namespace" | "shadow" | "none";
59
+ }
60
+
29
61
  export function defineAppWorkspaceConfig<T extends AppWorkspaceConfig>(config: T): T {
30
62
  return config;
31
63
  }
64
+
65
+ export function definePageConfig<T extends CustomPageConfig>(config: T): T {
66
+ return config;
67
+ }