openxiangda 1.0.134 → 1.0.136

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 (57) hide show
  1. package/README.md +6 -0
  2. package/lib/cli.js +947 -16
  3. package/lib/design-gates.js +40 -10
  4. package/lib/sdd.js +777 -0
  5. package/lib/workspace-init.js +3 -0
  6. package/openxiangda-skills/SKILL.md +11 -1
  7. package/openxiangda-skills/references/best-practices.md +32 -5
  8. package/openxiangda-skills/references/openxiangda-api.md +32 -6
  9. package/openxiangda-skills/references/pages/page-sdk.md +3 -0
  10. package/openxiangda-skills/references/pages/publish-flow.md +16 -0
  11. package/openxiangda-skills/references/permission-design-patterns.md +216 -0
  12. package/openxiangda-skills/references/permissions-settings.md +58 -0
  13. package/openxiangda-skills/references/platform-data-model.md +19 -4
  14. package/openxiangda-skills/references/resource-manifest-cheatsheet.md +32 -1
  15. package/openxiangda-skills/skills/openxiangda-architecture-design/SKILL.md +8 -0
  16. package/openxiangda-skills/skills/openxiangda-core/SKILL.md +2 -0
  17. package/openxiangda-skills/skills/openxiangda-permission-settings/SKILL.md +38 -1
  18. package/package.json +2 -1
  19. package/packages/sdk/dist/{ProcessPreview-Dfc4-wIq.d.mts → ProcessPreview-Ci8_UsbN.d.mts} +4 -0
  20. package/packages/sdk/dist/{ProcessPreview-Dfc4-wIq.d.ts → ProcessPreview-Ci8_UsbN.d.ts} +4 -0
  21. package/packages/sdk/dist/build/index.cjs.map +1 -1
  22. package/packages/sdk/dist/build/index.d.mts +7 -0
  23. package/packages/sdk/dist/build/index.d.ts +7 -0
  24. package/packages/sdk/dist/build/index.mjs.map +1 -1
  25. package/packages/sdk/dist/components/index.cjs +32 -4
  26. package/packages/sdk/dist/components/index.cjs.map +1 -1
  27. package/packages/sdk/dist/components/index.d.mts +8 -2
  28. package/packages/sdk/dist/components/index.d.ts +8 -2
  29. package/packages/sdk/dist/components/index.mjs +32 -4
  30. package/packages/sdk/dist/components/index.mjs.map +1 -1
  31. package/packages/sdk/dist/runtime/index.cjs +181 -28
  32. package/packages/sdk/dist/runtime/index.cjs.map +1 -1
  33. package/packages/sdk/dist/runtime/index.d.mts +2 -2
  34. package/packages/sdk/dist/runtime/index.d.ts +2 -2
  35. package/packages/sdk/dist/runtime/index.mjs +181 -28
  36. package/packages/sdk/dist/runtime/index.mjs.map +1 -1
  37. package/packages/sdk/dist/runtime/react.cjs +152 -25
  38. package/packages/sdk/dist/runtime/react.cjs.map +1 -1
  39. package/packages/sdk/dist/runtime/react.d.mts +57 -4
  40. package/packages/sdk/dist/runtime/react.d.ts +57 -4
  41. package/packages/sdk/dist/runtime/react.mjs +152 -25
  42. package/packages/sdk/dist/runtime/react.mjs.map +1 -1
  43. package/templates/openxiangda-react-spa/.cursor/rules/openxiangda.mdc +8 -1
  44. package/templates/openxiangda-react-spa/.qoder/rules/openxiangda.md +8 -1
  45. package/templates/openxiangda-react-spa/AGENTS.md +9 -1
  46. package/templates/openxiangda-react-spa/app-workspace.config.ts +14 -0
  47. package/templates/sy-lowcode-app-workspace/.cursor/rules/openxiangda-resources.mdc +5 -1
  48. package/templates/sy-lowcode-app-workspace/.cursor/rules/openxiangda.mdc +7 -1
  49. package/templates/sy-lowcode-app-workspace/.qoder/rules/openxiangda-resources.md +6 -0
  50. package/templates/sy-lowcode-app-workspace/.qoder/rules/openxiangda.md +7 -1
  51. package/templates/sy-lowcode-app-workspace/AGENTS.md +10 -1
  52. package/templates/sy-lowcode-app-workspace/app-workspace.config.ts +14 -0
  53. package/templates/sy-lowcode-app-workspace/examples/best-practices/README.md +3 -0
  54. package/templates/sy-lowcode-app-workspace/examples/best-practices/access-governance.md +239 -0
  55. package/templates/sy-lowcode-app-workspace/examples/best-practices/catalog.json +6 -0
  56. package/templates/sy-lowcode-app-workspace/examples/best-practices/decision-guide.md +25 -1
  57. package/templates/sy-lowcode-app-workspace/src/types/app-workspace.types.ts +12 -0
@@ -91,6 +91,7 @@ module.exports = __toCommonJS(runtime_exports);
91
91
  var isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
92
92
  var isSearchRuleLike = (value) => isRecord(value) && typeof value.key === "string" && (value.componentName === void 0 || typeof value.componentName === "string");
93
93
  var isBlobLike = (value) => typeof Blob !== "undefined" && value instanceof Blob;
94
+ var nowMs = () => typeof performance !== "undefined" && typeof performance.now === "function" ? performance.now() : Date.now();
94
95
  var normalizeMethod = (value) => {
95
96
  const method = String(value || "get").toLowerCase();
96
97
  if (method === "get" || method === "post" || method === "put" || method === "delete" || method === "patch") {
@@ -349,6 +350,26 @@ var normalizeJsonResponse = (rawResponse) => {
349
350
  raw: rawResponse
350
351
  };
351
352
  };
353
+ var createMissingFormInstanceIdError = (response) => {
354
+ const error = new Error(
355
+ "\u4FDD\u5B58\u63A5\u53E3\u672A\u8FD4\u56DE formInstId/formInstanceId\u3002\u4FDD\u5B58\u63A5\u53E3\u53EA\u627F\u8BFA\u8FD4\u56DE\u5B9E\u4F8B\u6807\u8BC6\u548C\u5DF2\u751F\u6210\u7684\u6D41\u6C34\u53F7\uFF1B\u5982\u9700\u5B8C\u6574\u884C\u6570\u636E\u3001\u516C\u5F0F\u56DE\u586B\u6216\u5176\u4ED6\u670D\u52A1\u7AEF\u5B57\u6BB5\uFF0C\u8BF7\u5728\u4FDD\u5B58\u6210\u529F\u540E\u663E\u5F0F\u8C03\u7528 sdk.form.getDetail\u3002"
356
+ );
357
+ error.response = response;
358
+ error.raw = response.raw;
359
+ return error;
360
+ };
361
+ var isRequestTracingEnabled = (context, options) => {
362
+ if (options.trace) return true;
363
+ const env = context.env || {};
364
+ const value = env.OPENXIANGDA_REQUEST_TRACE ?? env.openxiangdaRequestTrace ?? env.requestTrace;
365
+ return value === true || value === "true" || value === "1";
366
+ };
367
+ var buildRequestDedupeKey = (payload) => JSON.stringify({
368
+ path: payload.path,
369
+ method: payload.method,
370
+ query: payload.query || "",
371
+ headers: payload.headers || {}
372
+ });
352
373
  var normalizeBinaryResponse = (rawResponse) => {
353
374
  if (isRecord(rawResponse) && isBlobLike(rawResponse.blob)) {
354
375
  return rawResponse;
@@ -420,12 +441,90 @@ var pickCreatedFormInstanceId = (value) => {
420
441
  return text || void 0;
421
442
  }
422
443
  if (!isRecord(value)) return void 0;
423
- const direct = value.formInstId || value.formInstanceId || value.instanceId || value.id || value.result || value.data;
424
- if (direct !== value) {
425
- return pickCreatedFormInstanceId(direct);
444
+ const direct = value.formInstId || value.formInstanceId || value.instanceId || value.id;
445
+ if (direct !== value && direct !== void 0 && direct !== null) {
446
+ const picked = pickCreatedFormInstanceId(direct);
447
+ if (picked) return picked;
448
+ }
449
+ for (const candidate of [value.result, value.data, value.raw]) {
450
+ const picked = pickCreatedFormInstanceId(candidate);
451
+ if (picked) return picked;
452
+ }
453
+ return void 0;
454
+ };
455
+ var pickCreatedProcessInstanceId = (value) => {
456
+ if (!value || typeof value === "string") return void 0;
457
+ if (!isRecord(value)) return void 0;
458
+ const direct = value.processInstanceId || value.processInstId;
459
+ if (typeof direct === "string" && direct.trim()) return direct.trim();
460
+ for (const candidate of [value.result, value.data, value.raw]) {
461
+ const picked = pickCreatedProcessInstanceId(candidate);
462
+ if (picked) return picked;
426
463
  }
427
464
  return void 0;
428
465
  };
466
+ var pickSerialNumbers = (value) => {
467
+ if (!value || typeof value === "string" || !isRecord(value)) return void 0;
468
+ const direct = value.serialNumbers;
469
+ if (isRecord(direct)) {
470
+ const entries = Object.entries(direct).map(([key, item]) => [key, String(item ?? "").trim()]).filter(([, item]) => Boolean(item));
471
+ if (entries.length > 0) return Object.fromEntries(entries);
472
+ }
473
+ for (const candidate of [value.result, value.data, value.raw]) {
474
+ const picked = pickSerialNumbers(candidate);
475
+ if (picked) return picked;
476
+ }
477
+ return void 0;
478
+ };
479
+ var pickSerialNumber = (value) => {
480
+ if (!value || typeof value === "string") return void 0;
481
+ if (!isRecord(value)) return void 0;
482
+ const direct = value.serialNumber || value.serialNo || value.sequenceNo;
483
+ if (typeof direct === "string" && direct.trim()) return direct.trim();
484
+ const serialNumbers = pickSerialNumbers(value);
485
+ if (serialNumbers) {
486
+ const first = Object.values(serialNumbers)[0];
487
+ if (first) return first;
488
+ }
489
+ for (const candidate of [value.result, value.data, value.raw]) {
490
+ const picked = pickSerialNumber(candidate);
491
+ if (picked) return picked;
492
+ }
493
+ return void 0;
494
+ };
495
+ var normalizeFormCreateResponse = (response, formInstanceId) => {
496
+ const processInstanceId = pickCreatedProcessInstanceId(response);
497
+ const serialNumbers = pickSerialNumbers(response);
498
+ const serialNumber = pickSerialNumber(response);
499
+ const sourceResult = isRecord(response.result) ? response.result : {};
500
+ const normalizedResult = {
501
+ ...sourceResult,
502
+ formInstId: formInstanceId,
503
+ formInstanceId,
504
+ result: typeof response.result === "string" && response.result.trim() ? response.result.trim() : sourceResult.result || formInstanceId,
505
+ ...processInstanceId ? { processInstanceId } : {},
506
+ ...serialNumber ? { serialNumber } : {},
507
+ ...serialNumbers ? { serialNumbers } : {}
508
+ };
509
+ const normalizedData = isRecord(response.data) ? {
510
+ ...response.data,
511
+ formInstId: response.data.formInstId || formInstanceId,
512
+ formInstanceId: response.data.formInstanceId || formInstanceId,
513
+ ...processInstanceId ? { processInstanceId } : {},
514
+ ...serialNumber ? { serialNumber } : {},
515
+ ...serialNumbers ? { serialNumbers } : {}
516
+ } : response.data;
517
+ return {
518
+ ...response,
519
+ formInstId: formInstanceId,
520
+ formInstanceId,
521
+ ...processInstanceId ? { processInstanceId } : {},
522
+ ...serialNumber ? { serialNumber } : {},
523
+ ...serialNumbers ? { serialNumbers } : {},
524
+ result: normalizedResult,
525
+ data: normalizedData
526
+ };
527
+ };
429
528
  var isStableCreateLookupValue = (value) => {
430
529
  if (value === void 0 || value === null || value === "") return false;
431
530
  return typeof value === "string" || typeof value === "number" || typeof value === "boolean";
@@ -460,20 +559,6 @@ var firstListItem = (value) => {
460
559
  }
461
560
  return void 0;
462
561
  };
463
- var attachFormInstanceId = (response, formInstanceId) => {
464
- const withIds = (value) => isRecord(value) ? {
465
- ...value,
466
- formInstId: value.formInstId || formInstanceId,
467
- formInstanceId: value.formInstanceId || formInstanceId
468
- } : value;
469
- return {
470
- ...response,
471
- formInstId: formInstanceId,
472
- formInstanceId,
473
- result: response.result === null || response.result === void 0 ? formInstanceId : withIds(response.result),
474
- data: response.data === void 0 ? response.data : withIds(response.data)
475
- };
476
- };
477
562
  var withDefaultAppType = (context, params) => {
478
563
  if (!params) {
479
564
  return params;
@@ -593,6 +678,8 @@ var redirectAfterLogout = (context, options) => {
593
678
  );
594
679
  };
595
680
  var createPageSdk = (context) => {
681
+ const inFlightGetRequests = /* @__PURE__ */ new Map();
682
+ const recentRequestStats = /* @__PURE__ */ new Map();
596
683
  const request = async (options) => {
597
684
  const payload = {
598
685
  path: options.path,
@@ -601,17 +688,54 @@ var createPageSdk = (context) => {
601
688
  body: options.body,
602
689
  headers: options.headers
603
690
  };
604
- try {
691
+ const traceEnabled = isRequestTracingEnabled(context, options);
692
+ const traceLabel = options.traceLabel || `${payload.method.toUpperCase()} ${payload.path}`;
693
+ const dedupeKey = options.dedupe !== false && payload.method === "get" ? buildRequestDedupeKey(payload) : void 0;
694
+ if (traceEnabled && dedupeKey) {
695
+ const currentAt = nowMs();
696
+ const previous = recentRequestStats.get(dedupeKey);
697
+ const nextCount = previous && currentAt - previous.lastAt < 500 ? previous.count + 1 : 1;
698
+ recentRequestStats.set(dedupeKey, { count: nextCount, lastAt: currentAt });
699
+ if (nextCount > 1) {
700
+ console.warn(
701
+ `[OpenXiangda SDK] repeated request within 500ms (${nextCount}): ${traceLabel}`
702
+ );
703
+ }
704
+ }
705
+ if (dedupeKey && inFlightGetRequests.has(dedupeKey)) {
706
+ if (traceEnabled) {
707
+ console.debug(`[OpenXiangda SDK] deduped in-flight request: ${traceLabel}`);
708
+ }
709
+ return inFlightGetRequests.get(dedupeKey);
710
+ }
711
+ const task = (async () => {
712
+ const startedAt = nowMs();
605
713
  const rawResponse = await context.bridge.invoke(
606
714
  "transport.request",
607
715
  payload
608
716
  );
609
- return ensureSuccess(
717
+ const normalized = ensureSuccess(
610
718
  normalizeJsonResponse(rawResponse),
611
719
  payload
612
720
  );
721
+ if (traceEnabled) {
722
+ console.debug(
723
+ `[OpenXiangda SDK] ${traceLabel} ${Math.round(nowMs() - startedAt)}ms`
724
+ );
725
+ }
726
+ return normalized;
727
+ })();
728
+ if (dedupeKey) {
729
+ inFlightGetRequests.set(dedupeKey, task);
730
+ }
731
+ try {
732
+ return await task;
613
733
  } catch (error) {
614
734
  throw toSdkError(error, payload);
735
+ } finally {
736
+ if (dedupeKey && inFlightGetRequests.get(dedupeKey) === task) {
737
+ inFlightGetRequests.delete(dedupeKey);
738
+ }
615
739
  }
616
740
  };
617
741
  const download = async (options) => {
@@ -729,16 +853,17 @@ var createPageSdk = (context) => {
729
853
  processStartMode: params.processStartMode
730
854
  }
731
855
  });
732
- if (params.lookupAfterCreate === false) {
733
- return response;
734
- }
735
856
  const directId = pickCreatedFormInstanceId(response);
736
857
  if (directId) {
737
- return attachFormInstanceId(response, directId);
858
+ return normalizeFormCreateResponse(response, directId);
859
+ }
860
+ if (!params.lookupAfterCreate) {
861
+ throw createMissingFormInstanceIdError(response);
738
862
  }
739
863
  const filters = buildCreateLookupFilters(params.data || {}, params.lookupFields);
740
864
  if (!filters) {
741
- return response;
865
+ if (params.lookupAfterCreate === "legacy") return response;
866
+ throw createMissingFormInstanceIdError(response);
742
867
  }
743
868
  const lookupResponse = await request({
744
869
  path: buildAppPath(
@@ -756,7 +881,9 @@ var createPageSdk = (context) => {
756
881
  }
757
882
  });
758
883
  const lookupId = pickCreatedFormInstanceId(firstListItem(lookupResponse));
759
- return lookupId ? attachFormInstanceId(response, lookupId) : response;
884
+ if (lookupId) return normalizeFormCreateResponse(response, lookupId);
885
+ if (params.lookupAfterCreate === "legacy") return response;
886
+ throw createMissingFormInstanceIdError(response);
760
887
  },
761
888
  update: (params) => request({
762
889
  path: buildAppPath(
@@ -19111,7 +19238,23 @@ var pickFormInstanceId = (value) => {
19111
19238
  if (!value) return void 0;
19112
19239
  if (typeof value === "string") return value;
19113
19240
  if (typeof value !== "object") return void 0;
19114
- return value.formInstanceId || value.formInstId || value.instanceId || value.id || pickFormInstanceId(value.result) || pickFormInstanceId(value.data);
19241
+ const direct = value.formInstanceId || value.formInstId || value.instanceId || value.id;
19242
+ if (direct) return pickFormInstanceId(direct);
19243
+ for (const candidate of [value.result, value.data, value.raw]) {
19244
+ const picked = pickFormInstanceId(candidate);
19245
+ if (picked) return picked;
19246
+ }
19247
+ return void 0;
19248
+ };
19249
+ var getSubmitResultFormInstanceId = (submitResponse, config, submitBehavior) => {
19250
+ const responseId = pickFormInstanceId(submitResponse);
19251
+ if (responseId) return responseId;
19252
+ if (submitBehavior !== "create" && config.formInstanceId) {
19253
+ return config.formInstanceId;
19254
+ }
19255
+ throw new Error(
19256
+ "\u63D0\u4EA4\u63A5\u53E3\u672A\u8FD4\u56DE formInstId/formInstanceId\u3002\u4FDD\u5B58\u63A5\u53E3\u53EA\u8FD4\u56DE\u5B9E\u4F8B\u6807\u8BC6\u548C\u5DF2\u751F\u6210\u7684\u6D41\u6C34\u53F7\uFF1B\u5982\u9700\u5B8C\u6574\u6570\u636E\u3001\u516C\u5F0F\u56DE\u586B\u6216\u5176\u4ED6\u670D\u52A1\u7AEF\u5B57\u6BB5\uFF0C\u8BF7\u4FDD\u5B58\u6210\u529F\u540E\u518D\u67E5\u8BE2\u8BE6\u60C5\u3002"
19257
+ );
19115
19258
  };
19116
19259
  var normalizeFormDataForSubmit = (schema, formData) => {
19117
19260
  const next = { ...formData };
@@ -19310,7 +19453,7 @@ var InnerFormContent = ({
19310
19453
  selectedApprovers: initiatorSelectedApprovers,
19311
19454
  initiatorSelectedApprovers
19312
19455
  });
19313
- const formInstId = pickFormInstanceId(submitResponse) || config.formInstanceId || `inst_${Date.now()}`;
19456
+ const formInstId = getSubmitResultFormInstanceId(submitResponse, config, submitBehavior);
19314
19457
  if (config.submit?.afterSubmit) {
19315
19458
  await config.submit.afterSubmit({
19316
19459
  formInstanceId: formInstId,
@@ -19334,6 +19477,7 @@ var InnerFormContent = ({
19334
19477
  }
19335
19478
  } catch (error) {
19336
19479
  console.error("[FormSubmitTemplate] Submit failed:", error);
19480
+ import_antd36.message.error(error instanceof Error ? error.message : "\u63D0\u4EA4\u5931\u8D25");
19337
19481
  } finally {
19338
19482
  setSubmitting(false);
19339
19483
  }
@@ -21226,6 +21370,7 @@ var StandardFormPage = ({
21226
21370
  formInstanceId,
21227
21371
  defaultUploadProvider,
21228
21372
  submitBehavior,
21373
+ compatibility,
21229
21374
  onSubmit,
21230
21375
  onSubmitSuccess,
21231
21376
  inDrawer
@@ -21299,6 +21444,7 @@ var StandardFormPage = ({
21299
21444
  defaultUploadProvider,
21300
21445
  submitBehavior,
21301
21446
  permissions,
21447
+ compatibility,
21302
21448
  api
21303
21449
  },
21304
21450
  formType,
@@ -22630,6 +22776,7 @@ var DataManagementList = ({
22630
22776
  detailPageUrlBuilder,
22631
22777
  submitRenderer,
22632
22778
  requestOverride,
22779
+ allowSchemaFallback = false,
22633
22780
  rowActions = [],
22634
22781
  maxVisibleRowActions
22635
22782
  }) => {
@@ -22797,6 +22944,11 @@ var DataManagementList = ({
22797
22944
  const nextSearchKeyWord = saved?.filter?.searchKeyWord || "";
22798
22945
  const nextFilterGroup = hydrateFilterGroup(saved?.filter?.group);
22799
22946
  const nextFormType = propFormType || schemaResult.formType || "form";
22947
+ if (!schemaResult.schema && !allowSchemaFallback) {
22948
+ throw new Error(
22949
+ `\u8868\u5355 schema \u4E0D\u5B58\u5728\u6216\u672A\u53D1\u5E03: ${formUuid}\u3002\u8BF7\u5148\u53D1\u5E03\u8868\u5355 schema\uFF1B\u4E25\u683C\u6A21\u5F0F\u4E0D\u4F1A\u751F\u6210\u515C\u5E95\u8868\u5355\u3002`
22950
+ );
22951
+ }
22800
22952
  setFields(allFields);
22801
22953
  setRuntimeFormSchema(
22802
22954
  schemaResult.schema || buildFallbackFormSchema({
@@ -22827,7 +22979,7 @@ var DataManagementList = ({
22827
22979
  });
22828
22980
  } catch (error) {
22829
22981
  console.error("[DataManagementList] load schema failed:", error);
22830
- import_antd39.message.error("\u52A0\u8F7D\u8868\u5355\u914D\u7F6E\u5931\u8D25");
22982
+ import_antd39.message.error(error instanceof Error ? error.message : "\u52A0\u8F7D\u8868\u5355\u914D\u7F6E\u5931\u8D25");
22831
22983
  } finally {
22832
22984
  if (mounted) setSchemaLoading(false);
22833
22985
  }
@@ -22838,6 +22990,7 @@ var DataManagementList = ({
22838
22990
  };
22839
22991
  }, [
22840
22992
  appType,
22993
+ allowSchemaFallback,
22841
22994
  forcedConfig,
22842
22995
  formTitle,
22843
22996
  formUuid,