openxiangda 1.0.122 → 1.0.124

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.
@@ -61,6 +61,27 @@ await sdk.notification.sendByType({
61
61
  });
62
62
  ```
63
63
 
64
+ User-facing in-app message centers should read the platform inbox instead of
65
+ creating app-specific message/log forms:
66
+
67
+ ```ts
68
+ const inbox = await sdk.notification.listInbox({
69
+ page: 1,
70
+ limit: 20,
71
+ readStatus: "unread",
72
+ });
73
+
74
+ await sdk.notification.markRead(messageId);
75
+ await sdk.notification.markAllRead();
76
+
77
+ const unread = await sdk.notification.getUnreadCount();
78
+ ```
79
+
80
+ Inbox APIs are app-scoped and only return the current logged-in user's `inapp`
81
+ messages. Use `keyword`, `templateCode`, and `readStatus` for server-side
82
+ filtering. Do not expose `/api/message/history` to normal users; it is an
83
+ administrative send-history surface.
84
+
64
85
  CLI smoke tests:
65
86
 
66
87
  ```bash
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openxiangda",
3
- "version": "1.0.122",
3
+ "version": "1.0.124",
4
4
  "description": "OpenXiangda CLI, workspace build tools, runtime SDK, and form components.",
5
5
  "private": false,
6
6
  "bin": {
@@ -2445,6 +2445,45 @@ var createPageSdk = (context) => {
2445
2445
  ...params,
2446
2446
  appType: resolveAppType(context, params.appType)
2447
2447
  }
2448
+ }),
2449
+ listInbox: (params = {}) => request({
2450
+ path: buildOpenXiangdaAppPath(
2451
+ context,
2452
+ params.appType,
2453
+ "/notifications/inbox"
2454
+ ),
2455
+ method: "get",
2456
+ query: {
2457
+ page: params.page,
2458
+ limit: params.limit,
2459
+ readStatus: params.readStatus,
2460
+ keyword: params.keyword,
2461
+ templateCode: params.templateCode
2462
+ }
2463
+ }),
2464
+ getUnreadCount: (params = {}) => request({
2465
+ path: buildOpenXiangdaAppPath(
2466
+ context,
2467
+ params.appType,
2468
+ "/notifications/inbox/unread-count"
2469
+ ),
2470
+ method: "get"
2471
+ }),
2472
+ markRead: (messageId, params = {}) => request({
2473
+ path: buildOpenXiangdaAppPath(
2474
+ context,
2475
+ params.appType,
2476
+ `/notifications/inbox/${encodePathSegment(messageId)}/read`
2477
+ ),
2478
+ method: "post"
2479
+ }),
2480
+ markAllRead: (params = {}) => request({
2481
+ path: buildOpenXiangdaAppPath(
2482
+ context,
2483
+ params.appType,
2484
+ "/notifications/inbox/read-all"
2485
+ ),
2486
+ method: "post"
2448
2487
  })
2449
2488
  };
2450
2489
  const sdk = {
@@ -35317,10 +35356,18 @@ var OpenXiangdaProvider = ({
35317
35356
  const [accessToken, setAccessTokenState] = (0, import_react216.useState)(null);
35318
35357
  const accessTokenRef = (0, import_react216.useRef)(null);
35319
35358
  const setAccessToken = (0, import_react216.useCallback)(
35320
- (nextAccessToken) => {
35359
+ (nextAccessToken, options = {}) => {
35360
+ if (!nextAccessToken && options.clearIfToken && accessTokenRef.current?.token !== options.clearIfToken) {
35361
+ return;
35362
+ }
35321
35363
  const normalizedAccessToken = nextAccessToken || null;
35322
- accessTokenRef.current = normalizedAccessToken;
35323
- setAccessTokenState(normalizedAccessToken);
35364
+ const nextState = normalizedAccessToken ? {
35365
+ token: normalizedAccessToken,
35366
+ scope: options.scope || "default",
35367
+ path: options.path || null
35368
+ } : null;
35369
+ accessTokenRef.current = nextState;
35370
+ setAccessTokenState(nextState);
35324
35371
  },
35325
35372
  []
35326
35373
  );
@@ -35346,7 +35393,13 @@ var OpenXiangdaProvider = ({
35346
35393
  return;
35347
35394
  }
35348
35395
  setState((prev) => ({ ...prev, loading: true, error: null }));
35349
- const requestFetch = options.accessToken !== void 0 ? createAuthorizedFetch(resolvedFetch, options.accessToken || null) : createAuthorizedFetch(resolvedFetch, accessTokenRef.current);
35396
+ const requestFetch = options.accessToken !== void 0 ? createAuthorizedFetch(
35397
+ resolvedFetch,
35398
+ createAccessTokenState(
35399
+ options.accessToken || null,
35400
+ options.accessTokenOptions
35401
+ )
35402
+ ) : createAuthorizedFetch(resolvedFetch, accessTokenRef.current);
35350
35403
  try {
35351
35404
  const response = await requestFetch(
35352
35405
  buildServiceUrl3(
@@ -35756,9 +35809,11 @@ var buildServiceUrl3 = (servicePrefix, path) => {
35756
35809
  var createAuthorizedFetch = (baseFetch, accessToken) => {
35757
35810
  if (!accessToken) return baseFetch;
35758
35811
  return ((input, init = {}) => {
35812
+ const token = resolveAccessTokenForCurrentRoute(accessToken);
35813
+ if (!token) return baseFetch(input, init);
35759
35814
  const headers = new Headers(init.headers || {});
35760
35815
  if (!headers.has("authorization")) {
35761
- headers.set("authorization", `Bearer ${accessToken}`);
35816
+ headers.set("authorization", `Bearer ${token}`);
35762
35817
  }
35763
35818
  return baseFetch(input, {
35764
35819
  ...init,
@@ -35766,6 +35821,34 @@ var createAuthorizedFetch = (baseFetch, accessToken) => {
35766
35821
  });
35767
35822
  });
35768
35823
  };
35824
+ var createAccessTokenState = (accessToken, options = {}) => accessToken ? {
35825
+ token: accessToken,
35826
+ scope: options.scope || "default",
35827
+ path: options.path || null
35828
+ } : null;
35829
+ var resolveAccessTokenForCurrentRoute = (accessToken) => {
35830
+ if (accessToken.scope !== "public") return accessToken.token;
35831
+ const scopedPath = normalizeRoutePath(accessToken.path);
35832
+ const currentPath = normalizeRoutePath(getCurrentPathname2());
35833
+ if (!scopedPath) {
35834
+ return isPublicRoutePath(currentPath) ? accessToken.token : null;
35835
+ }
35836
+ if (currentPath === scopedPath || isPublicRoutePath(currentPath) && currentPath.startsWith(`${scopedPath}/`)) {
35837
+ return accessToken.token;
35838
+ }
35839
+ return null;
35840
+ };
35841
+ var normalizeRoutePath = (path) => {
35842
+ const text = String(path || "").trim();
35843
+ if (!text) return "";
35844
+ try {
35845
+ const base = typeof window !== "undefined" ? window.location.origin : "http://localhost";
35846
+ return new URL(text, base).pathname.replace(/\/+$/, "") || "/";
35847
+ } catch {
35848
+ return text.split(/[?#]/, 1)[0].replace(/\/+$/, "") || "/";
35849
+ }
35850
+ };
35851
+ var isPublicRoutePath = (path) => /\/public(?:\/|$)/.test(path);
35769
35852
  var readJsonPayload = async (response) => {
35770
35853
  try {
35771
35854
  return await response.json();
@@ -35866,6 +35949,7 @@ var attachCallback = (loginUrl, callback) => {
35866
35949
  }
35867
35950
  };
35868
35951
  var getCurrentHref4 = () => typeof window === "undefined" ? "" : window.location.href;
35952
+ var getCurrentPathname2 = () => typeof window === "undefined" ? "" : window.location.pathname;
35869
35953
  var getCurrentHostname2 = () => typeof window === "undefined" ? "" : window.location.hostname;
35870
35954
  var getRuntimeEnv = (key) => {
35871
35955
  const env = typeof process !== "undefined" ? process.env : void 0;
@@ -35958,6 +36042,8 @@ var usePublicAccess = (options = {}) => {
35958
36042
  const [session, setSession] = (0, import_react217.useState)(null);
35959
36043
  const [error, setError] = (0, import_react217.useState)(null);
35960
36044
  const [loading, setLoading] = (0, import_react217.useState)(Boolean(autoStart));
36045
+ const activeSessionRef = (0, import_react217.useRef)(null);
36046
+ const mountedRef = (0, import_react217.useRef)(true);
35961
36047
  const sessionInputKey = JSON.stringify(sessionInput);
35962
36048
  const stableSessionInput = (0, import_react217.useMemo)(() => sessionInput, [sessionInputKey]);
35963
36049
  const client = (0, import_react217.useMemo)(
@@ -35973,15 +36059,31 @@ var usePublicAccess = (options = {}) => {
35973
36059
  setLoading(true);
35974
36060
  setError(null);
35975
36061
  try {
36062
+ const resolvedPath = input.path || stableSessionInput.path || readPathFromLocation();
35976
36063
  const data = await client.startSession({
35977
36064
  ...stableSessionInput,
35978
36065
  ...input,
35979
36066
  ticket: input.ticket || stableSessionInput.ticket || readTicketFromLocation(),
35980
- path: input.path || stableSessionInput.path || readPathFromLocation()
36067
+ path: resolvedPath
35981
36068
  });
36069
+ if (!mountedRef.current) return data;
36070
+ activeSessionRef.current = {
36071
+ accessToken: data.accessToken,
36072
+ path: resolvedPath
36073
+ };
35982
36074
  setSession(data);
35983
- setAccessToken(data.accessToken);
35984
- await reloadRuntime({ accessToken: data.accessToken });
36075
+ setAccessToken(data.accessToken, {
36076
+ scope: "public",
36077
+ path: resolvedPath
36078
+ });
36079
+ await reloadRuntime({
36080
+ accessToken: data.accessToken,
36081
+ accessTokenOptions: {
36082
+ scope: "public",
36083
+ path: resolvedPath
36084
+ }
36085
+ });
36086
+ if (!mountedRef.current) return data;
35985
36087
  setLoading(false);
35986
36088
  return data;
35987
36089
  } catch (caught) {
@@ -35989,8 +36091,10 @@ var usePublicAccess = (options = {}) => {
35989
36091
  caught instanceof Error ? caught.message : "\u516C\u5F00\u8BBF\u95EE\u4F1A\u8BDD\u521B\u5EFA\u5931\u8D25",
35990
36092
  { payload: caught }
35991
36093
  );
35992
- setError(nextError);
35993
- setLoading(false);
36094
+ if (mountedRef.current) {
36095
+ setError(nextError);
36096
+ setLoading(false);
36097
+ }
35994
36098
  throw nextError;
35995
36099
  }
35996
36100
  },
@@ -36000,6 +36104,20 @@ var usePublicAccess = (options = {}) => {
36000
36104
  if (!autoStart) return;
36001
36105
  void startSession().catch(() => void 0);
36002
36106
  }, [autoStart, startSession]);
36107
+ (0, import_react217.useEffect)(
36108
+ () => {
36109
+ mountedRef.current = true;
36110
+ return () => {
36111
+ mountedRef.current = false;
36112
+ const activeSession = activeSessionRef.current;
36113
+ if (!activeSession) return;
36114
+ activeSessionRef.current = null;
36115
+ setAccessToken(null, { clearIfToken: activeSession.accessToken });
36116
+ void reloadRuntime({ accessToken: null });
36117
+ };
36118
+ },
36119
+ [reloadRuntime, setAccessToken]
36120
+ );
36003
36121
  return {
36004
36122
  loading,
36005
36123
  error,