openxiangda 1.0.88 → 1.0.89
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.
- package/README.md +2 -0
- package/openxiangda-skills/SKILL.md +2 -0
- package/openxiangda-skills/references/architecture-design.md +1 -0
- package/openxiangda-skills/references/openxiangda-api.md +2 -0
- package/openxiangda-skills/references/pages/page-sdk.md +2 -0
- package/openxiangda-skills/references/resource-manifest-cheatsheet.md +2 -0
- package/package.json +1 -1
- package/packages/sdk/dist/components/index.cjs +13 -2
- package/packages/sdk/dist/components/index.cjs.map +1 -1
- package/packages/sdk/dist/components/index.mjs +13 -2
- package/packages/sdk/dist/components/index.mjs.map +1 -1
- package/packages/sdk/dist/runtime/index.cjs +102 -21
- package/packages/sdk/dist/runtime/index.cjs.map +1 -1
- package/packages/sdk/dist/runtime/index.mjs +102 -21
- package/packages/sdk/dist/runtime/index.mjs.map +1 -1
- package/packages/sdk/dist/runtime/react.cjs +70 -11
- package/packages/sdk/dist/runtime/react.cjs.map +1 -1
- package/packages/sdk/dist/runtime/react.d.mts +1 -1
- package/packages/sdk/dist/runtime/react.d.ts +1 -1
- package/packages/sdk/dist/runtime/react.mjs +70 -11
- package/packages/sdk/dist/runtime/react.mjs.map +1 -1
package/README.md
CHANGED
|
@@ -178,6 +178,8 @@ React SPA 新应用的无需登录访问统一使用 `/view/:appType/public/*`
|
|
|
178
178
|
|
|
179
179
|
`mode: "ticket"` 的公开策略默认按单次 ticket 使用;只有明确配置 `ticketConfig.singleUse: false` 时才允许复用。新 public session 只返回 bearer token,SDK 会把 token 注入后续 runtime/bootstrap、dataView、function、connector 请求,不依赖认证 cookie。
|
|
180
180
|
|
|
181
|
+
公开访问和测试脚本不能只判断 HTTP 状态。平台部分运行时接口可能返回 HTTP 200,同时在 JSON envelope 中返回业务错误码,例如 `code: "PUBLIC_GRANT_DENIED"`。只有 `code` 为 `200`、`"200"` 或兼容成功码 `0` 时才算成功;字符串错误码、`success: false` 或非 2xx HTTP 都必须当作失败。
|
|
182
|
+
|
|
181
183
|
旧 `?publicAccess=guest` 和表单 `settings/forms/*.json` 里的 `publicAccess` 只用于旧 `sy-lowcode-view` 兼容。新 React SPA 应用不要再设计或生成这种链接。
|
|
182
184
|
|
|
183
185
|
```json
|
|
@@ -46,6 +46,7 @@ OpenXiangda supports two workspace modes. Classic `sy-lowcode-app-workspace` pub
|
|
|
46
46
|
- ✅ For non-trivial app requirements, run the architecture design gate first: forms, fields, pages, permissions, data views, status/workflow, automation, notifications, and development tasks must be decided before coding.
|
|
47
47
|
- ✅ For app login/auth requirements, run the auth security gate before coding: enabled methods, registration policy, identity matching keys, provider boundary, default roles, rate limits, audit fields, and third-party ownership must be confirmed.
|
|
48
48
|
- ✅ For public/no-login access requirements, run the public access design gate before coding: public routes, external role codes, guest vs ticket, form/dataView/function/connector grants, and backend permission groups must be confirmed. New React SPA apps use `/view/:appType/public/*`, `src/resources/routes/`, `src/resources/public-access/`, and `PublicAccessGate`.
|
|
49
|
+
- ✅ Public/no-login validation must inspect the JSON envelope, not only HTTP status. HTTP 200 with `code: "PUBLIC_GRANT_DENIED"` is a denied request; success requires `code` `200`, `"200"`, or compatible `0`, and no `success: false`.
|
|
49
50
|
- ✅ For form-entry UX, pick components in this order: OpenXiangda platform form components → `antd` / `antd-mobile` wrappers → custom component only when neither exists.
|
|
50
51
|
- ✅ List pages, linked options, remote selectors, and report drill-downs must use paginated server-side queries with explicit searchable fields. Do not fetch a large page and filter in local state.
|
|
51
52
|
- ✅ Treat workflow forms as an exception. Ordinary ticket/order/task/asset lifecycles use status fields, state machines, action logs, permissions, and automations; workflows are for real approval tasks.
|
|
@@ -149,6 +150,7 @@ When the user provides a root domain such as `https://yida.wisejob.cn/`, use it
|
|
|
149
150
|
- 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.
|
|
150
151
|
- For application login, declare auth resources in `src/resources/auth/<code>.json` and publish them with `openxiangda resource publish`. App Function auth providers may validate external credentials and return only an identity assertion; they must never issue tokens, set cookies, or write platform user/binding tables directly. The platform auth service decides create/bind/reject and issues tokens.
|
|
151
152
|
- For external/no-login pages in React SPA apps, declare `src/resources/routes/<code>.json` and `src/resources/public-access/<code>.json`, put the page under `/view/:appType/public/*`, and use `PublicAccessGate` or `createPublicAccessClient`. Public guest access to forms, dataViews, functions, and connectors is denied unless policy `grants` explicitly names the resource; backend permission groups still apply.
|
|
153
|
+
- When verifying public access, parse the response body and reject string business errors such as `PUBLIC_GRANT_DENIED` even if the HTTP status is 200. Do not use `response.ok` alone as the success condition.
|
|
152
154
|
- 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.
|
|
153
155
|
- 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.
|
|
154
156
|
- Use `openxiangda app snapshot <APP_XXX> --profile <name> --json` for diagnosis before changing an existing app.
|
|
@@ -265,6 +265,7 @@ For simple CRUD/admin lists, design can proceed with the appropriate OpenXiangda
|
|
|
265
265
|
- Policy `externalRoleCodes` are virtual role codes carried by the scoped public token; use the same codes in page/form/dataView permission groups.
|
|
266
266
|
- Policy `grants` must list every form/dataView/function/connector the public page can access. Do not rely on broad guest permissions.
|
|
267
267
|
- Use `PublicAccessGate` in React routes or `createPublicAccessClient` for custom bootstrapping.
|
|
268
|
+
- In validation scripts, success requires the JSON envelope success code (`200`, `"200"`, or compatible `0`). HTTP 200 with `code: "PUBLIC_GRANT_DENIED"` is a denied request, not a success.
|
|
268
269
|
- Old `?publicAccess=guest` and form `publicAccess` settings are legacy compatibility and should not appear in new-app designs.
|
|
269
270
|
|
|
270
271
|
## Final Document Template
|
|
@@ -189,6 +189,8 @@ Requires Bearer token. Deletes legacy public/guest access config for the form.
|
|
|
189
189
|
|
|
190
190
|
New React SPA apps use `/view/:appType/public/*`, route resources, public access policies, and a scoped public session. Do not use `?publicAccess=guest` for new apps.
|
|
191
191
|
|
|
192
|
+
Runtime APIs use a JSON envelope. Do not treat HTTP 200 alone as success: `code: "PUBLIC_GRANT_DENIED"` and any other string error code is a failure even when the HTTP status is 200. Validation scripts should require `code === 200`, `code === "200"`, or compatible success code `0`, and should also fail on `success: false`.
|
|
193
|
+
|
|
192
194
|
### GET `/apps/:appType/routes`
|
|
193
195
|
|
|
194
196
|
Requires Bearer token. Lists route resources.
|
|
@@ -176,6 +176,8 @@ await publicAccess.startSession({
|
|
|
176
176
|
|
|
177
177
|
`PublicAccessGate` stores the returned bearer token in the runtime provider and injects it into follow-up SDK requests. If you call `createPublicAccessClient` directly outside the provider, pass the returned `accessToken` as `Authorization: Bearer <token>` for follow-up APIs. The matching resources must exist under `src/resources/routes/` and `src/resources/public-access/`. Public guest access to forms, data views, functions, and connectors is denied unless the policy `grants` explicitly names that resource.
|
|
178
178
|
|
|
179
|
+
When testing public pages or writing direct fetch wrappers, check the JSON envelope, not only `response.ok`. Platform runtime APIs can return HTTP 200 with `code: "PUBLIC_GRANT_DENIED"`; this is a failed request. Treat only `code === 200`, `code === "200"`, or compatible success code `0` as success, and fail on `success: false`.
|
|
180
|
+
|
|
179
181
|
Logout and current-user role switching:
|
|
180
182
|
|
|
181
183
|
```ts
|
|
@@ -126,6 +126,8 @@ import { PublicAccessGate } from "openxiangda/runtime/react";
|
|
|
126
126
|
</PublicAccessGate>
|
|
127
127
|
```
|
|
128
128
|
|
|
129
|
+
公开访问验证必须检查 JSON envelope。HTTP 200 但 `code: "PUBLIC_GRANT_DENIED"` 代表后端已拒绝;只有 `code` 为 `200`、`"200"` 或兼容成功码 `0`,且没有 `success: false` 时才算成功。
|
|
130
|
+
|
|
129
131
|
## 2. Public Access Policy — `src/resources/public-access/<code>.json`
|
|
130
132
|
|
|
131
133
|
公开策略声明外部角色和可访问资源。未显式 grant 的 form/dataView/function/connector 默认拒绝;grant 后仍受对应后端权限组控制。
|
package/package.json
CHANGED
|
@@ -39151,6 +39151,11 @@ var joinUrl = (baseUrl, url2) => {
|
|
|
39151
39151
|
if (/^https?:\/\//i.test(url2)) return url2;
|
|
39152
39152
|
return `${trimTrailingSlash(baseUrl)}${url2.startsWith("/") ? url2 : `/${url2}`}`;
|
|
39153
39153
|
};
|
|
39154
|
+
var isSuccessCode = (value) => {
|
|
39155
|
+
if (value === void 0 || value === null || value === "") return true;
|
|
39156
|
+
const code = Number(value);
|
|
39157
|
+
return Number.isFinite(code) ? code === 0 || code >= 200 && code < 300 : false;
|
|
39158
|
+
};
|
|
39154
39159
|
var normalizeRuntimeFileUrl = (baseUrl, value) => {
|
|
39155
39160
|
if (typeof value !== "string" || !value) return value;
|
|
39156
39161
|
if (/^(https?:)?\/\//i.test(value) || /^(blob|data):/i.test(value)) return value;
|
|
@@ -39177,6 +39182,12 @@ var parseResponse = async (response) => {
|
|
|
39177
39182
|
const message6 = typeof payload === "object" && payload ? payload.message || payload.error || response.statusText : response.statusText;
|
|
39178
39183
|
throw new Error(message6 || "\u8BF7\u6C42\u5931\u8D25");
|
|
39179
39184
|
}
|
|
39185
|
+
if (typeof payload === "object" && payload) {
|
|
39186
|
+
const hasCode = Object.prototype.hasOwnProperty.call(payload, "code");
|
|
39187
|
+
if (payload.success === false || hasCode && !isSuccessCode(payload.code)) {
|
|
39188
|
+
throw new Error(payload.message || payload.error || "\u8BF7\u6C42\u5931\u8D25");
|
|
39189
|
+
}
|
|
39190
|
+
}
|
|
39180
39191
|
if (typeof payload === "object" && payload) {
|
|
39181
39192
|
return payload;
|
|
39182
39193
|
}
|
|
@@ -40920,14 +40931,14 @@ var TASK_STATUS_META = {
|
|
|
40920
40931
|
init_cjs_shims();
|
|
40921
40932
|
var hasOwn = (value, key) => Object.prototype.hasOwnProperty.call(value, key);
|
|
40922
40933
|
var isRuntimeEnvelope = (value) => !!value && typeof value === "object" && !Array.isArray(value) && (hasOwn(value, "code") || hasOwn(value, "success") || hasOwn(value, "data") || hasOwn(value, "result") || hasOwn(value, "message") || hasOwn(value, "error"));
|
|
40923
|
-
var
|
|
40934
|
+
var isSuccessCode2 = (code) => {
|
|
40924
40935
|
if (code === void 0 || code === null || code === "") return true;
|
|
40925
40936
|
const normalized = Number(code);
|
|
40926
40937
|
return Number.isFinite(normalized) ? normalized === 0 || normalized === 200 : false;
|
|
40927
40938
|
};
|
|
40928
40939
|
var unwrapRuntimeResponse = (response) => {
|
|
40929
40940
|
if (!isRuntimeEnvelope(response)) return response;
|
|
40930
|
-
if (response.success === false || !
|
|
40941
|
+
if (response.success === false || !isSuccessCode2(response.code)) {
|
|
40931
40942
|
throw new Error(response.message || response.error || "\u8BF7\u6C42\u5931\u8D25");
|
|
40932
40943
|
}
|
|
40933
40944
|
if (hasOwn(response, "data")) return response.data;
|