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
@@ -101,6 +101,7 @@ var usePageSdkStore = () => {
101
101
  var isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
102
102
  var isSearchRuleLike = (value) => isRecord(value) && typeof value.key === "string" && (value.componentName === void 0 || typeof value.componentName === "string");
103
103
  var isBlobLike = (value) => typeof Blob !== "undefined" && value instanceof Blob;
104
+ var nowMs = () => typeof performance !== "undefined" && typeof performance.now === "function" ? performance.now() : Date.now();
104
105
  var normalizeMethod = (value) => {
105
106
  const method = String(value || "get").toLowerCase();
106
107
  if (method === "get" || method === "post" || method === "put" || method === "delete" || method === "patch") {
@@ -359,6 +360,26 @@ var normalizeJsonResponse = (rawResponse) => {
359
360
  raw: rawResponse
360
361
  };
361
362
  };
363
+ var createMissingFormInstanceIdError = (response) => {
364
+ const error = new Error(
365
+ "\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"
366
+ );
367
+ error.response = response;
368
+ error.raw = response.raw;
369
+ return error;
370
+ };
371
+ var isRequestTracingEnabled = (context, options) => {
372
+ if (options.trace) return true;
373
+ const env = context.env || {};
374
+ const value = env.OPENXIANGDA_REQUEST_TRACE ?? env.openxiangdaRequestTrace ?? env.requestTrace;
375
+ return value === true || value === "true" || value === "1";
376
+ };
377
+ var buildRequestDedupeKey = (payload) => JSON.stringify({
378
+ path: payload.path,
379
+ method: payload.method,
380
+ query: payload.query || "",
381
+ headers: payload.headers || {}
382
+ });
362
383
  var normalizeBinaryResponse = (rawResponse) => {
363
384
  if (isRecord(rawResponse) && isBlobLike(rawResponse.blob)) {
364
385
  return rawResponse;
@@ -430,12 +451,90 @@ var pickCreatedFormInstanceId = (value) => {
430
451
  return text || void 0;
431
452
  }
432
453
  if (!isRecord(value)) return void 0;
433
- const direct = value.formInstId || value.formInstanceId || value.instanceId || value.id || value.result || value.data;
434
- if (direct !== value) {
435
- return pickCreatedFormInstanceId(direct);
454
+ const direct = value.formInstId || value.formInstanceId || value.instanceId || value.id;
455
+ if (direct !== value && direct !== void 0 && direct !== null) {
456
+ const picked = pickCreatedFormInstanceId(direct);
457
+ if (picked) return picked;
458
+ }
459
+ for (const candidate of [value.result, value.data, value.raw]) {
460
+ const picked = pickCreatedFormInstanceId(candidate);
461
+ if (picked) return picked;
462
+ }
463
+ return void 0;
464
+ };
465
+ var pickCreatedProcessInstanceId = (value) => {
466
+ if (!value || typeof value === "string") return void 0;
467
+ if (!isRecord(value)) return void 0;
468
+ const direct = value.processInstanceId || value.processInstId;
469
+ if (typeof direct === "string" && direct.trim()) return direct.trim();
470
+ for (const candidate of [value.result, value.data, value.raw]) {
471
+ const picked = pickCreatedProcessInstanceId(candidate);
472
+ if (picked) return picked;
473
+ }
474
+ return void 0;
475
+ };
476
+ var pickSerialNumbers = (value) => {
477
+ if (!value || typeof value === "string" || !isRecord(value)) return void 0;
478
+ const direct = value.serialNumbers;
479
+ if (isRecord(direct)) {
480
+ const entries = Object.entries(direct).map(([key, item]) => [key, String(item ?? "").trim()]).filter(([, item]) => Boolean(item));
481
+ if (entries.length > 0) return Object.fromEntries(entries);
482
+ }
483
+ for (const candidate of [value.result, value.data, value.raw]) {
484
+ const picked = pickSerialNumbers(candidate);
485
+ if (picked) return picked;
486
+ }
487
+ return void 0;
488
+ };
489
+ var pickSerialNumber = (value) => {
490
+ if (!value || typeof value === "string") return void 0;
491
+ if (!isRecord(value)) return void 0;
492
+ const direct = value.serialNumber || value.serialNo || value.sequenceNo;
493
+ if (typeof direct === "string" && direct.trim()) return direct.trim();
494
+ const serialNumbers = pickSerialNumbers(value);
495
+ if (serialNumbers) {
496
+ const first = Object.values(serialNumbers)[0];
497
+ if (first) return first;
498
+ }
499
+ for (const candidate of [value.result, value.data, value.raw]) {
500
+ const picked = pickSerialNumber(candidate);
501
+ if (picked) return picked;
436
502
  }
437
503
  return void 0;
438
504
  };
505
+ var normalizeFormCreateResponse = (response, formInstanceId) => {
506
+ const processInstanceId = pickCreatedProcessInstanceId(response);
507
+ const serialNumbers = pickSerialNumbers(response);
508
+ const serialNumber = pickSerialNumber(response);
509
+ const sourceResult = isRecord(response.result) ? response.result : {};
510
+ const normalizedResult = {
511
+ ...sourceResult,
512
+ formInstId: formInstanceId,
513
+ formInstanceId,
514
+ result: typeof response.result === "string" && response.result.trim() ? response.result.trim() : sourceResult.result || formInstanceId,
515
+ ...processInstanceId ? { processInstanceId } : {},
516
+ ...serialNumber ? { serialNumber } : {},
517
+ ...serialNumbers ? { serialNumbers } : {}
518
+ };
519
+ const normalizedData = isRecord(response.data) ? {
520
+ ...response.data,
521
+ formInstId: response.data.formInstId || formInstanceId,
522
+ formInstanceId: response.data.formInstanceId || formInstanceId,
523
+ ...processInstanceId ? { processInstanceId } : {},
524
+ ...serialNumber ? { serialNumber } : {},
525
+ ...serialNumbers ? { serialNumbers } : {}
526
+ } : response.data;
527
+ return {
528
+ ...response,
529
+ formInstId: formInstanceId,
530
+ formInstanceId,
531
+ ...processInstanceId ? { processInstanceId } : {},
532
+ ...serialNumber ? { serialNumber } : {},
533
+ ...serialNumbers ? { serialNumbers } : {},
534
+ result: normalizedResult,
535
+ data: normalizedData
536
+ };
537
+ };
439
538
  var isStableCreateLookupValue = (value) => {
440
539
  if (value === void 0 || value === null || value === "") return false;
441
540
  return typeof value === "string" || typeof value === "number" || typeof value === "boolean";
@@ -470,20 +569,6 @@ var firstListItem = (value) => {
470
569
  }
471
570
  return void 0;
472
571
  };
473
- var attachFormInstanceId = (response, formInstanceId) => {
474
- const withIds = (value) => isRecord(value) ? {
475
- ...value,
476
- formInstId: value.formInstId || formInstanceId,
477
- formInstanceId: value.formInstanceId || formInstanceId
478
- } : value;
479
- return {
480
- ...response,
481
- formInstId: formInstanceId,
482
- formInstanceId,
483
- result: response.result === null || response.result === void 0 ? formInstanceId : withIds(response.result),
484
- data: response.data === void 0 ? response.data : withIds(response.data)
485
- };
486
- };
487
572
  var withDefaultAppType = (context, params) => {
488
573
  if (!params) {
489
574
  return params;
@@ -603,6 +688,8 @@ var redirectAfterLogout = (context, options) => {
603
688
  );
604
689
  };
605
690
  var createPageSdk = (context) => {
691
+ const inFlightGetRequests = /* @__PURE__ */ new Map();
692
+ const recentRequestStats = /* @__PURE__ */ new Map();
606
693
  const request = async (options) => {
607
694
  const payload = {
608
695
  path: options.path,
@@ -611,17 +698,54 @@ var createPageSdk = (context) => {
611
698
  body: options.body,
612
699
  headers: options.headers
613
700
  };
614
- try {
701
+ const traceEnabled = isRequestTracingEnabled(context, options);
702
+ const traceLabel = options.traceLabel || `${payload.method.toUpperCase()} ${payload.path}`;
703
+ const dedupeKey = options.dedupe !== false && payload.method === "get" ? buildRequestDedupeKey(payload) : void 0;
704
+ if (traceEnabled && dedupeKey) {
705
+ const currentAt = nowMs();
706
+ const previous = recentRequestStats.get(dedupeKey);
707
+ const nextCount = previous && currentAt - previous.lastAt < 500 ? previous.count + 1 : 1;
708
+ recentRequestStats.set(dedupeKey, { count: nextCount, lastAt: currentAt });
709
+ if (nextCount > 1) {
710
+ console.warn(
711
+ `[OpenXiangda SDK] repeated request within 500ms (${nextCount}): ${traceLabel}`
712
+ );
713
+ }
714
+ }
715
+ if (dedupeKey && inFlightGetRequests.has(dedupeKey)) {
716
+ if (traceEnabled) {
717
+ console.debug(`[OpenXiangda SDK] deduped in-flight request: ${traceLabel}`);
718
+ }
719
+ return inFlightGetRequests.get(dedupeKey);
720
+ }
721
+ const task = (async () => {
722
+ const startedAt = nowMs();
615
723
  const rawResponse = await context.bridge.invoke(
616
724
  "transport.request",
617
725
  payload
618
726
  );
619
- return ensureSuccess(
727
+ const normalized = ensureSuccess(
620
728
  normalizeJsonResponse(rawResponse),
621
729
  payload
622
730
  );
731
+ if (traceEnabled) {
732
+ console.debug(
733
+ `[OpenXiangda SDK] ${traceLabel} ${Math.round(nowMs() - startedAt)}ms`
734
+ );
735
+ }
736
+ return normalized;
737
+ })();
738
+ if (dedupeKey) {
739
+ inFlightGetRequests.set(dedupeKey, task);
740
+ }
741
+ try {
742
+ return await task;
623
743
  } catch (error) {
624
744
  throw toSdkError(error, payload);
745
+ } finally {
746
+ if (dedupeKey && inFlightGetRequests.get(dedupeKey) === task) {
747
+ inFlightGetRequests.delete(dedupeKey);
748
+ }
625
749
  }
626
750
  };
627
751
  const download = async (options) => {
@@ -739,16 +863,17 @@ var createPageSdk = (context) => {
739
863
  processStartMode: params.processStartMode
740
864
  }
741
865
  });
742
- if (params.lookupAfterCreate === false) {
743
- return response;
744
- }
745
866
  const directId = pickCreatedFormInstanceId(response);
746
867
  if (directId) {
747
- return attachFormInstanceId(response, directId);
868
+ return normalizeFormCreateResponse(response, directId);
869
+ }
870
+ if (!params.lookupAfterCreate) {
871
+ throw createMissingFormInstanceIdError(response);
748
872
  }
749
873
  const filters = buildCreateLookupFilters(params.data || {}, params.lookupFields);
750
874
  if (!filters) {
751
- return response;
875
+ if (params.lookupAfterCreate === "legacy") return response;
876
+ throw createMissingFormInstanceIdError(response);
752
877
  }
753
878
  const lookupResponse = await request({
754
879
  path: buildAppPath(
@@ -766,7 +891,9 @@ var createPageSdk = (context) => {
766
891
  }
767
892
  });
768
893
  const lookupId = pickCreatedFormInstanceId(firstListItem(lookupResponse));
769
- return lookupId ? attachFormInstanceId(response, lookupId) : response;
894
+ if (lookupId) return normalizeFormCreateResponse(response, lookupId);
895
+ if (params.lookupAfterCreate === "legacy") return response;
896
+ throw createMissingFormInstanceIdError(response);
770
897
  },
771
898
  update: (params) => request({
772
899
  path: buildAppPath(