openxiangda 1.0.77 → 1.0.79

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 (25) hide show
  1. package/README.md +3 -1
  2. package/lib/cli.js +330 -11
  3. package/openxiangda-skills/SKILL.md +11 -9
  4. package/openxiangda-skills/references/automation-v3.md +17 -1
  5. package/openxiangda-skills/references/connector-resources.md +4 -1
  6. package/openxiangda-skills/references/data-views.md +23 -9
  7. package/openxiangda-skills/references/pages/page-sdk.md +15 -0
  8. package/openxiangda-skills/references/resource-manifest-cheatsheet.md +92 -19
  9. package/openxiangda-skills/references/workflow-v3.md +17 -1
  10. package/openxiangda-skills/skills/openxiangda-app/SKILL.md +7 -5
  11. package/openxiangda-skills/skills/openxiangda-core/SKILL.md +7 -4
  12. package/openxiangda-skills/skills/openxiangda-form/SKILL.md +9 -9
  13. package/openxiangda-skills/skills/openxiangda-inspect/SKILL.md +2 -2
  14. package/openxiangda-skills/skills/openxiangda-page/SKILL.md +19 -16
  15. package/openxiangda-skills/skills/openxiangda-permission-settings/SKILL.md +4 -4
  16. package/openxiangda-skills/skills/openxiangda-workflow-automation/SKILL.md +27 -8
  17. package/package.json +1 -1
  18. package/packages/sdk/dist/runtime/index.cjs +20 -0
  19. package/packages/sdk/dist/runtime/index.cjs.map +1 -1
  20. package/packages/sdk/dist/runtime/index.d.mts +18 -1
  21. package/packages/sdk/dist/runtime/index.d.ts +18 -1
  22. package/packages/sdk/dist/runtime/index.mjs +20 -0
  23. package/packages/sdk/dist/runtime/index.mjs.map +1 -1
  24. package/templates/sy-lowcode-app-workspace/scripts/build-js-code.mjs +8 -2
  25. package/templates/sy-lowcode-app-workspace/tsconfig.js-code-nodes.json +5 -1
@@ -26,10 +26,11 @@ OpenXiangda supports two workspace modes. Classic `sy-lowcode-app-workspace` pub
26
26
  | 创建 / 修改自定义代码页 / portal / dashboard | `openxiangda-page` | edit `src/pages/<code>/` → `workspace publish --page <code>` |
27
27
  | 审批流程 / workflow / 流程节点 / JS_CODE | `openxiangda-workflow-automation` | `openxiangda workflow validate / create / publish` |
28
28
  | 自动化 / 定时任务 / 提交触发 / cron | `openxiangda-workflow-automation` | `openxiangda automation validate / create / publish / enable` |
29
+ | App Function / function_call / 可复用后端逻辑 | `openxiangda-workflow-automation` | declare `src/resources/functions/<code>.json` + `src/functions/<code>/index.ts` |
29
30
  | 角色 / 权限组 / 字段权限 / 数据范围 / 公开访问 | `openxiangda-permission-settings` | `openxiangda permission ...` / `openxiangda settings ...` |
30
31
  | 看应用结构 / 快照 / 对比 / 诊断 / 排查 / 报错 | `openxiangda-inspect` | `openxiangda app snapshot <APP_XXX> --profile <name> --json` |
31
32
  | 登录 / 切换平台 / profile / token / whoami | `openxiangda-core` | `openxiangda env --profile <name>` / `openxiangda auth status` |
32
- | 多表只读联表 / 固定口径统计 / 看板指标 | `openxiangda-form` (data view) | declare `src/resources/data-views/<code>.json` → `resource publish` |
33
+ | 多表只读联表 / 固定口径统计 / 强实时复杂查询 / 看板指标 | `openxiangda-form` (data view) | declare `src/resources/data-views/<code>.json` with `storageMode` → `resource publish` |
33
34
  | 调外部 / 第三方 API / 钉钉 / 自建系统 | `openxiangda-page` (connector) | declare `src/resources/connectors/<code>.json` → `sdk.connector.invoke` |
34
35
 
35
36
  ### Hard rules — always
@@ -133,14 +134,15 @@ When the user provides a root domain such as `https://yida.wisejob.cn/`, use it
133
134
  - All visible copy in forms and pages must be written for end users. Do not put implementation notes, developer explanations, schema descriptions, or "this area is generated by..." text into page sections, cards, labels, tips, or empty states.
134
135
  - Use logical resource codes in local files. Platform-specific IDs such as `formUuid`, `pageId`, `workflowId`, and `automationId` must be isolated by profile.
135
136
  - Put engineering-managed resources in `src/resources/` and use `openxiangda resource validate|plan|publish|pull`. `workspace publish` publishes workspace forms/pages first, then runs non-destructive resource upsert. Only pass `--prune` when the user explicitly wants local manifests to delete platform-side extras.
136
- - For repeated read-only multi-form queries or predefined dashboard metrics, create a data view manifest in `src/resources/data-views/`. Use row views plus `sdk.dataView.query` for joined lists/lookups; use `viewType: "aggregate"` plus `sdk.dataView.stats` for count/sum/avg/min/max statistics. Before choosing scheduled refresh, confirm the user's freshness tolerance; default to manual or 10 minutes and above unless fresher data is explicitly required. Add `indexes` for common runtime filters/order fields, aggregate dimensions, and time buckets. Do not use data views for single-form CRUD, simple linkedForm selects, real-time writes, write-back, or ad-hoc BI. Read `references/data-views.md` before designing one.
137
+ - For repeated read-only multi-form queries or predefined dashboard metrics, create a data view manifest in `src/resources/data-views/`. Use `storageMode: "materialized"` for refreshed lists/reports that tolerate delay; use `storageMode: "live"` for bounded real-time joins where source changes must appear immediately. Use row views plus `sdk.dataView.query` for joined lists/lookups; use `viewType: "aggregate"` plus `sdk.dataView.stats` for count/sum/avg/min/max statistics. Add `indexes` only for materialized views. Do not use data views for single-form CRUD, simple linkedForm selects, writes, write-back, unbounded realtime joins, or ad-hoc BI. Read `references/data-views.md` before designing one.
137
138
  - For external APIs, create a connector manifest in `src/resources/connectors/` and call it from pages through `sdk.connector`; never put third-party API keys in page source.
139
+ - For reusable backend logic shared by pages, automations, and workflows, create an App Function in `src/resources/functions/<functionCode>.json` plus `src/functions/<functionCode>/index.ts`. Call it from pages with `sdk.function.invoke`, from automation/workflow graphs with `function_call`, or from the runtime endpoint when the caller has app automation management permission. Current App Function MVP exposes controlled helpers such as `ctx.resources`, `ctx.form`, `ctx.dataView`, `ctx.connector`, `ctx.notification`, and `ctx.platform.api`; it does not expose raw SQL or Redis.
138
140
  - Before scaffolding a real app, complex page, data management page, portal shell, status lifecycle, role governance, or automation, read `references/best-practices.md` and pick the architecture first. Prefer copying the relevant pattern from `examples/best-practices/` into `src/` instead of generating a single large page file.
139
141
  - 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.
140
142
  - Use `openxiangda app snapshot <APP_XXX> --profile <name> --json` for diagnosis before changing an existing app.
141
143
  - Run write commands that update `.openxiangda/state.json` sequentially within the same workspace. Read-only commands can run in parallel.
142
144
  - 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.
143
- - 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.
145
+ - Use JS_CODE for node-local cross-form data queries, create/update/batch update operations, process termination, platform API calls, external HTTP calls, and backend trigger orchestration. For logic that pages, automations, and workflows should reuse through a stable backend entry, prefer App Function. Do not use JS_CODE for simple UI interactions, ordinary form validation, or display-only page behavior.
144
146
  - 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.
145
147
 
146
148
  ## Subskills
@@ -169,9 +171,9 @@ Core CLI / state:
169
171
 
170
172
  - `references/openxiangda-api.md` — `/openxiangda-api/v1` request and response fields.
171
173
  - `references/workspace-state.md` — `.openxiangda/state.json` shape and profile isolation rules.
172
- - `references/connector-resources.md` — `src/resources` manifests, connector schema, and SDK connector calls.
173
- - `references/resource-manifest-cheatsheet.md` — 复制即用的 connector / data-view / notification / workflow / automation / JS_CODE / role / permission group / settings / menu manifest 骨架与运行时调用示例。在创建任何 `src/resources/` 资源前先看这份。
174
- - `references/data-views.md` — `src/resources/data-views` materialized view resources, DSL, permissions, refresh, CLI commands, and runtime SDK usage.
174
+ - `references/connector-resources.md` — `src/resources` manifests, connector schema, App Function boundary, and platform runtime calls.
175
+ - `references/resource-manifest-cheatsheet.md` — 复制即用的 connector / data-view / notification / App Function / workflow / automation / JS_CODE / role / permission group / settings / menu manifest 骨架与运行时调用示例。在创建任何 `src/resources/` 资源前先看这份。
176
+ - `references/data-views.md` — `src/resources/data-views` materialized/live data view resources, DSL, permissions, refresh, CLI commands, and runtime SDK usage.
175
177
  - `references/notifications.md` — `src/resources/notifications`, notification templates/type bindings, and `sdk.notification` / `ctx.notification`.
176
178
  - `references/best-practices.md` — initialized examples for modular pages, state lifecycles, role governance, permission isolation, high-performance queries, portal shells, workflow boundaries, and automation patterns.
177
179
 
@@ -184,13 +186,13 @@ Form authoring:
184
186
  Page authoring:
185
187
 
186
188
  - `references/pages/workspace-structure.md` — `src/pages/<pageCode>/` layout and config.
187
- - `references/pages/page-sdk.md` — `openxiangda/runtime` data and runtime APIs.
189
+ - `references/pages/page-sdk.md` — `openxiangda/runtime` data, connector, data view, notification, and App Function APIs.
188
190
  - `references/pages/publish-flow.md` — workspace publish steps for code pages.
189
191
 
190
192
  Workflow / automation / permissions:
191
193
 
192
- - `references/workflow-v3.md` — workflow v3 definitions, JS_CODE nodes, trusted_node mode.
193
- - `references/automation-v3.md` — automation triggers, conditions, and publishing.
194
+ - `references/workflow-v3.md` — workflow v3 definitions, JS_CODE nodes, App Function `function_call`, and trusted_node mode.
195
+ - `references/automation-v3.md` — automation triggers, conditions, App Function `function_call`, and publishing.
194
196
  - `references/permissions-settings.md` — roles, page/form permission groups, settings.
195
197
 
196
198
  Platform domain knowledge (read these first when generating forms or pages so the output matches the live platform behavior):
@@ -254,6 +254,22 @@ Supported node types:
254
254
 
255
255
  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.
256
256
 
257
+ For reusable backend logic that should be shared by pages, multiple automations, and workflows, prefer an App Function under `src/functions/<functionCode>/index.ts` plus `src/resources/functions/<functionCode>.json`. Automation graphs can call it with a `function_call` node:
258
+
259
+ ```json
260
+ {
261
+ "id": "call_summary",
262
+ "type": "function_call",
263
+ "data": {
264
+ "functionCode": "reservation_reminder_summary",
265
+ "input": { "scope": "today" },
266
+ "saveResponseTo": "summary"
267
+ }
268
+ }
269
+ ```
270
+
271
+ Use JS_CODE V2 when the script is local to one automation graph node.
272
+
257
273
  ```json
258
274
  {
259
275
  "id": "sync_customer",
@@ -275,7 +291,7 @@ Author source in `sy-lowcode-app-workspace/src/js-code-nodes/<scriptCode>/index.
275
291
 
276
292
  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`.
277
293
 
278
- 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`.
294
+ Runtime context includes `ctx.triggerEvent`, `ctx.formData`, `ctx.workflowData`, `ctx.operator`, `ctx.app`, `ctx.variables`, and `ctx.node`. Prefer the higher-level resource helpers when available: `ctx.resources.resolveForm/resolveDataView/resolveConnector`, `ctx.form.queryMany/createOne/updateOne/updateById`, `ctx.dataView.query/stats`, `ctx.connector.call/invoke`, `ctx.notification.*`, and `ctx.platform.api.*`. Legacy data/process bridge methods remain available as `ctx.methods.queryOneData`, `queryManyData`, `updateOneData`, `updateDataByFormInstanceId`, `updateManyData`, `createOneData`, `terminateProcess`, and `getAllParentDepartments`.
279
295
 
280
296
  Code automation also exposes `ctx.logger.debug/info/warn/error(message, data?)`. AI-authored code should log input parsing, query conditions, external calls, writes, branch decisions, and caught errors. Logs are stored as full raw execution data by default. Use CLI diagnosis commands:
281
297
 
@@ -11,6 +11,7 @@ Common folders:
11
11
  - `workflows`
12
12
  - `automations`
13
13
  - `data-views`
14
+ - `functions`
14
15
  - `permissions/page-groups`
15
16
  - `permissions/form-groups`
16
17
  - `settings/forms`
@@ -28,7 +29,9 @@ Connector manifests use stable `code` values. The platform maps connector `code`
28
29
 
29
30
  Notification manifests live under `src/resources/notifications/` and contain `templates` plus `typeConfigs`. See `notifications.md` before generating reminders or message templates.
30
31
 
31
- Data view manifests live under `src/resources/data-views/` and define read-only materialized views for repeated multi-form joins or fixed aggregate statistics. Use row views with `sdk.dataView.query` for joined list/report/lookup sources, and aggregate views with `sdk.dataView.stats` for dashboard metrics where refresh lag is acceptable. Before generating one, confirm freshness tolerance and add indexes for common filters, sort fields, dimensions, and date buckets. Do not use them for single-form CRUD, simple linkedForm selects, real-time writes, write-back, or ad-hoc BI. See `data-views.md` before generating one.
32
+ Data view manifests live under `src/resources/data-views/` and define read-only joined or aggregate query resources. Use `storageMode: "materialized"` for refreshed lists/reports where lag is acceptable, and `storageMode: "live"` for bounded real-time query shapes. Use row views with `sdk.dataView.query` and aggregate views with `sdk.dataView.stats`. Before generating one, confirm freshness tolerance, query bounds, and indexes for materialized filters/sort fields/dimensions/date buckets. Do not use them for single-form CRUD, simple linkedForm selects, writes, write-back, or ad-hoc BI. See `data-views.md` before generating one.
33
+
34
+ App Function manifests live under `src/resources/functions/`, with source in `src/functions/<functionCode>/index.ts`. Use them for reusable server-side logic that pages, automations, and workflows can share through `sdk.function.invoke` or `function_call` nodes. App Functions expose controlled runtime helpers such as `ctx.resources`, `ctx.form`, `ctx.dataView`, `ctx.connector`, `ctx.notification`, and `ctx.platform.api`; they do not expose raw SQL or Redis in the current MVP. Use JS_CODE V2 only for node-local workflow/automation scripts.
32
35
 
33
36
  ```json
34
37
  {
@@ -1,6 +1,6 @@
1
1
  # Data View Resources
2
2
 
3
- Data views are OpenXiangda-managed read-only PostgreSQL materialized views. They turn repeated multi-form joins and predefined aggregate statistics into published resources under `src/resources/data-views/`.
3
+ Data views are OpenXiangda-managed read-only query resources under `src/resources/data-views/`. They can run as PostgreSQL materialized views (`storageMode: "materialized"`, the default) or as live logical views (`storageMode: "live"`). Use them to publish repeated multi-form joins and predefined aggregate statistics as reusable app resources.
4
4
 
5
5
  Use a data view when the app needs read-only joined data from multiple forms, such as:
6
6
 
@@ -10,13 +10,14 @@ Use a data view when the app needs read-only joined data from multiple forms, su
10
10
  - Dashboard statistics such as monthly ticket count, order amount by customer, or task count by status.
11
11
  - Reusable lookup or report data consumed by several pages or automations.
12
12
  - Large list pages where repeated client-side cross-form joins would be slow or inconsistent.
13
+ - Real-time multi-form reads where the query shape is fixed and the source data volume is bounded (`storageMode: "live"`).
13
14
 
14
15
  Do not use a data view for:
15
16
 
16
17
  - Single-form CRUD. Use `sdk.form.advancedSearch`, form pages, or `DataManagementList`.
17
18
  - Simple one-form dropdown options. Use `SelectField` with `optionSource.type: "linkedForm"`.
18
19
  - Writes or write-back. Data views are read-only.
19
- - Strong real-time views after every form update. Data views update after manual or scheduled refresh.
20
+ - Heavy real-time joins that cannot be paginated, indexed, filtered, or otherwise bounded.
20
21
  - Raw SQL, incremental refresh, source-table trigger refresh, ad-hoc BI/pivot/window analysis, or write-heavy realtime dashboards.
21
22
 
22
23
  ## Authoring
@@ -25,8 +26,13 @@ Place manifests in `src/resources/data-views/*.json`. Use logical `formCode` val
25
26
 
26
27
  Two view types are supported:
27
28
 
28
- - `viewType: "row"` or omitted: row-level joined materialized view using `select`.
29
- - `viewType: "aggregate"`: grouped statistics materialized view using `dimensions` and `measures`; query it with `stats`.
29
+ - `viewType: "row"` or omitted: row-level joined view using `select`.
30
+ - `viewType: "aggregate"`: grouped statistics view using `dimensions` and `measures`; query it with `stats`.
31
+
32
+ Two storage modes are supported:
33
+
34
+ - `storageMode: "materialized"` or omitted: create a PostgreSQL materialized view. Results may be stale until manual or scheduled refresh. Use `indexes` for common filters, sort fields, aggregate dimensions, and date buckets.
35
+ - `storageMode: "live"`: do not create a materialized view. Every query is compiled and executed against source forms in real time. `indexes` are ignored and refresh APIs do not apply. Use it only for bounded real-time query shapes.
30
36
 
31
37
  Row view example:
32
38
 
@@ -34,6 +40,7 @@ Row view example:
34
40
  {
35
41
  "code": "ticket_with_customer",
36
42
  "name": "Ticket With Customer",
43
+ "storageMode": "materialized",
37
44
  "base": { "formCode": "service_ticket", "alias": "ticket" },
38
45
  "joins": [
39
46
  {
@@ -74,6 +81,7 @@ Aggregate statistics example:
74
81
  "code": "ticket_stats_by_customer",
75
82
  "name": "Ticket Stats By Customer",
76
83
  "viewType": "aggregate",
84
+ "storageMode": "materialized",
77
85
  "base": { "formCode": "service_ticket", "alias": "ticket" },
78
86
  "joins": [
79
87
  {
@@ -144,9 +152,11 @@ Filters:
144
152
 
145
153
  Refresh:
146
154
 
155
+ - Refresh applies only to materialized data views.
147
156
  - `manual` means refresh only when an administrator runs refresh or the resource is recreated.
148
157
  - `scheduled` uses cron, for example `0 */10 * * * *`.
149
- - Query results may be stale. Show or inspect `lastRefreshedAt` when freshness matters.
158
+ - Materialized query results may be stale. Show or inspect `lastRefreshedAt` when freshness matters.
159
+ - Live data views have no refresh state; `lastRefreshedAt` is `null` and refresh requests should be rejected.
150
160
  - Do not choose a high-frequency schedule by default. Ask or infer how stale the data may be before setting `scheduled`.
151
161
 
152
162
  Indexes:
@@ -159,11 +169,12 @@ Indexes:
159
169
 
160
170
  ## Performance And Freshness
161
171
 
162
- Data views move expensive joins and aggregations from page runtime to materialized-view refresh time. They reduce repeated page pressure only when the view shape, refresh cadence, and indexes are chosen deliberately.
172
+ Materialized data views move expensive joins and aggregations from page runtime to refresh time. Live data views keep results current by executing the same declared query shape on each request. Both modes reduce repeated page code complexity only when the view shape, data scope, and query bounds are chosen deliberately.
163
173
 
164
174
  Before creating a data view, confirm these points:
165
175
 
166
176
  - **Freshness tolerance**: ask whether users need near-real-time data, 10-30 minute freshness, hourly/daily reports, or manual snapshots.
177
+ - **Storage mode**: use `materialized` for read-heavy reports that tolerate refresh lag; use `live` for bounded real-time joins where source-table changes must appear immediately.
167
178
  - **Query shape**: list the fields users will filter, sort, group, or drill down by. These should become output aliases and usually indexes.
168
179
  - **Data scope**: if the business only needs active, recent, or in-scope records, put that rule in definition `where` so old/source-irrelevant rows are filtered before materialization.
169
180
  - **Visible fields**: output only fields the page or permission rules need. Use hidden scope aliases for data permissions when needed, but do not build one huge catch-all view.
@@ -174,11 +185,12 @@ Refresh cadence guidance:
174
185
  - Use `scheduled` every 10-30 minutes for normal dashboards, joined report lists, and recurring management views.
175
186
  - Use hourly or daily schedules for leadership reports, historical statistics, and large aggregate views.
176
187
  - Avoid intervals below 5 minutes unless the user explicitly confirms the time sensitivity and expected source-table volume. High-frequency refreshes can pressure both source tables and the materialized view.
177
- - For strong real-time behavior after each form save, do not use a data view; query the source form directly or design a different backend workflow.
188
+ - For strong real-time behavior after each form save, use `storageMode: "live"` only when the query is bounded. If the logic includes writes, side effects, raw platform orchestration, or expensive unbounded joins, use source-form queries, App Functions, or a different backend workflow instead.
178
189
 
179
190
  Index guidance:
180
191
 
181
- - Index only materialized-view output aliases, not source references such as `customer.name`.
192
+ - For materialized mode, index only output aliases, not source references such as `customer.name`.
193
+ - Live data views ignore `indexes`; keep their runtime filters and page size conservative.
182
194
  - For row views, index IDs used in drill-down (`ticketId`, `customerId`) and common filters/order fields (`statusValue`, `ownerId`, `createdAt`).
183
195
  - For aggregate views, index the dimensions/time buckets used in runtime `filters`, `having` drill-downs, and `order` (`customerId`, `statusValue`, `createdMonth`).
184
196
  - Keep compound indexes aligned with common query prefixes. For example, dashboard filters by customer then month should use `["customerId", "createdMonth"]`.
@@ -219,7 +231,7 @@ Diagnostic commands:
219
231
  ```bash
220
232
  openxiangda data-view list --profile dev
221
233
  openxiangda data-view status ticket_with_customer --profile dev
222
- openxiangda data-view refresh ticket_with_customer --profile dev
234
+ openxiangda data-view refresh ticket_with_customer --profile dev # materialized only
223
235
  openxiangda data-view query ticket_with_customer --profile dev --fields ticketId,customerName
224
236
  openxiangda data-view query ticket_with_customer --profile dev --query-json query.json
225
237
  openxiangda data-view stats ticket_stats_by_customer --profile dev --fields customerName,ticketCount
@@ -228,6 +240,8 @@ openxiangda data-view stats ticket_stats_by_customer --profile dev --query-json
228
240
 
229
241
  ## Runtime SDK
230
242
 
243
+ `sdk.dataView.query` and `sdk.dataView.stats` work for both materialized and live data views. Responses include `storageMode`; materialized responses may include `lastRefreshedAt`, while live responses return current data and `lastRefreshedAt: null`.
244
+
231
245
  Direct query:
232
246
 
233
247
  ```ts
@@ -7,9 +7,11 @@ Guidelines:
7
7
  - Do not hardcode `/openxiangda-api` calls inside end-user page components unless the page is explicitly an admin tool.
8
8
  - Prefer SDK modules for form data, user context, permissions, and platform navigation.
9
9
  - Use `sdk.dataView.query` for published read-only multi-form row data views. Use `sdk.dataView.stats` for aggregate data views declared with `viewType: "aggregate"`.
10
+ - Data views can be `storageMode: "materialized"` for refreshed report/list data or `storageMode: "live"` for bounded real-time joins. Page code calls the same SDK methods for both modes and should inspect `response.storageMode` / `response.lastRefreshedAt` when freshness is visible to users.
10
11
  - Data view runtime queries can filter, sort, paginate, and select only output aliases.
11
12
  - Keep data view page calls paginated and field-scoped. Runtime filters/order should use aliases that the data view indexes, especially for aggregate dimensions and date buckets.
12
13
  - Use `sdk.dataSource.run()` with a page data source descriptor when the page config should own the data view code, default fields, or default filters.
14
+ - Use `sdk.function.invoke(code, { input })` for reusable backend business logic declared under `src/resources/functions/` and `src/functions/`. Do not implement multi-form orchestration, connector fan-out, notification orchestration, or permission-sensitive backend rules directly in a page component.
13
15
  - Use `sdk.connector.invoke`, `sdk.connector.call("connector.api")`, or `sdk.connector.download` for external services. The SDK calls the platform runtime connector endpoint; it must not call third-party domains directly.
14
16
  - Use `sdk.notification.sendByType` and `batchSendByType` for reusable business messages. Custom notification types must be declared in `src/resources/notifications/` and published with `openxiangda resource publish`.
15
17
  - For the current user's department hierarchy, use `sdk.department.getCurrentUserParentDepartments()`; do not hardcode `GET /department/:id/parentDepartments` in page code.
@@ -87,6 +89,19 @@ const response = await sdk.dataSource.run("tickets", {
87
89
 
88
90
  Data view filters, having, and fields use output aliases such as `customerName` or `ticketCount`, not source references such as `customer.name`. If the page only needs one form, prefer `sdk.form.advancedSearch`. If the page only needs a simple one-form dropdown, prefer linkedForm options.
89
91
 
92
+ App Function invocation:
93
+
94
+ ```ts
95
+ const result = await sdk.function.invoke("reservation_reminder_summary", {
96
+ input: {
97
+ scope: "today",
98
+ keyword,
99
+ },
100
+ })
101
+ ```
102
+
103
+ Use App Functions when the logic must run server-side and be reusable by pages, automations, or workflows. Current runtime invocation requires the caller to have app automation management permission; ordinary page users should normally consume function-backed data through a permission-aware page or a narrower backend resource.
104
+
90
105
  Logout and current-user role switching:
91
106
 
92
107
  ```ts
@@ -66,13 +66,15 @@ const data = await sdk.connector.call("crm.getCustomer", { body: { keyword } });
66
66
  - 多表只读联表列表 / lookup / 报表明细:用行级 data view,页面调用 `sdk.dataView.query`。
67
67
  - 固定口径统计 / 看板指标 / 图表数据源:用 `viewType: "aggregate"`,页面调用 `sdk.dataView.stats`。
68
68
  - ECharts 或自定义 dashboard 只负责展示;统计口径稳定时,数据源优先沉淀成 aggregate data view。
69
- - 发布前为常用筛选、排序、维度和时间桶声明 `indexes`,并确认刷新延迟是否能被业务接受。
70
- - 不用于单表 CRUD、简单 `linkedForm` 下拉、强实时、写回或临时 BI 查询。
69
+ - 默认 `storageMode: "materialized"`,适合读多写少和可接受刷新延迟的列表/报表;发布前为常用筛选、排序、维度和时间桶声明 `indexes`。
70
+ - `storageMode: "live"` 每次查询实时执行,适合强实时但数据量可控的固定复杂查询;live 模式忽略 `indexes` 且不能 refresh。
71
+ - 不用于单表 CRUD、简单 `linkedForm` 下拉、写回、无边界重查询或临时 BI 查询。
71
72
 
72
73
  ```json
73
74
  {
74
75
  "code": "ticket_with_customer",
75
76
  "name": "Ticket With Customer",
77
+ "storageMode": "materialized",
76
78
  "base": { "formCode": "service_ticket", "alias": "ticket" },
77
79
  "joins": [
78
80
  {
@@ -137,7 +139,7 @@ const stats = await sdk.dataView.stats("ticket_stats_by_customer", {
137
139
  });
138
140
  ```
139
141
 
140
- 性能要点:索引只能引用输出 alias;行级视图索引常用 filter/order 字段;聚合视图索引 dimensions/date buckets;`countDistinct` 和高基数维度刷新成本较高;低于 5 分钟的 scheduled refresh 需要用户明确确认时间敏感度。完整规则见 [`data-views.md`](data-views.md)。
142
+ 性能要点:索引只能引用输出 alias;行级视图索引常用 filter/order 字段;聚合视图索引 dimensions/date buckets;`countDistinct` 和高基数维度刷新成本较高;低于 5 分钟的 scheduled refresh 需要用户明确确认时间敏感度。live 视图不刷新、不建索引,必须控制字段、过滤和分页。完整规则见 [`data-views.md`](data-views.md)。
141
143
 
142
144
  ## 3. Notification — `src/resources/notifications/<code>.json`
143
145
 
@@ -178,7 +180,73 @@ await sdk.notification.sendByType({
178
180
 
179
181
  JS_CODE 调用:`ctx.notification.sendByType({ ... })`。允许的 channels:`inapp` / `email` / `dingding` / `wechat` / `thirdparty_todo`。完整规则见 [`notifications.md`](notifications.md)。
180
182
 
181
- ## 4. Workflow — `src/resources/workflows/<code>/workflow.json`(manifest)+ `src/workflows/<code>/workflow.ts`(代码优先)
183
+ ## 4. App Function — `src/resources/functions/<functionCode>.json` + `src/functions/<functionCode>/index.ts`
184
+
185
+ ```json
186
+ {
187
+ "code": "reservation_reminder_summary",
188
+ "name": "Reservation Reminder Summary",
189
+ "resources": {
190
+ "forms": ["reservation_order"],
191
+ "dataViews": ["reservation_order_overview"],
192
+ "connectors": ["crm"]
193
+ },
194
+ "definitionJson": {
195
+ "runtimeMode": "trusted_node",
196
+ "timeout": 30000,
197
+ "sourceFile": {
198
+ "localPath": "src/functions/reservation_reminder_summary/index.ts"
199
+ }
200
+ }
201
+ }
202
+ ```
203
+
204
+ ```ts
205
+ // src/functions/reservation_reminder_summary/index.ts
206
+ export default async function reservationReminderSummary(ctx, input) {
207
+ const orders = await ctx.form.queryMany({
208
+ formCode: "reservation_order",
209
+ filters: [{ field: "status", operator: "=", value: "pending" }],
210
+ pageSize: 50,
211
+ });
212
+
213
+ const overview = await ctx.dataView.query("reservation_order_overview", {
214
+ pageSize: 20,
215
+ });
216
+
217
+ return {
218
+ pendingCount: orders.totalCount || orders.data?.length || 0,
219
+ overview: overview.data || [],
220
+ input,
221
+ };
222
+ }
223
+ ```
224
+
225
+ 调用方式:
226
+
227
+ ```ts
228
+ const result = await sdk.function.invoke("reservation_reminder_summary", {
229
+ input: { scope: "today" },
230
+ });
231
+ ```
232
+
233
+ 自动化 / 流程图节点调用:
234
+
235
+ ```json
236
+ {
237
+ "id": "call_summary",
238
+ "type": "function_call",
239
+ "data": {
240
+ "functionCode": "reservation_reminder_summary",
241
+ "input": { "scope": "today" },
242
+ "saveResponseTo": "summary"
243
+ }
244
+ }
245
+ ```
246
+
247
+ 适用边界:可复用后端业务逻辑、跨页面/自动化/流程共享的查询编排、连接器调用、通知编排、受控平台 API 调用。当前 MVP 不暴露原始 SQL/Redis;运行时接口需要应用自动化管理权限,自动化/流程内部调用走服务端上下文。
248
+
249
+ ## 5. Workflow — `src/resources/workflows/<code>/workflow.json`(manifest)+ `src/workflows/<code>/workflow.ts`(代码优先)
182
250
 
183
251
  ```jsonc
184
252
  // src/resources/workflows/customer_approval/workflow.json
@@ -208,7 +276,7 @@ export default defineWorkflow({
208
276
 
209
277
  CLI 编译为 `definition.v3.json` + `preview.json`,平台运行时仍走标准工作流引擎。完整规则见 [`workflow-v3.md`](workflow-v3.md)。
210
278
 
211
- ## 5. Automation — `src/resources/automations/<code>/{definition.code.json,preview.json}` + `src/automations/<code>/index.ts`
279
+ ## 6. Automation — `src/resources/automations/<code>/{definition.code.json,preview.json}` + `src/automations/<code>/index.ts`
212
280
 
213
281
  ```jsonc
214
282
  // src/resources/automations/notify_on_submit/definition.code.json
@@ -242,17 +310,18 @@ export default async function (ctx) {
242
310
 
243
311
  `trigger_v2` 事件源:`form_data` / `form_field` / `workflow_task` / `workflow_process` / `mode: "scheduled"`(fixed_time / form_date_field)。完整规则见 [`automation-v3.md`](automation-v3.md)。
244
312
 
245
- ## 6. JS_CODE V2 — `src/js-code-nodes/<scriptCode>/index.ts`
313
+ ## 7. JS_CODE V2 — `src/js-code-nodes/<scriptCode>/index.ts`
314
+
315
+ JS_CODE V2 适合自动化/流程图里的节点级脚本。跨页面、跨自动化、需要一个稳定后端入口的可复用逻辑优先写 App Function,再用 `function_call` 节点或 `sdk.function.invoke` 调用。
246
316
 
247
317
  ```ts
248
318
  export default async function (ctx) {
249
- const { formData, operator, methods, notification, platform, utils, console } = ctx;
250
- const result = await methods.queryManyData({
319
+ const { formData, operator, form, dataView, connector, notification, platform, utils, console } = ctx;
320
+ const result = await form.queryMany({
251
321
  formCode: "customer",
252
- formUuid: ctx.app.formUuids.customer,
253
- searchCondition: [/* 查询条件 */],
322
+ filters: [/* 查询条件 */],
254
323
  });
255
- return { count: result.length };
324
+ return { count: result.totalCount || result.data?.length || 0 };
256
325
  }
257
326
  ```
258
327
 
@@ -275,7 +344,9 @@ export default async function (ctx) {
275
344
 
276
345
  构建:`pnpm build-js-code --script sync_customer`(先 `tsc` 再打包到 `dist/js-code-nodes/<code>/index.cjs`)。CLI validate/create/publish 时会上传快照、用 `{ bucketName, objectName, sha256 }` 替换 `sourceFile.localPath`。
277
346
 
278
- ## 7. Role `src/resources/roles/<code>.json`
347
+ `ctx.methods.*` 仍可用于兼容旧脚本;新脚本优先使用 `ctx.resources`、`ctx.form`、`ctx.dataView`、`ctx.connector`、`ctx.notification` 和 `ctx.platform.api`。
348
+
349
+ ## 8. Role — `src/resources/roles/<code>.json`
279
350
 
280
351
  ```json
281
352
  {
@@ -284,7 +355,7 @@ export default async function (ctx) {
284
355
  }
285
356
  ```
286
357
 
287
- ## 8. Page Permission Group — `src/resources/permissions/page-groups/<code>.json`
358
+ ## 9. Page Permission Group — `src/resources/permissions/page-groups/<code>.json`
288
359
 
289
360
  ```json
290
361
  {
@@ -298,7 +369,7 @@ export default async function (ctx) {
298
369
 
299
370
  `formCodes` / `pageCodes` / `menuCodes` 留空表示对匹配角色全部可见。
300
371
 
301
- ## 9. Form Permission Group — `src/resources/permissions/form-groups/<formCode>/<groupCode>.json`
372
+ ## 10. Form Permission Group — `src/resources/permissions/form-groups/<formCode>/<groupCode>.json`
302
373
 
303
374
  ```json
304
375
  {
@@ -321,7 +392,7 @@ export default async function (ctx) {
321
392
  }
322
393
  ```
323
394
 
324
- ## 10. Form Settings — `src/resources/settings/forms/<formCode>.json`
395
+ ## 11. Form Settings — `src/resources/settings/forms/<formCode>.json`
325
396
 
326
397
  ```json
327
398
  {
@@ -339,7 +410,7 @@ export default async function (ctx) {
339
410
  }
340
411
  ```
341
412
 
342
- ## 11. Menu — `src/resources/menus/<code>.json`
413
+ ## 12. Menu — `src/resources/menus/<code>.json`
343
414
 
344
415
  ```json
345
416
  {
@@ -359,16 +430,17 @@ export default async function (ctx) {
359
430
  ```text
360
431
  需要展示数据?
361
432
  ├─ 单表 CRUD → 表单页 + DataManagementList(不要 data view)
362
- ├─ 多表只读联表 → src/resources/data-views/
433
+ ├─ 多表只读联表 → src/resources/data-views/(materialized 或 live)
363
434
  ├─ 表单字段下拉 → SelectField + optionSource.type: "linkedForm"(不要 data view)
364
435
  └─ 大屏 / 报表 → 原生报表 → ECharts page
365
436
 
366
437
  需要后端逻辑?
367
438
  ├─ 真有审批 → workflow(src/workflows/<code>/workflow.ts)
368
439
  ├─ 状态流转 → 表单 status 字段 + 状态机 + automation
440
+ ├─ 可复用服务逻辑 → App Function(src/functions/<code>/index.ts + function_call / sdk.function.invoke)
369
441
  ├─ 提交/字段触发 → automation(trigger_v2 + src/automations/<code>/index.ts)
370
442
  ├─ 定时任务 → automation(mode: "scheduled")
371
- └─ 复杂后端逻辑 → JS_CODE V2 trusted_node(src/js-code-nodes/<code>/index.ts)
443
+ └─ 流程/自动化节点内脚本 → JS_CODE V2 trusted_node(src/js-code-nodes/<code>/index.ts)
372
444
 
373
445
  需要外部数据?
374
446
  ├─ 第三方 HTTP → src/resources/connectors/ + sdk.connector.call
@@ -380,5 +452,6 @@ export default async function (ctx) {
380
452
  - ❌ 把 `formUuid` / `pageId` 等平台 ID 写进 manifest。**用 code,CLI 自动解析。**
381
453
  - ❌ 把 API key、token、密码写进 manifest。**用占位符,平台后台填真实值。**
382
454
  - ❌ 同时在 manifest 与平台后台编辑同一个资源(漂移源)。**统一以 manifest 为单一来源**,需要看平台版本就 `resource pull`。
383
- - ❌ 用 data view 代替 `linkedForm` 下拉、单表 CRUD 或写回。**data view 是只读 + 延迟刷新。**
455
+ - ❌ 用 data view 代替 `linkedForm` 下拉、单表 CRUD 或写回。**data view 是只读;materialized 有刷新延迟,live 要控制查询边界。**
456
+ - ❌ 把跨页面/跨流程复用的后端逻辑都塞进 JS_CODE。**优先 App Function,JS_CODE 只做节点内脚本。**
384
457
  - ❌ 在 page 源码里 hardcode `/api/notification-config/*` 或 `/connectors/actions/invoke`。**用 `sdk.notification` / `sdk.connector`。**
@@ -135,7 +135,23 @@ File snapshot:
135
135
 
136
136
  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>`.
137
137
 
138
- 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.
138
+ For reusable backend logic that should be shared by pages, automations, and workflows, prefer an App Function under `src/functions/<functionCode>/index.ts` plus `src/resources/functions/<functionCode>.json`. Workflow graphs that support App Function nodes can call it with:
139
+
140
+ ```json
141
+ {
142
+ "id": "call_summary",
143
+ "type": "function_call",
144
+ "data": {
145
+ "functionCode": "reservation_reminder_summary",
146
+ "input": { "scope": "process" },
147
+ "saveResponseTo": "summary"
148
+ }
149
+ }
150
+ ```
151
+
152
+ Use JS_CODE V2 when the script is local to one workflow node.
153
+
154
+ 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.resources`, `ctx.form`, `ctx.dataView`, `ctx.connector`, `ctx.notification`, `ctx.platform.api.*`, `ctx.utils`, `require`, `process`, and `Buffer`. Prefer `ctx.resources.resolveForm/resolveDataView/resolveConnector`, `ctx.form.queryMany/createOne/updateOne/updateById`, `ctx.dataView.query/stats`, and `ctx.connector.call/invoke` for platform-side work. Legacy data/process bridge methods remain available as `ctx.methods.queryOneData/queryManyData/updateOneData/updateDataByFormInstanceId/updateManyData/createOneData/terminateProcess/getAllParentDepartments`.
139
155
 
140
156
  Example `src/js-code-nodes/sync_customer/index.ts`:
141
157
 
@@ -99,7 +99,7 @@ openxiangda workspace bind --profile <name> --app-type APP_XXX
99
99
  - Do not search platform apps or reuse similar app names unless the user explicitly asks to reuse an existing app or gives an `appType`.
100
100
  - Store platform-specific IDs only in `.openxiangda/state.json`.
101
101
  - Use logical local codes for forms, pages, workflows, automations, and menus.
102
- - Before scaffolding a new business app, read `../../references/best-practices.md` and pick an architecture from the initialized `examples/best-practices/` catalog. Do not generate a large single-file app page when a template pattern already covers the scenario.
102
+ - Before scaffolding a new business app, read `references/best-practices.md` and pick an architecture from the initialized `examples/best-practices/` catalog. Do not generate a large single-file app page when a template pattern already covers the scenario.
103
103
  - For Phase 6 React SPA apps, use `--runtime react-spa`. The generated app owns routing/layouts/menus with React Router and Tailwind CSS; platform permissions still come from backend runtime bootstrap and route checks.
104
104
  - React SPA form publish is schema-only by default. Do not add OSS credentials or old embedded form bundles unless explicitly maintaining legacy compatibility with `--legacy-form-bundle`.
105
105
  - When publishing after app edits, prefer targeted commands (`workspace publish --changed --dry-run`, then `--changed`, `--page`, `--form`, or `--only`). Do not publish all forms/pages just because one page changed.
@@ -107,10 +107,12 @@ openxiangda workspace bind --profile <name> --app-type APP_XXX
107
107
 
108
108
  ## Resource State
109
109
 
110
- Read `../../references/workspace-state.md` when changing `.openxiangda/state.json`.
110
+ Read `references/workspace-state.md` when changing `.openxiangda/state.json`.
111
111
 
112
- Read `../../references/openxiangda-api.md` when exact endpoint fields are needed.
112
+ Read `references/openxiangda-api.md` when exact endpoint fields are needed.
113
113
 
114
- Read `../../references/architecture-patterns.md` to understand the overall app structure (how forms, pages, menus, workflows, and permissions compose into an app, and the recommended `src/forms` / `src/pages` layout) before scaffolding or restructuring an app workspace.
114
+ Read `references/resource-manifest-cheatsheet.md` before creating shared resources such as data views, connectors, notifications, App Functions, workflows, automations, permissions, settings, or menus.
115
115
 
116
- Read `../../references/best-practices.md` before implementing app-level patterns such as status lifecycles, role governance, PC/mobile portals, high-performance data management pages, workflow boundaries, and automation.
116
+ Read `references/architecture-patterns.md` to understand the overall app structure (how forms, pages, menus, workflows, and permissions compose into an app, and the recommended `src/forms` / `src/pages` layout) before scaffolding or restructuring an app workspace.
117
+
118
+ Read `references/best-practices.md` before implementing app-level patterns such as status lifecycles, role governance, PC/mobile portals, high-performance data management pages, workflow boundaries, and automation.
@@ -287,7 +287,10 @@ It is responsible for:
287
287
 
288
288
  ## References
289
289
 
290
- - `../../references/openxiangda-api.md` — exact endpoint contracts.
291
- - `../../references/workspace-state.md` — `.openxiangda/state.json` shape and profile-scoped resource maps.
292
- - `../../references/architecture-patterns.md` — overall architecture of an OpenXiangda app workspace (CRUD data flow, page/form layering, recommended directory organization). Read this when you need to understand how forms, pages, workflows, and platform state fit together end-to-end.
293
- - `../../references/best-practices.md` — initialized best-practice templates and rules for modular pages, status lifecycles, role governance, data isolation, query performance, and workflow boundaries.
290
+ - `references/openxiangda-api.md` — exact endpoint contracts.
291
+ - `references/workspace-state.md` — `.openxiangda/state.json` shape and profile-scoped resource maps.
292
+ - `references/resource-manifest-cheatsheet.md` — quick templates for connectors, data views, App Functions, workflows, automations, permissions, settings, and menus.
293
+ - `references/data-views.md` — materialized/live data view authoring, refresh behavior, permissions, indexes, and runtime SDK usage.
294
+ - `references/connector-resources.md` — engineering resource folders, connector usage, App Function boundary, and platform runtime calls.
295
+ - `references/architecture-patterns.md` — overall architecture of an OpenXiangda app workspace (CRUD data flow, page/form layering, recommended directory organization). Read this when you need to understand how forms, pages, workflows, and platform state fit together end-to-end.
296
+ - `references/best-practices.md` — initialized best-practice templates and rules for modular pages, status lifecycles, role governance, data isolation, query performance, and workflow boundaries.
@@ -110,12 +110,12 @@ Do not use `openxiangda form create` as the normal way to generate a user-facing
110
110
 
111
111
  Read these references only when writing or reviewing schema:
112
112
 
113
- - `../../references/forms/form-schema.md`
114
- - `../../references/forms/component-registry.md`
115
- - `../../references/forms/layout-and-rules.md`
116
- - `../../references/platform-data-model.md` — how each field type is persisted (JSONB, `{label, value}` for option fields, attachment shape). Consult before designing schema or wiring values.
117
- - `../../references/component-guide.md` — platform component first, `antd` / `antd-mobile` fallback, and native-control ban for AI-authored app code.
118
- - `../../references/best-practices.md` — status lifecycle fields, ownership redundancy, role-governance fields, and the boundary between normal forms and workflow forms.
113
+ - `references/forms/form-schema.md`
114
+ - `references/forms/component-registry.md`
115
+ - `references/forms/layout-and-rules.md`
116
+ - `references/platform-data-model.md` — how each field type is persisted (JSONB, `{label, value}` for option fields, attachment shape). Consult before designing schema or wiring values.
117
+ - `references/component-guide.md` — platform component first, `antd` / `antd-mobile` fallback, and native-control ban for AI-authored app code.
118
+ - `references/best-practices.md` — status lifecycle fields, ownership redundancy, role-governance fields, and the boundary between normal forms and workflow forms.
119
119
 
120
120
  ## Rules
121
121
 
@@ -133,13 +133,13 @@ Read these references only when writing or reviewing schema:
133
133
  - Do not copy `formUuid` from dev to prod unless the target platform explicitly already uses that ID.
134
134
  - Keep `schema.ts` and `page.tsx` as the source for fields/layout/rules and presentation; generated build output is not the source of truth.
135
135
  - In `schema.ts`, import `defineFormSchema` from `openxiangda` and default-export the schema.
136
- - Treat `schema.ts` as the field/component contract. Prefer platform field components listed in `../../references/forms/component-registry.md`; do not replace them with raw JSX controls in `page.tsx`.
136
+ - Treat `schema.ts` as the field/component contract. Prefer platform field components listed in `references/forms/component-registry.md`; do not replace them with raw JSX controls in `page.tsx`.
137
137
  - Keep `page.tsx` presentation-only. It may customize layout, wrappers, sections, or routing, but should render a complete OpenXiangda standard form/runtime rather than assembling isolated raw controls.
138
138
  - If a platform field is missing for a real business need, submit feedback with `openxiangda feedback submit --yes` and then use an `antd` / `antd-mobile` wrapper as a local workaround. Report the workaround and fingerprint to the user.
139
139
  - Put validation on field-level `rules`; never generate old top-level validation arrays under `schema.rules`. Top-level `rules` is only for `FormEffect[]` with `when` and `then`.
140
- - Always provide `options` for option components. `SelectField`, `MultiSelectField`, `RadioField`, `CheckboxField`, and `CascadeSelectField` must not be emitted with `options` undefined. See `../../references/component-guide.md` for the full list and `../../references/platform-data-model.md` for the `{label, value}` storage contract.
140
+ - Always provide `options` for option components. `SelectField`, `MultiSelectField`, `RadioField`, `CheckboxField`, and `CascadeSelectField` must not be emitted with `options` undefined. See `references/component-guide.md` for the full list and `references/platform-data-model.md` for the `{label, value}` storage contract.
141
141
  - Do not emit raw native form controls in app/workspace source. For basic entry use `TextField`, `TextAreaField`, `NumberField`, `DateField`, `SelectField`, `RadioField`, etc.; for platform-specific entry use `UserSelectField`, `DepartmentSelectField`, `AttachmentField`, `ImageField`, `EditorField`, `DigitalSignatureField`, and `LocationField`.
142
142
  - Workflow form pages use the same workspace form structure plus workflow v3 configuration. In legacy workspaces, publish the form page bundle through workspace publish before treating it as complete. In React SPA workspaces, `workspace publish --form` syncs schema only, and the SPA default process pages render through `runtime deploy`.
143
143
  - After editing one form, use `workspace publish --form <formCode>` or preview `--changed --dry-run`; do not run full workspace publish by default.
144
144
  - Use `openxiangda form pull` or app snapshot before overwriting an existing form.
145
- - Before assuming a value shape (e.g. dates, attachments, member fields, option fields), verify against `../../references/platform-data-model.md` instead of guessing.
145
+ - Before assuming a value shape (e.g. dates, attachments, member fields, option fields), verify against `references/platform-data-model.md` instead of guessing.
@@ -59,6 +59,6 @@ Use logical local codes first. If a code is missing from `.openxiangda/state.jso
59
59
 
60
60
  ## References
61
61
 
62
- - `../../references/platform-data-model.md` — how each field type is persisted on the platform (JSONB, `{label, value}` for option fields, attachment shape, etc.). Consult this when a snapshot value looks unexpected so you can tell broken data from normal storage format.
63
- - `../../references/troubleshooting.md` — catalog of known failure modes (missing styles, `options` runtime errors, mismatched option labels, etc.). Cross-check inspection findings against this list before reporting an issue or proposing a fix.
62
+ - `references/platform-data-model.md` — how each field type is persisted on the platform (JSONB, `{label, value}` for option fields, attachment shape, etc.). Consult this when a snapshot value looks unexpected so you can tell broken data from normal storage format.
63
+ - `references/troubleshooting.md` — catalog of known failure modes (missing styles, `options` runtime errors, mismatched option labels, etc.). Cross-check inspection findings against this list before reporting an issue or proposing a fix.
64
64