openxiangda 1.0.247 → 1.0.249

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
@@ -135,7 +135,7 @@ React SPA workspaces publish their frontend with `openxiangda runtime deploy`. E
135
135
 
136
136
  Environment-managed `release ship`, recovery `release deploy`, and `release promote` accept the same audited Runtime rollback pair. It is forwarded only to `runtime-stage`, never to resource stages or `app-finalize`. Ship freezes the intent in its private journal so production confirmation can inherit it automatically; without the explicit pair, lineage divergence remains blocked.
137
137
 
138
- Before publishing the OpenXiangda npm package, run `npm run test:release` once on the final clean version commit. It writes hash-bound evidence under `.openxiangda/evidence/tests/<HEAD>/release.json`. `npm publish` validates that evidence against the exact Git commit, package version, Node version, and complete passing result, then runs the mainline guard; it does not execute the full release suite a second time. Any commit, version, runtime, result, or evidence-hash drift remains fail closed.
138
+ Before publishing the OpenXiangda npm package, run `npm run test:release` once on the final clean version commit. For an AdminList-only SDK patch, use the fixed `npm run test:release:sdk-admin-list` profile instead; it runs the AdminList state and response contracts plus the release-evidence guard without exercising unrelated deployment orchestration. Both commands write hash-bound evidence under `.openxiangda/evidence/tests/<HEAD>/release.json`. `npm publish` validates that evidence against the exact Git commit, package version, Node version, and complete passing result, then runs the mainline guard; it does not execute the selected suite a second time. Unknown or cross-module changes should continue to use the full release suite. Any commit, version, runtime, result, or evidence-hash drift remains fail closed.
139
139
 
140
140
  Parallel tasks develop and test in isolated worktrees, but feature worktrees do not publish. After approved commits are merged and pushed, `sdd bundle <release-change> --changes ...` unions their exact structured scope and preserves the source changes' common Git baseline instead of adopting the post-merge `HEAD`. Old imported changes that predate source-base metadata must pass `--source-base-ref <commit>` explicitly. Commit/push the bundle, then run `release publish --change <release-change> --profile <name>` from a clean local main/master whose commit exactly equals the authoritative remote tip. It verifies without rewriting reviewed SDD files, waits for the app lease, freezes one authoritative App capture, executes exact Form/Backend/Runtime staged steps, atomically finalizes the Root App release, and records a resumable local execution journal. Runtime checks use the narrow head endpoint; immutable Git-base artifact hashes are reused across plans.
141
141
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openxiangda",
3
- "version": "1.0.247",
3
+ "version": "1.0.249",
4
4
  "description": "OpenXiangda CLI, workspace build tools, runtime SDK, and form components.",
5
5
  "private": false,
6
6
  "bin": {
@@ -72,6 +72,7 @@
72
72
  "test:changed": "npm run check && node scripts/run-test-suite.mjs --changed --evidence",
73
73
  "test:contract": "npm run check && node scripts/run-test-suite.mjs --contract --evidence",
74
74
  "test:release": "npm run check && node scripts/run-test-suite.mjs --release --evidence",
75
+ "test:release:sdk-admin-list": "npm run check && node scripts/run-test-suite.mjs --release-profile sdk-admin-list --evidence",
75
76
  "test:critical": "npm run test:release",
76
77
  "test:all": "node scripts/run-test-suite.mjs --all --evidence",
77
78
  "test:full": "npm run test:all",
@@ -91,6 +92,7 @@
91
92
  "test:resource-cli": "node scripts/resource-cli-smoke.mjs",
92
93
  "test:permission-snapshot": "node scripts/permission-snapshot-smoke.mjs",
93
94
  "test:permission-role-users": "node scripts/permission-role-users-smoke.mjs",
95
+ "test:sdk-admin-list": "vitest run packages/sdk/src/runtime/react/admin-list/AdminList.state.test.tsx packages/sdk/src/runtime/react/admin-list/AdminList.selection.test.tsx",
94
96
  "test:sdk-bundle": "node scripts/sdk-browser-bundle-smoke.mjs",
95
97
  "test:sdk-list-response": "vitest run packages/sdk/src/runtime/react/admin-list/dataSources.test.ts packages/sdk/src/components/core/dataManagementApi.test.ts",
96
98
  "test:api-contract": "node scripts/openxiangda-api-contract-smoke.mjs",
@@ -44484,6 +44484,7 @@ function AdminList(props) {
44484
44484
  const [notificationApi, notificationHolder] = import_antd12.notification.useNotification();
44485
44485
  const [modalApi, modalHolder] = import_antd12.Modal.useModal();
44486
44486
  const sourceKey = dataSource.key || "anonymous";
44487
+ const listIdentity = JSON.stringify([listKey, sourceKey]);
44487
44488
  const [rawPreference, setRawPreference] = (0, import_react21.useState)(null);
44488
44489
  const [preference, setPreference] = (0, import_react21.useState)(
44489
44490
  () => mergeAdminListPreference({
@@ -44503,7 +44504,8 @@ function AdminList(props) {
44503
44504
  const [settingsTab, setSettingsTab] = (0, import_react21.useState)("search");
44504
44505
  const [columnSearch, setColumnSearch] = (0, import_react21.useState)("");
44505
44506
  const [expanded, setExpanded] = (0, import_react21.useState)(false);
44506
- const [ready, setReady] = (0, import_react21.useState)(false);
44507
+ const [readyIdentity, setReadyIdentity] = (0, import_react21.useState)(null);
44508
+ const ready = readyIdentity === listIdentity;
44507
44509
  const [rows, setRows] = (0, import_react21.useState)([]);
44508
44510
  const [total, setTotal] = (0, import_react21.useState)(0);
44509
44511
  const [allowedColumnKeys, setAllowedColumnKeys] = (0, import_react21.useState)(void 0);
@@ -44526,9 +44528,12 @@ function AdminList(props) {
44526
44528
  const [draggedSearchKey, setDraggedSearchKey] = (0, import_react21.useState)("");
44527
44529
  const selectedKeysRef = (0, import_react21.useRef)([]);
44528
44530
  const dataSourceRef = (0, import_react21.useRef)(dataSource);
44531
+ const listIdentityRef = (0, import_react21.useRef)(listIdentity);
44532
+ const requestVersionRef = (0, import_react21.useRef)(0);
44529
44533
  const onQueryChangeRef = (0, import_react21.useRef)(onQueryChange);
44530
44534
  const onSelectionChangeRef = (0, import_react21.useRef)(onSelectionChange);
44531
44535
  dataSourceRef.current = dataSource;
44536
+ listIdentityRef.current = listIdentity;
44532
44537
  onQueryChangeRef.current = onQueryChange;
44533
44538
  onSelectionChangeRef.current = onSelectionChange;
44534
44539
  const definitionFingerprint = JSON.stringify({
@@ -44599,10 +44604,19 @@ function AdminList(props) {
44599
44604
  }, [rowKey, rowSelectable, rows]);
44600
44605
  (0, import_react21.useEffect)(() => {
44601
44606
  let active = true;
44602
- setReady(false);
44607
+ const initializationVersion = ++requestVersionRef.current;
44608
+ setReadyIdentity(null);
44609
+ setRows([]);
44610
+ setTotal(0);
44611
+ setAllowedColumnKeys(void 0);
44612
+ setHiddenFieldsCount(0);
44613
+ setLoading(false);
44614
+ setError(null);
44603
44615
  const load = async () => {
44604
44616
  const saved = dataSourceRef.current.preferences ? await dataSourceRef.current.preferences.get(listKey).catch(() => null) : null;
44605
- if (!active) return;
44617
+ if (!active || initializationVersion !== requestVersionRef.current || listIdentityRef.current !== listIdentity) {
44618
+ return;
44619
+ }
44606
44620
  setRawPreference(saved);
44607
44621
  const merged = mergeAdminListPreference({
44608
44622
  version: configVersion,
@@ -44630,7 +44644,7 @@ function AdminList(props) {
44630
44644
  selectedKeysRef.current = [];
44631
44645
  setSelectedKeys([]);
44632
44646
  previousFingerprintRef.current = "";
44633
- setReady(true);
44647
+ setReadyIdentity(listIdentity);
44634
44648
  };
44635
44649
  void load();
44636
44650
  return () => {
@@ -44642,8 +44656,7 @@ function AdminList(props) {
44642
44656
  defaultVisibleSearchCount,
44643
44657
  definitionFingerprint,
44644
44658
  form,
44645
- listKey,
44646
- sourceKey
44659
+ listIdentity
44647
44660
  ]);
44648
44661
  (0, import_react21.useEffect)(() => {
44649
44662
  if (!allowedColumnKeys) return;
@@ -44678,28 +44691,34 @@ function AdminList(props) {
44678
44691
  ]);
44679
44692
  const loadData = (0, import_react21.useCallback)(async () => {
44680
44693
  if (!ready) return;
44694
+ const requestVersion = ++requestVersionRef.current;
44695
+ const requestedIdentity = listIdentity;
44696
+ const requestedDataSource = dataSourceRef.current;
44697
+ const isCurrentRequest = () => requestVersion === requestVersionRef.current && requestedIdentity === listIdentityRef.current;
44681
44698
  setLoading(true);
44682
44699
  setError(null);
44683
44700
  onQueryChangeRef.current?.(query);
44684
44701
  try {
44685
- const result = await dataSourceRef.current.query(query);
44702
+ const result = await requestedDataSource.query(query);
44703
+ if (!isCurrentRequest()) return;
44686
44704
  setRows(result.rows || []);
44687
44705
  setTotal(Math.max(0, Number(result.total || 0)));
44688
44706
  setAllowedColumnKeys(result.allowedColumnKeys);
44689
44707
  setHiddenFieldsCount(Math.max(0, Number(result.hiddenFieldsCount || 0)));
44690
44708
  } catch (requestError) {
44709
+ if (!isCurrentRequest()) return;
44691
44710
  setRows([]);
44692
44711
  setTotal(0);
44693
44712
  setError(
44694
44713
  requestError instanceof Error ? requestError : new Error(String(requestError || "\u6570\u636E\u52A0\u8F7D\u5931\u8D25"))
44695
44714
  );
44696
44715
  } finally {
44697
- setLoading(false);
44716
+ if (isCurrentRequest()) setLoading(false);
44698
44717
  }
44699
- }, [query, ready]);
44718
+ }, [listIdentity, query, ready]);
44700
44719
  (0, import_react21.useEffect)(() => {
44701
44720
  if (!ready) return;
44702
- const fingerprint = queryFingerprint(query, sourceKey);
44721
+ const fingerprint = queryFingerprint(query, listIdentity);
44703
44722
  if (previousFingerprintRef.current && previousFingerprintRef.current !== fingerprint && selectedKeysRef.current.length) {
44704
44723
  clearSelection("\u67E5\u8BE2\u6761\u4EF6\u6216\u6392\u5E8F\u5DF2\u53D8\u5316\uFF0C\u5DF2\u6E05\u7A7A\u8DE8\u9875\u9009\u62E9");
44705
44724
  }
@@ -44710,7 +44729,7 @@ function AdminList(props) {
44710
44729
  loadData,
44711
44730
  query,
44712
44731
  ready,
44713
- sourceKey
44732
+ listIdentity
44714
44733
  ]);
44715
44734
  const visibleSearchFields = (0, import_react21.useMemo)(() => {
44716
44735
  const searchMap = new Map(searchFields.map((field) => [field.key, field]));
@@ -45885,7 +45904,7 @@ function createFormAdminListSource(options) {
45885
45904
  filterOperators: options.filterOperators || {}
45886
45905
  });
45887
45906
  return {
45888
- key: `form:${appType}:${options.formUuid}`,
45907
+ key: `form:${appType}:${options.formUuid}:${options.listKey}`,
45889
45908
  preferences: createAdminListPreferenceStore(options.sdk, appType),
45890
45909
  async query(query) {
45891
45910
  const filters = {
@@ -45916,7 +45935,7 @@ function createDataViewAdminListSource(options) {
45916
45935
  filterOperators: options.filterOperators || {}
45917
45936
  });
45918
45937
  return {
45919
- key: `data-view:${appType}:${options.dataViewCode}`,
45938
+ key: `data-view:${appType}:${options.dataViewCode}:${options.listKey}`,
45920
45939
  preferences: createAdminListPreferenceStore(options.sdk, appType),
45921
45940
  async query(query) {
45922
45941
  const filters = {
@@ -45947,7 +45966,7 @@ function createFunctionAdminListSource(options) {
45947
45966
  functionCode: options.functionCode
45948
45967
  });
45949
45968
  return {
45950
- key: `function:${appType}:${options.functionCode}`,
45969
+ key: `function:${appType}:${options.functionCode}:${options.listKey}`,
45951
45970
  preferences: createAdminListPreferenceStore(options.sdk, appType),
45952
45971
  async query(query) {
45953
45972
  const response = await options.sdk.function.invoke(options.functionCode, {