openxiangda 1.0.87 → 1.0.88

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 CHANGED
@@ -176,6 +176,8 @@ const affiliatedDepartmentExternalId = user?.affiliatedDepartment?.externalId
176
176
 
177
177
  React SPA 新应用的无需登录访问统一使用 `/view/:appType/public/*`。应用在 `src/resources/routes/*.json` 声明公开路由,在 `src/resources/public-access/*.json` 声明公开策略、外部角色和可访问资源 grant,页面用 `PublicAccessGate` 或 `createPublicAccessClient` 创建 scoped public session。公开 guest 的 form、dataView、function、connector 默认全部拒绝,只有 policy `grants` 明确列出的资源可访问,并且仍受对应表单权限组、dataView 权限组等后端权限控制。
178
178
 
179
+ `mode: "ticket"` 的公开策略默认按单次 ticket 使用;只有明确配置 `ticketConfig.singleUse: false` 时才允许复用。新 public session 只返回 bearer token,SDK 会把 token 注入后续 runtime/bootstrap、dataView、function、connector 请求,不依赖认证 cookie。
180
+
179
181
  旧 `?publicAccess=guest` 和表单 `settings/forms/*.json` 里的 `publicAccess` 只用于旧 `sy-lowcode-view` 兼容。新 React SPA 应用不要再设计或生成这种链接。
180
182
 
181
183
  ```json
@@ -263,10 +263,12 @@ Body:
263
263
  ```json
264
264
  {
265
265
  "expiresAt": "2026-06-18T12:00:00.000Z",
266
- "subject": { "usage": "single-use" }
266
+ "subject": { "scenario": "score-link" }
267
267
  }
268
268
  ```
269
269
 
270
+ Tickets are single-use by default. Set policy `ticketConfig.singleUse: false` only when the link is intentionally reusable.
271
+
270
272
  ### POST `/apps/:appType/public/session`
271
273
 
272
274
  Does not require an existing login. Creates a scoped public guest session.
@@ -285,6 +287,8 @@ Body:
285
287
  }
286
288
  ```
287
289
 
290
+ The response returns a scoped bearer token. New React SPA apps must use `PublicAccessGate` or `createPublicAccessClient` so follow-up runtime/bootstrap, dataView, function, and connector calls include `Authorization: Bearer <token>`. Do not depend on auth cookies for public sessions.
291
+
288
292
  Returns the normal guest token payload plus `extra.publicAccess`.
289
293
 
290
294
  ### GET `/apps/:appType/menus`
@@ -174,7 +174,7 @@ await publicAccess.startSession({
174
174
  });
175
175
  ```
176
176
 
177
- 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.
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
179
  Logout and current-user role switching:
180
180
 
@@ -157,13 +157,15 @@ Ticket 模式:
157
157
  "routeCode": "public.scoreLookup",
158
158
  "pathPattern": "/view/:appType/public/score",
159
159
  "externalRoleCodes": ["external_ticket_holder"],
160
- "ticketConfig": { "ttlSeconds": 1800 },
160
+ "ticketConfig": { "ttlSeconds": 1800, "singleUse": true },
161
161
  "grants": {
162
162
  "dataViews": ["public_score_lookup"]
163
163
  }
164
164
  }
165
165
  ```
166
166
 
167
+ Ticket 默认单次使用,首次换取 public session 后立即失效;只有明确配置 `ticketConfig.singleUse: false` 的业务场景才允许复用。
168
+
167
169
  `grants.forms` 可以写本地 `formCode`,CLI 发布时会解析为真实 `formUuid`。
168
170
 
169
171
  ## 3. Connector — `src/resources/connectors/<code>.json`
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openxiangda",
3
- "version": "1.0.87",
3
+ "version": "1.0.88",
4
4
  "description": "OpenXiangda CLI, workspace build tools, runtime SDK, and form components.",
5
5
  "private": false,
6
6
  "bin": {
@@ -4102,12 +4102,23 @@ var OpenXiangdaProvider = ({
4102
4102
  () => appType || resolveAppTypeFromLocation(),
4103
4103
  [appType]
4104
4104
  );
4105
+ const [accessToken, setAccessTokenState] = (0, import_react8.useState)(null);
4106
+ const setAccessToken = (0, import_react8.useCallback)(
4107
+ (nextAccessToken) => {
4108
+ setAccessTokenState(nextAccessToken || null);
4109
+ },
4110
+ []
4111
+ );
4112
+ const authorizedFetch = (0, import_react8.useMemo)(
4113
+ () => createAuthorizedFetch(resolvedFetch, accessToken),
4114
+ [accessToken, resolvedFetch]
4115
+ );
4105
4116
  const [state, setState] = (0, import_react8.useState)({
4106
4117
  data: null,
4107
4118
  loading: true,
4108
4119
  error: null
4109
4120
  });
4110
- const reload = (0, import_react8.useCallback)(async () => {
4121
+ const reload = (0, import_react8.useCallback)(async (options = {}) => {
4111
4122
  if (!resolvedAppType) {
4112
4123
  setState({
4113
4124
  data: null,
@@ -4120,8 +4131,9 @@ var OpenXiangdaProvider = ({
4120
4131
  return;
4121
4132
  }
4122
4133
  setState((prev) => ({ ...prev, loading: true, error: null }));
4134
+ const requestFetch = options.accessToken !== void 0 ? createAuthorizedFetch(resolvedFetch, options.accessToken || null) : authorizedFetch;
4123
4135
  try {
4124
- const response = await resolvedFetch(
4136
+ const response = await requestFetch(
4125
4137
  buildServiceUrl3(
4126
4138
  servicePrefix,
4127
4139
  `/openxiangda-api/v1/apps/${encodeURIComponent(
@@ -4153,7 +4165,7 @@ var OpenXiangdaProvider = ({
4153
4165
  error: normalizeRuntimeError(error)
4154
4166
  });
4155
4167
  }
4156
- }, [resolvedAppType, resolvedFetch, servicePrefix]);
4168
+ }, [authorizedFetch, resolvedAppType, resolvedFetch, servicePrefix]);
4157
4169
  (0, import_react8.useEffect)(() => {
4158
4170
  void reload();
4159
4171
  }, [reload]);
@@ -4162,10 +4174,11 @@ var OpenXiangdaProvider = ({
4162
4174
  ...state,
4163
4175
  appType: resolvedAppType,
4164
4176
  servicePrefix,
4165
- fetchImpl: resolvedFetch,
4166
- reload
4177
+ fetchImpl: authorizedFetch,
4178
+ reload,
4179
+ setAccessToken
4167
4180
  }),
4168
- [reload, resolvedAppType, resolvedFetch, servicePrefix, state]
4181
+ [authorizedFetch, reload, resolvedAppType, servicePrefix, state]
4169
4182
  );
4170
4183
  return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(OpenXiangdaRuntimeContext.Provider, { value, children });
4171
4184
  };
@@ -4432,6 +4445,19 @@ var buildServiceUrl3 = (servicePrefix, path) => {
4432
4445
  const suffix = path.startsWith("/") ? path : `/${path}`;
4433
4446
  return `${prefix2}${suffix}`;
4434
4447
  };
4448
+ var createAuthorizedFetch = (baseFetch, accessToken) => {
4449
+ if (!accessToken) return baseFetch;
4450
+ return ((input, init = {}) => {
4451
+ const headers = new Headers(init.headers || {});
4452
+ if (!headers.has("authorization")) {
4453
+ headers.set("authorization", `Bearer ${accessToken}`);
4454
+ }
4455
+ return baseFetch(input, {
4456
+ ...init,
4457
+ headers
4458
+ });
4459
+ });
4460
+ };
4435
4461
  var readJsonPayload = async (response) => {
4436
4462
  try {
4437
4463
  return await response.json();
@@ -4555,7 +4581,8 @@ var usePublicAccess = (options = {}) => {
4555
4581
  appType: runtimeAppType,
4556
4582
  servicePrefix: runtimeServicePrefix,
4557
4583
  fetchImpl: runtimeFetchImpl,
4558
- reload: reloadRuntime
4584
+ reload: reloadRuntime,
4585
+ setAccessToken
4559
4586
  } = runtime;
4560
4587
  const {
4561
4588
  appType = runtimeAppType,
@@ -4589,7 +4616,8 @@ var usePublicAccess = (options = {}) => {
4589
4616
  path: input.path || stableSessionInput.path || readPathFromLocation()
4590
4617
  });
4591
4618
  setSession(data);
4592
- await reloadRuntime();
4619
+ setAccessToken(data.accessToken);
4620
+ await reloadRuntime({ accessToken: data.accessToken });
4593
4621
  setLoading(false);
4594
4622
  return data;
4595
4623
  } catch (caught) {
@@ -4602,7 +4630,7 @@ var usePublicAccess = (options = {}) => {
4602
4630
  throw nextError;
4603
4631
  }
4604
4632
  },
4605
- [client, reloadRuntime, stableSessionInput]
4633
+ [client, reloadRuntime, setAccessToken, stableSessionInput]
4606
4634
  );
4607
4635
  (0, import_react9.useEffect)(() => {
4608
4636
  if (!autoStart) return;