weaver-work-cli 0.1.5 → 0.1.7

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 (225) hide show
  1. package/README.md +14 -9
  2. package/dist/cmd/skills/index.js +60 -0
  3. package/dist/internal/e10/auth/commands.js +48 -32
  4. package/dist/internal/e10/auth/session.js +44 -3
  5. package/dist/internal/e10/auth/xiaoe.js +363 -11
  6. package/dist/internal/e10/context.js +4 -2
  7. package/dist/internal/skills/detector.js +42 -0
  8. package/dist/internal/skills/install.js +50 -3
  9. package/dist/shortcuts/archive/render/search-format.js +12 -3
  10. package/dist/shortcuts/ebuilder-form/continuation.js +70 -0
  11. package/dist/shortcuts/ebuilder-form/errors.js +54 -0
  12. package/dist/shortcuts/ebuilder-form/host.js +1238 -0
  13. package/dist/shortcuts/ebuilder-form/index.js +108 -0
  14. package/dist/shortcuts/ebuilder-form/manifest.js +347 -0
  15. package/dist/shortcuts/ebuilder-form/operations/read.js +157 -0
  16. package/dist/shortcuts/ebuilder-form/operations/registry.js +56 -0
  17. package/dist/shortcuts/ebuilder-form/operations/shared.js +132 -0
  18. package/dist/shortcuts/ebuilder-form/operations/types.js +19 -0
  19. package/dist/shortcuts/ebuilder-form/operations/write.js +277 -0
  20. package/dist/shortcuts/ebuilder-form/operations.js +8 -0
  21. package/dist/shortcuts/ehr/operations/salary.js +2 -140
  22. package/dist/shortcuts/im/continuation.js +70 -0
  23. package/dist/shortcuts/{yimiaoban → im}/errors.js +14 -14
  24. package/dist/shortcuts/{yimiaoban → im}/host.js +26 -26
  25. package/dist/shortcuts/{yimiaoban → im}/index.js +42 -42
  26. package/dist/shortcuts/{yimiaoban → im}/manifest.js +7 -6
  27. package/dist/shortcuts/{yimiaoban → im}/operations/file-user.js +54 -41
  28. package/dist/shortcuts/{yimiaoban → im}/operations/group-read.js +46 -44
  29. package/dist/shortcuts/{yimiaoban → im}/operations/group-write.js +113 -66
  30. package/dist/shortcuts/{yimiaoban → im}/operations/msg-sync.js +164 -141
  31. package/dist/shortcuts/{yimiaoban → im}/operations/msg-write.js +47 -47
  32. package/dist/shortcuts/{yimiaoban → im}/operations/person.js +10 -10
  33. package/dist/shortcuts/{yimiaoban → im}/operations/registry.js +10 -8
  34. package/dist/shortcuts/{yimiaoban → im}/operations/session.js +82 -83
  35. package/dist/shortcuts/im/operations/shared.js +442 -0
  36. package/dist/shortcuts/{yimiaoban → im}/operations/types.js +1 -1
  37. package/dist/shortcuts/im/operations/user-remind.js +201 -0
  38. package/dist/shortcuts/{yimiaoban → im}/operations/write-util.js +7 -7
  39. package/dist/shortcuts/{yimiaoban → im}/operations.js +3 -3
  40. package/dist/shortcuts/index.js +10 -2
  41. package/dist/shortcuts/invoice/manifest.js +113 -10
  42. package/dist/shortcuts/invoice/operations/import.js +39 -14
  43. package/dist/shortcuts/invoice/operations/reim.js +38 -18
  44. package/dist/shortcuts/invoice/operations/shared.js +35 -6
  45. package/dist/shortcuts/jiuchuanhui/manifest.js +3 -3
  46. package/dist/shortcuts/jiuchuanhui/operations/registry.js +2 -2
  47. package/dist/shortcuts/{yimiaoban → okr}/continuation.js +10 -10
  48. package/dist/shortcuts/okr/errors.js +70 -0
  49. package/dist/shortcuts/okr/host.js +169 -0
  50. package/dist/shortcuts/okr/index.js +155 -0
  51. package/dist/shortcuts/okr/manifest.js +70 -0
  52. package/dist/shortcuts/okr/operations/link.js +305 -0
  53. package/dist/shortcuts/okr/operations/read.js +481 -0
  54. package/dist/shortcuts/okr/operations/registry.js +31 -0
  55. package/dist/shortcuts/okr/operations/shared.js +381 -0
  56. package/dist/shortcuts/okr/operations/types.js +88 -0
  57. package/dist/shortcuts/okr/operations/write.js +838 -0
  58. package/dist/shortcuts/okr/operations.js +8 -0
  59. package/dist/shortcuts/project/continuation.js +70 -0
  60. package/dist/shortcuts/project/errors.js +53 -0
  61. package/dist/shortcuts/project/host.js +160 -0
  62. package/dist/shortcuts/project/index.js +106 -0
  63. package/dist/shortcuts/project/manifest.js +36 -0
  64. package/dist/shortcuts/project/operations/read.js +551 -0
  65. package/dist/shortcuts/project/operations/registry.js +31 -0
  66. package/dist/shortcuts/project/operations/shared.js +68 -0
  67. package/dist/shortcuts/project/operations/types.js +39 -0
  68. package/dist/shortcuts/project/operations/write.js +266 -0
  69. package/dist/shortcuts/project/operations.js +8 -0
  70. package/dist/shortcuts/workflow/continuation.js +508 -0
  71. package/dist/shortcuts/workflow/endpoints.js +149 -0
  72. package/dist/shortcuts/workflow/errors.js +112 -0
  73. package/dist/shortcuts/workflow/host.js +224 -0
  74. package/dist/shortcuts/workflow/index.js +116 -0
  75. package/dist/shortcuts/workflow/manifest.js +30 -0
  76. package/dist/shortcuts/workflow/operations/form.js +4159 -0
  77. package/dist/shortcuts/workflow/operations/read.js +3778 -0
  78. package/dist/shortcuts/workflow/operations/registry.js +33 -0
  79. package/dist/shortcuts/workflow/operations/shared.js +167 -0
  80. package/dist/shortcuts/workflow/operations/types.js +44 -0
  81. package/dist/shortcuts/workflow/operations/write.js +1991 -0
  82. package/dist/shortcuts/workflow/operations.js +119 -0
  83. package/dist/shortcuts/workflow/state.js +182 -0
  84. package/docs/SKILL.md +5 -3
  85. package/docs/_catalog.md +10 -1
  86. package/docs/agent-invoice.md +2 -2
  87. package/docs/agent-skill-install.md +2 -2
  88. package/docs/e10-auth.md +3 -1
  89. package/docs/ebuilder-form.md +38 -0
  90. package/docs/im.md +104 -0
  91. package/docs/invoice.md +5 -5
  92. package/docs/okr.md +122 -0
  93. package/docs/operation-manual.md +10 -7
  94. package/docs/project.md +69 -0
  95. package/docs/skill-review-2026-09-19.md +68 -0
  96. package/package.json +3 -2
  97. package/scripts/package-skill.mjs +38 -11
  98. package/scripts/sync-connector-skills.mjs +172 -0
  99. package/skill-template/business-info.json +357 -16
  100. package/skill-template/domains/ebuilder-form.md +5 -0
  101. package/skill-template/domains/okr.md +18 -0
  102. package/skill-template/domains/shijingran.md +3 -0
  103. package/skill-template/domains/skill-maker.md +4 -4
  104. package/skill-template/domains/workflow.md +22 -0
  105. package/skill-template/domains/yepiaotong.md +4 -2
  106. package/skill-template/domains/yimiaoban.md +13 -10
  107. package/skill-template/product.json +5 -0
  108. package/skill-template/skill-template.md +1 -0
  109. package/skills/weaver-e10-calendar/SKILL.md +5 -1
  110. package/skills/weaver-e10-calendar/references/calendar-query.md +7 -0
  111. package/skills/weaver-e10-calendar/references/source-manifest.json +314 -32
  112. package/skills/weaver-e10-ebuilder-form/SKILL.md +123 -0
  113. package/skills/weaver-e10-ebuilder-form/references/context-routing.md +90 -0
  114. package/skills/weaver-e10-ebuilder-form/references/custom-api.md +86 -0
  115. package/skills/weaver-e10-ebuilder-form/references/disabled-capabilities.md +50 -0
  116. package/skills/weaver-e10-ebuilder-form/references/fields-browser.md +100 -0
  117. package/skills/weaver-e10-ebuilder-form/references/list-nlist.md +91 -0
  118. package/skills/weaver-e10-ebuilder-form/references/openapi-read.md +74 -0
  119. package/skills/weaver-e10-ebuilder-form/references/openapi-write.md +86 -0
  120. package/skills/weaver-e10-ebuilder-form/references/source-manifest.json +107 -0
  121. package/skills/weaver-e10-esb/SKILL.md +1 -1
  122. package/skills/weaver-e10-esb/references/source-manifest.json +15 -14
  123. package/skills/weaver-e10-hrm/SKILL.md +1 -1
  124. package/skills/weaver-e10-hrm/references/source-manifest.json +357 -38
  125. package/skills/weaver-e10-jiuchuanhui/SKILL.md +22 -4
  126. package/skills/weaver-e10-jiuchuanhui/references/clue.md +4 -2
  127. package/skills/weaver-e10-jiuchuanhui/references/contact.md +1 -1
  128. package/skills/weaver-e10-jiuchuanhui/references/customer.md +3 -3
  129. package/skills/weaver-e10-jiuchuanhui/references/disabled-capabilities.md +1 -1
  130. package/skills/weaver-e10-jiuchuanhui/references/discovery-config.md +10 -10
  131. package/skills/weaver-e10-jiuchuanhui/references/general-helpers.md +3 -3
  132. package/skills/weaver-e10-jiuchuanhui/references/sale.md +7 -7
  133. package/skills/weaver-e10-jiuchuanhui/references/source-manifest.json +3 -3
  134. package/skills/weaver-e10-jucailin/SKILL.md +2 -2
  135. package/skills/weaver-e10-mail/SKILL.md +22 -6
  136. package/skills/weaver-e10-meeting/SKILL.md +22 -1
  137. package/skills/weaver-e10-okr/SKILL.md +139 -0
  138. package/skills/weaver-e10-okr/references/okr-align.md +59 -0
  139. package/skills/weaver-e10-okr/references/okr-keyresult.md +68 -0
  140. package/skills/weaver-e10-okr/references/okr-link-routing.md +67 -0
  141. package/skills/weaver-e10-okr/references/okr-query.md +105 -0
  142. package/skills/weaver-e10-okr/references/okr-write.md +94 -0
  143. package/skills/weaver-e10-okr/references/source-manifest.json +672 -0
  144. package/skills/weaver-e10-plan/SKILL.md +3 -3
  145. package/skills/weaver-e10-plan/references/plan-report-write.md +1 -1
  146. package/skills/weaver-e10-plan/references/source-manifest.json +20 -20
  147. package/skills/weaver-e10-qiyecheng/SKILL.md +21 -1
  148. package/skills/weaver-e10-qiyecheng/references/source-manifest.json +17 -17
  149. package/skills/weaver-e10-shared/SKILL.md +79 -11
  150. package/skills/weaver-e10-shared/references/e10-auth-and-session.md +41 -8
  151. package/skills/weaver-e10-shared/references/non-interactive-environment.md +78 -0
  152. package/skills/weaver-e10-shared/references/weaver-e10-installation.md +39 -0
  153. package/skills/weaver-e10-shijingran/SKILL.md +83 -0
  154. package/skills/weaver-e10-shijingran/references/field-resolution.md +105 -0
  155. package/skills/weaver-e10-shijingran/references/operations.md +154 -0
  156. package/skills/weaver-e10-shijingran/references/source-manifest.json +1380 -0
  157. package/skills/weaver-e10-shijingran/references/write-operations.md +90 -0
  158. package/skills/weaver-e10-skill-maker/SKILL.md +26 -9
  159. package/skills/weaver-e10-skill-maker/references/business-skill-generation.md +39 -8
  160. package/skills/weaver-e10-skill-maker/references/detector-validation.md +25 -8
  161. package/skills/weaver-e10-skill-maker/references/module-cli-generation.md +66 -5
  162. package/skills/weaver-e10-skill-maker/references/module-gap-audit-and-repair.md +86 -0
  163. package/skills/weaver-e10-skill-maker/references/source-update-detection.md +59 -5
  164. package/skills/weaver-e10-skill-maker/references/weaver-skill-style.md +16 -8
  165. package/skills/weaver-e10-wenshuding/SKILL.md +28 -4
  166. package/skills/weaver-e10-wenshuding/references/archive-search.md +14 -0
  167. package/skills/weaver-e10-wenshuding/references/source-manifest.json +27 -26
  168. package/skills/weaver-e10-workflow/SKILL.md +250 -0
  169. package/skills/weaver-e10-workflow/references/source-manifest.json +1062 -0
  170. package/skills/weaver-e10-workflow/references/workflow-agent-entry.md +96 -0
  171. package/skills/weaver-e10-workflow/references/workflow-batch.md +275 -0
  172. package/skills/weaver-e10-workflow/references/workflow-create.md +182 -0
  173. package/skills/weaver-e10-workflow/references/workflow-edit.md +106 -0
  174. package/skills/weaver-e10-workflow/references/workflow-memory.md +165 -0
  175. package/skills/weaver-e10-workflow/references/workflow-operate.md +153 -0
  176. package/skills/weaver-e10-workflow/references/workflow-query.md +821 -0
  177. package/skills/weaver-e10-workflow/references/workflow-share.md +117 -0
  178. package/skills/weaver-e10-workflow/references/workflow-view.md +189 -0
  179. package/skills/weaver-e10-yepiaotong/SKILL.md +28 -28
  180. package/skills/weaver-e10-yepiaotong/references/invoice-disabled-capabilities.md +11 -5
  181. package/skills/weaver-e10-yepiaotong/references/invoice-import.md +14 -8
  182. package/skills/weaver-e10-yepiaotong/references/invoice-issuing-examples.md +9 -43
  183. package/skills/weaver-e10-yepiaotong/references/invoice-issuing.md +8 -0
  184. package/skills/weaver-e10-yepiaotong/references/invoice-reim.md +13 -17
  185. package/skills/weaver-e10-yepiaotong/references/reim-api-reference.md +6 -7
  186. package/skills/weaver-e10-yepiaotong/references/reim-examples.md +2 -2
  187. package/skills/weaver-e10-yepiaotong/references/source-manifest.json +1155 -553
  188. package/skills/weaver-e10-yimiaoban/SKILL.md +56 -38
  189. package/skills/weaver-e10-yimiaoban/references/ding-write.md +8 -8
  190. package/skills/weaver-e10-yimiaoban/references/error-codes.md +16 -0
  191. package/skills/weaver-e10-yimiaoban/references/field-resolution.md +10 -10
  192. package/skills/weaver-e10-yimiaoban/references/file-user-i18n.md +12 -12
  193. package/skills/weaver-e10-yimiaoban/references/group-read.md +16 -16
  194. package/skills/weaver-e10-yimiaoban/references/group-write.md +17 -15
  195. package/skills/weaver-e10-yimiaoban/references/msg-read.md +21 -17
  196. package/skills/weaver-e10-yimiaoban/references/msg-write.md +10 -10
  197. package/skills/weaver-e10-yimiaoban/references/person.md +6 -6
  198. package/skills/weaver-e10-yimiaoban/references/safety-boundaries.md +15 -11
  199. package/skills/weaver-e10-yimiaoban/references/session-sysmsg.md +11 -10
  200. package/skills/weaver-e10-yimiaoban/references/source-manifest.json +2164 -154
  201. package/skills/weaver-e10-yimiaoban/references/user-remind.md +75 -0
  202. package/skills/weaver-e10-ziguanjia/SKILL.md +4 -2
  203. package/skills/weaver-e10-ziguanjia/references/source-manifest.json +99 -29
  204. package/tools/weaver-skill-detector-1.0.13/SKILL.md +253 -0
  205. package/tools/weaver-skill-detector-1.0.13/_meta.json +6 -0
  206. package/tools/weaver-skill-detector-1.0.13/references/checklist.md +298 -0
  207. package/tools/weaver-skill-detector-1.0.13/scripts/validate_skill.py +4190 -0
  208. package/dist/shortcuts/yimiaoban/operations/shared.js +0 -236
  209. package/docs/yimiaoban.md +0 -86
  210. package/skills/weaver-e10-calendar/product.json +0 -8
  211. package/skills/weaver-e10-esb/product.json +0 -8
  212. package/skills/weaver-e10-hrm/product.json +0 -8
  213. package/skills/weaver-e10-jiuchuanhui/product.json +0 -8
  214. package/skills/weaver-e10-jucailin/product.json +0 -8
  215. package/skills/weaver-e10-mail/product.json +0 -8
  216. package/skills/weaver-e10-meeting/product.json +0 -8
  217. package/skills/weaver-e10-plan/product.json +0 -8
  218. package/skills/weaver-e10-qiyecheng/product.json +0 -8
  219. package/skills/weaver-e10-skill-maker/product.json +0 -8
  220. package/skills/weaver-e10-wenshuding/product.json +0 -8
  221. package/skills/weaver-e10-yepiaotong/product.json +0 -8
  222. package/skills/weaver-e10-yepiaotong/references/invoice-red.md +0 -261
  223. package/skills/weaver-e10-yimiaoban/product.json +0 -8
  224. package/skills/weaver-e10-ziguanjia/product.json +0 -8
  225. /package/dist/shortcuts/{yimiaoban → im}/operations/msg-parse.js +0 -0
@@ -0,0 +1,1238 @@
1
+ import { randomUUID } from 'node:crypto';
2
+ import { requireE10Session } from '../../internal/e10/context.js';
3
+ import { getActiveProfile } from '../../internal/e10/auth/session.js';
4
+ import { createE10RequestRuntime } from '../../internal/e10/request-runtime.js';
5
+ import { ebuilderAuthError, ebuilderBusinessError, ebuilderNetworkError, ebuilderValidationError, } from './errors.js';
6
+ const PUBLIC_API_PREFIX = '/api/ebuilder';
7
+ const API_PREFIX_PATTERN = /^\/api\/ebuilder(?:\d+)?$/u;
8
+ const SUCCESS_CODES = new Set(['0', '200', 'true']);
9
+ const DIRECT_LIST_TYPES = {
10
+ list: { pageType: 'SEARCH', listKind: 'List' },
11
+ search: { pageType: 'SEARCH', listKind: 'List' },
12
+ searchlist: { pageType: 'SEARCH', listKind: 'List' },
13
+ viewport: { pageType: 'VIEWPORT', listKind: 'NList' },
14
+ nlist: { pageType: 'VIEWPORT', listKind: 'NList' },
15
+ view: { pageType: 'VIEWPORT', listKind: 'NList' },
16
+ };
17
+ const OPENAPI_READ_PATHS = {
18
+ detail: '/form/dataset/v2/getDataById',
19
+ query: '/form/dataset/v2/getAllData',
20
+ count: '/form/dataset/v2/getAllDataCount',
21
+ };
22
+ const OPENAPI_WRITE_PATHS = {
23
+ add: '/form/dataset/v2/saveFormData',
24
+ update: '/form/dataset/v2/updateFormData',
25
+ delete: '/form/dataset/v2/deleteData',
26
+ };
27
+ const CUSTOM_READ_PATHS = {
28
+ detail: '/form/formdata/v2/getFormDataByPk',
29
+ query: '/form/formdata/v2/getFormDataList',
30
+ count: '/form/formdata/v2/getFormDataCount',
31
+ };
32
+ const CUSTOM_WRITE_PATHS = {
33
+ add: '/form/formdata/v2/saveFormData',
34
+ update: '/form/formdata/v2/updateFormData',
35
+ delete: '/form/formdata/v2/deleteFormData',
36
+ };
37
+ export function resolveApiPrefix(value) {
38
+ const raw = (value || PUBLIC_API_PREFIX).trim();
39
+ if (!API_PREFIX_PATTERN.test(raw)) {
40
+ throw ebuilderValidationError('api_prefix_invalid', 'apiPrefix 必须是 /api/ebuilder 或 /api/ebuilder{appId}');
41
+ }
42
+ return raw;
43
+ }
44
+ export function routeEbuilderPath(apiPrefix, suffix) {
45
+ const prefix = resolveApiPrefix(apiPrefix);
46
+ if (!suffix.startsWith('/') || suffix.startsWith('/api/') || suffix.includes('/../')) {
47
+ throw ebuilderValidationError('path_invalid', 'eBuilder API suffix 必须是以 / 开头且不含 /api 前缀的相对路径');
48
+ }
49
+ const routedPrefix = suffix === '/form' || suffix.startsWith('/form/') ? prefix : PUBLIC_API_PREFIX;
50
+ return routedPrefix + suffix;
51
+ }
52
+ export function parseDirectListUrl(value, expectedOrigin) {
53
+ const raw = String(value || '').trim();
54
+ if (!raw)
55
+ throw ebuilderValidationError('url_required', '缺少页面 URL');
56
+ const confirmedOrigin = expectedOrigin ? originOnly(expectedOrigin) : originFromUrl(raw);
57
+ if (!confirmedOrigin)
58
+ throw ebuilderValidationError('url_origin_required', '相对页面 URL 还需提供当前 E10 origin');
59
+ const parsed = parseUrl(raw, confirmedOrigin);
60
+ const urlOrigin = parsed.origin === 'null' ? '' : parsed.origin;
61
+ if (/^https?:/iu.test(raw) && urlOrigin !== confirmedOrigin) {
62
+ throw ebuilderValidationError('url_cross_origin', '页面 URL 与当前 origin 不同源');
63
+ }
64
+ const entryUrl = /^https?:/iu.test(raw) ? raw : confirmedOrigin + raw;
65
+ const segments = parsed.pathname.split('/').filter(Boolean);
66
+ const pathMarkers = segments
67
+ .map((item) => item.toLowerCase())
68
+ .filter((item) => DIRECT_LIST_TYPES[item]);
69
+ if (new Set(pathMarkers.map((item) => DIRECT_LIST_TYPES[item].pageType)).size > 1) {
70
+ throw ebuilderValidationError('url_type_conflict', '页面 URL 路径同时包含表格和数据列表类型');
71
+ }
72
+ let pathType = '';
73
+ let pathId = '';
74
+ let pathAppId = '';
75
+ let pathMenuId = '';
76
+ if (segments.length >= 2) {
77
+ const marker = segments[segments.length - 2].toLowerCase();
78
+ const candidate = segments[segments.length - 1];
79
+ if (DIRECT_LIST_TYPES[marker]) {
80
+ if (/^\d+$/u.test(candidate)) {
81
+ pathType = marker;
82
+ pathId = candidate;
83
+ }
84
+ else {
85
+ const combined = /^(\d+)_(\d+)$/u.exec(candidate);
86
+ const menu = /^(\d+)-(\d+)$/u.exec(candidate);
87
+ if (combined) {
88
+ pathType = marker;
89
+ pathAppId = combined[1];
90
+ pathId = combined[2];
91
+ }
92
+ else if (menu
93
+ && segments.length >= 4
94
+ && ['view', 'detail'].includes(segments[segments.length - 4].toLowerCase())
95
+ && /^\d+$/u.test(segments[segments.length - 3])) {
96
+ pathType = marker;
97
+ pathAppId = segments[segments.length - 3];
98
+ pathId = menu[1];
99
+ pathMenuId = menu[2];
100
+ }
101
+ }
102
+ }
103
+ }
104
+ const lowerParams = lowerSearchParams(parsed);
105
+ const queryTypes = ['pagetype', 'listkind', 'viewtype']
106
+ .flatMap((key) => lowerParams.get(key) || [])
107
+ .map((item) => item.trim().toLowerCase())
108
+ .filter((item) => DIRECT_LIST_TYPES[item]);
109
+ let queryType = '';
110
+ if (queryTypes.length > 0) {
111
+ if (new Set(queryTypes.map((item) => DIRECT_LIST_TYPES[item].pageType)).size !== 1) {
112
+ throw ebuilderValidationError('url_type_conflict', '页面 URL 的列表类型参数互相冲突');
113
+ }
114
+ queryType = queryTypes[0];
115
+ }
116
+ if (pathType && queryType && DIRECT_LIST_TYPES[pathType].pageType !== DIRECT_LIST_TYPES[queryType].pageType) {
117
+ throw ebuilderValidationError('url_type_conflict', '页面 URL 的路径与 pageType/listKind 类型冲突');
118
+ }
119
+ const marker = pathType || queryType;
120
+ if (!marker)
121
+ throw ebuilderValidationError('url_type_missing', '无法从页面 URL 识别列表类型;需要 list/search 或 viewport/VIEWPORT');
122
+ const queryIds = ['pageid', 'listid'].flatMap((key) => lowerParams.get(key) || []).filter(Boolean);
123
+ if (new Set(queryIds).size > 1)
124
+ throw ebuilderValidationError('url_id_conflict', '页面 URL 中的 pageId/listId 互相冲突');
125
+ const queryId = queryIds[0] || '';
126
+ if (pathId && queryId && pathId !== queryId)
127
+ throw ebuilderValidationError('url_id_conflict', '页面 URL 路径 ID 与 pageId/listId 参数不一致');
128
+ const pageId = pathId || queryId;
129
+ if (!pageId)
130
+ throw ebuilderValidationError('url_id_missing', '页面 URL 缺少可识别的 pageId/listId');
131
+ assertDecimalId(pageId, 'URL pageId/listId');
132
+ const appIds = [];
133
+ if (pathAppId)
134
+ appIds.push(pathAppId);
135
+ appIds.push(...(lowerParams.get('appid') || []));
136
+ const appPath = /\/(?:sp\/)?ebdapp\/(?:view|detail)\/(\d+)(?:\/|$)/iu.exec(parsed.pathname);
137
+ if (appPath)
138
+ appIds.push(appPath[1]);
139
+ if (new Set(appIds.filter(Boolean)).size > 1)
140
+ throw ebuilderValidationError('url_app_conflict', '页面 URL 中的 appId 互相冲突');
141
+ const menuIds = [];
142
+ if (pathMenuId)
143
+ menuIds.push(pathMenuId);
144
+ menuIds.push(...['cusmenuid', 'menuid'].flatMap((key) => lowerParams.get(key) || []));
145
+ if (new Set(menuIds.filter(Boolean)).size > 1)
146
+ throw ebuilderValidationError('url_menu_conflict', '页面 URL 中的 menuId/cusMenuId 互相冲突');
147
+ const type = DIRECT_LIST_TYPES[marker];
148
+ return {
149
+ origin: confirmedOrigin,
150
+ entryUrl,
151
+ pageId,
152
+ pageType: type.pageType,
153
+ listKind: type.listKind,
154
+ appId: appIds.find(Boolean) || '',
155
+ menuId: menuIds.find(Boolean) || '',
156
+ };
157
+ }
158
+ export function encodeNlistCustomConfig(value) {
159
+ const source = JSON.stringify(value);
160
+ if (!source)
161
+ return '';
162
+ const dictionary = new Map();
163
+ const output = [];
164
+ let nextCode = 1 << 16;
165
+ let current = source[0];
166
+ function codeFor(token) {
167
+ const existing = dictionary.get(token);
168
+ if (existing !== undefined)
169
+ return existing;
170
+ if (token.length !== 1)
171
+ throw ebuilderValidationError('custom_config_invalid', `无法编码 customConfig 片段: ${token}`);
172
+ const code = token.codePointAt(0);
173
+ dictionary.set(token, code);
174
+ return code;
175
+ }
176
+ for (const char of Array.from(source.slice(1))) {
177
+ const combined = current + char;
178
+ if (dictionary.has(combined)) {
179
+ current = combined;
180
+ continue;
181
+ }
182
+ output.push(codeFor(current));
183
+ dictionary.set(combined, nextCode);
184
+ nextCode += 1;
185
+ current = char;
186
+ }
187
+ output.push(codeFor(current));
188
+ return output.join(',');
189
+ }
190
+ export function pickEbuilderDataKey(field) {
191
+ const config = record(field.config);
192
+ const name = stringValue(field.name);
193
+ const id = stringValue(field.id);
194
+ const dataKey = stringValue(config.dataKey);
195
+ if (name && name !== id)
196
+ return name;
197
+ return dataKey || name;
198
+ }
199
+ export function createEbuilderFormHost(ctx) {
200
+ let e10Session = null;
201
+ let sessionContext = null;
202
+ async function ensureSession() {
203
+ if (!e10Session) {
204
+ e10Session = await requireE10Session(ctx);
205
+ sessionContext = {
206
+ baseUrl: e10Session.getBaseUrl(),
207
+ profile: ctx.getGlobalOptions().profile || getActiveProfile(),
208
+ userId: e10Session.userId,
209
+ };
210
+ }
211
+ return e10Session;
212
+ }
213
+ async function getSession() {
214
+ await ensureSession();
215
+ return sessionContext;
216
+ }
217
+ async function runtime() {
218
+ return createE10RequestRuntime(ctx, await ensureSession(), {});
219
+ }
220
+ async function requestJson(options) {
221
+ assertApiPath(options.path);
222
+ const response = await (await runtime()).requestJson({
223
+ module: 'ebuilder-form',
224
+ operation: options.operation,
225
+ method: options.method || 'POST',
226
+ path: options.path,
227
+ query: options.query,
228
+ body: options.body,
229
+ form: options.form,
230
+ formData: options.formData,
231
+ headers: options.headers,
232
+ bigIntStrings: options.bigIntStrings ?? true,
233
+ });
234
+ if ([302, 401, 403].includes(response.status)) {
235
+ throw ebuilderAuthError('session_expired', 'E10 登录态已失效或当前账号无权访问该 eBuilder 数据', { httpStatus: response.status });
236
+ }
237
+ if (response.parseError) {
238
+ throw ebuilderNetworkError('protocol', `接口返回不是合法 JSON(HTTP ${response.status})`, { retryable: false });
239
+ }
240
+ if (response.status < 200 || response.status >= 300) {
241
+ throw ebuilderBusinessError('http_error', `eBuilder 接口 HTTP ${response.status}`, { code: response.status, details: { message: responseMessage(response.value) } });
242
+ }
243
+ return response.value;
244
+ }
245
+ async function resolveDeployment(appId) {
246
+ const id = assertDecimalId(appId, 'appId');
247
+ const payload = await requestJson({
248
+ operation: 'ebuilder-form.context.resolve',
249
+ method: 'GET',
250
+ path: '/api/ebuilder/form/app/isIndependentDeploy',
251
+ query: { appId: id },
252
+ });
253
+ const data = unwrapWeaData(payload, '独立部署检查');
254
+ if (typeof data !== 'boolean')
255
+ throw ebuilderBusinessError('deployment_response_invalid', '独立部署检查 data 不是布尔值');
256
+ return data ? `/api/ebuilder${id}` : PUBLIC_API_PREFIX;
257
+ }
258
+ async function searchMenus(keywords) {
259
+ const normalized = keywords.map((item) => item.trim()).filter(Boolean).slice(0, 5);
260
+ if (normalized.length === 0)
261
+ throw ebuilderValidationError('keywords_required', 'menuKeywords 至少包含一个关键词');
262
+ const payload = await requestJson({
263
+ operation: 'ebuilder-form.menu.search',
264
+ path: '/api/ebuilder/form/viewlist/searchPermittedEbMenus',
265
+ body: normalized,
266
+ });
267
+ const data = unwrapWeaData(payload, '菜单搜索');
268
+ if (!Array.isArray(data))
269
+ throw ebuilderBusinessError('menu_response_invalid', '菜单搜索 data 不是数组');
270
+ return data
271
+ .filter((item) => Boolean(item && typeof item === 'object' && !Array.isArray(item)))
272
+ .filter((item) => {
273
+ const type = stringValue(item.pageType).toUpperCase();
274
+ return ['SEARCH', 'VIEWPORT'].includes(type) && stringValue(item.pageId) && stringValue(item.appId);
275
+ });
276
+ }
277
+ async function resolveContext(input) {
278
+ if (input.context && typeof input.context === 'object' && !Array.isArray(input.context)) {
279
+ return validateContext(input.context);
280
+ }
281
+ if (input.url)
282
+ return resolveUrlContext(input);
283
+ if (input.menuKeywords || input.menu || input.menuId)
284
+ return resolveMenuContext(input);
285
+ return resolveDirectContext(input);
286
+ }
287
+ async function resolveMenuContext(input) {
288
+ const keywords = Array.isArray(input.menuKeywords)
289
+ ? input.menuKeywords.map((item) => String(item))
290
+ : Array.isArray(input.menu)
291
+ ? input.menu.map((item) => String(item))
292
+ : [String(input.menuKeywords || input.menu || '')];
293
+ const menuId = stringValue(input.menuId);
294
+ const menus = await searchMenus(keywords);
295
+ const menu = selectMenu(menus, keywords, menuId);
296
+ const appId = assertDecimalId(menu.appId, 'menu.appId');
297
+ const pageId = assertDecimalId(menu.pageId, 'menu.pageId');
298
+ const pageType = stringValue(menu.pageType).toUpperCase();
299
+ const apiPrefix = await resolveDeployment(appId);
300
+ const target = await resolveListTarget(apiPrefix, appId, pageId, pageType);
301
+ return validateContext({
302
+ origin: (await getSession()).baseUrl,
303
+ appId,
304
+ apiPrefix,
305
+ listId: target.listId,
306
+ hostPageId: pageType === 'VIEWPORT' ? pageId : '',
307
+ listKind: pageType === 'SEARCH' ? 'List' : 'NList',
308
+ menuId: stringValue(menu.menuId),
309
+ menuName: stringValue(menu.menuName || menu.refferName),
310
+ menuUrl: stringValue(menu.menuUrl),
311
+ objId: target.objId,
312
+ });
313
+ }
314
+ async function resolveUrlContext(input) {
315
+ const target = parseDirectListUrl(input.url, (await getSession()).baseUrl);
316
+ const requestedAppId = stringValue(input.appId);
317
+ if (requestedAppId && target.appId && requestedAppId !== target.appId) {
318
+ throw ebuilderValidationError('app_conflict', 'appId 与页面 URL 中的 appId 不一致');
319
+ }
320
+ let appId = requestedAppId || target.appId;
321
+ const requestedPrefix = input.apiPrefix ? resolveApiPrefix(String(input.apiPrefix)) : '';
322
+ if (requestedPrefix && requestedPrefix !== PUBLIC_API_PREFIX) {
323
+ const prefixAppId = requestedPrefix.replace(PUBLIC_API_PREFIX, '');
324
+ if (appId && appId !== prefixAppId)
325
+ throw ebuilderValidationError('api_prefix_conflict', 'apiPrefix 与页面 URL/输入 appId 不一致');
326
+ appId = prefixAppId;
327
+ }
328
+ let apiPrefix = requestedPrefix;
329
+ let listId = target.pageId;
330
+ let objId = '';
331
+ if (appId) {
332
+ apiPrefix = apiPrefix || await resolveDeployment(appId);
333
+ const resolved = await resolveListTarget(apiPrefix, appId, target.pageId, target.pageType);
334
+ listId = resolved.listId;
335
+ objId = resolved.objId;
336
+ }
337
+ else if (target.pageType === 'SEARCH') {
338
+ const list = await listInit(PUBLIC_API_PREFIX, target.pageId, '');
339
+ appId = list.appId;
340
+ objId = list.objId;
341
+ apiPrefix = apiPrefix || await resolveDeployment(appId);
342
+ }
343
+ else {
344
+ const page = await viewPageInfo(PUBLIC_API_PREFIX, target.pageId, 'ebuilder-form.context.resolve');
345
+ const parsed = parseNlistContext(page, '');
346
+ appId = parsed.appId;
347
+ listId = parsed.listId;
348
+ objId = parsed.objId;
349
+ apiPrefix = apiPrefix || await resolveDeployment(appId);
350
+ }
351
+ return validateContext({
352
+ origin: target.origin,
353
+ appId,
354
+ apiPrefix,
355
+ hostPageId: target.pageType === 'VIEWPORT' ? target.pageId : '',
356
+ listId,
357
+ listKind: target.listKind,
358
+ menuId: target.menuId,
359
+ entryUrl: target.entryUrl,
360
+ objId,
361
+ });
362
+ }
363
+ async function resolveDirectContext(input) {
364
+ const appId = stringValue(input.appId);
365
+ const apiPrefix = input.apiPrefix ? resolveApiPrefix(String(input.apiPrefix)) : (appId ? await resolveDeployment(appId) : PUBLIC_API_PREFIX);
366
+ const context = {
367
+ origin: (await getSession()).baseUrl,
368
+ appId,
369
+ apiPrefix,
370
+ objId: stringValue(input.objId),
371
+ listId: stringValue(input.listId),
372
+ hostPageId: stringValue(input.hostPageId),
373
+ listKind: stringValue(input.listKind),
374
+ menuUrl: stringValue(input.menuUrl),
375
+ entryUrl: stringValue(input.entryUrl),
376
+ menuId: stringValue(input.menuId),
377
+ menuName: stringValue(input.menuName),
378
+ };
379
+ if (!context.objId && context.listId && context.appId && context.listKind === 'List') {
380
+ const list = await listInit(context.apiPrefix, context.listId, context.appId);
381
+ context.objId = list.objId;
382
+ }
383
+ if ((!context.objId || !context.listId) && context.hostPageId && context.appId && context.listKind === 'NList') {
384
+ const page = await viewPageInfo(context.apiPrefix, context.hostPageId, 'ebuilder-form.context.resolve');
385
+ const parsed = parseNlistContext(page, context.appId);
386
+ context.listId = parsed.listId;
387
+ context.objId = parsed.objId;
388
+ }
389
+ return validateContext(context);
390
+ }
391
+ function validateContext(raw) {
392
+ const apiPrefix = resolveApiPrefix(stringValue(raw.apiPrefix) || PUBLIC_API_PREFIX);
393
+ const appId = stringValue(raw.appId);
394
+ if (apiPrefix !== PUBLIC_API_PREFIX) {
395
+ const prefixAppId = apiPrefix.replace(PUBLIC_API_PREFIX, '');
396
+ if (appId && prefixAppId !== appId)
397
+ throw ebuilderValidationError('context_conflict', 'apiPrefix 中的 appId 与 context.appId 不一致');
398
+ }
399
+ const objId = stringValue(raw.objId);
400
+ if (!objId)
401
+ throw ebuilderValidationError('obj_id_required', 'context.objId 必填');
402
+ return {
403
+ origin: stringValue(raw.origin) || sessionContext?.baseUrl || '',
404
+ appId,
405
+ apiPrefix,
406
+ objId: assertDecimalId(objId, 'objId'),
407
+ listId: optionalDecimalId(raw.listId, 'listId'),
408
+ hostPageId: optionalDecimalId(raw.hostPageId, 'hostPageId'),
409
+ listKind: normalizeListKind(raw.listKind),
410
+ menuId: optionalDecimalId(raw.menuId, 'menuId'),
411
+ menuName: stringValue(raw.menuName),
412
+ menuUrl: stringValue(raw.menuUrl),
413
+ entryUrl: stringValue(raw.entryUrl),
414
+ };
415
+ }
416
+ async function resolveListTarget(apiPrefix, appId, pageId, pageType) {
417
+ if (pageType === 'SEARCH') {
418
+ const list = await listInit(apiPrefix, pageId, appId);
419
+ return { listId: pageId, objId: list.objId };
420
+ }
421
+ if (pageType !== 'VIEWPORT')
422
+ throw ebuilderValidationError('page_type_invalid', `不支持的 pageType: ${pageType}`);
423
+ const page = await viewPageInfo(apiPrefix, pageId, 'ebuilder-form.context.resolve');
424
+ const parsed = parseNlistContext(page, appId);
425
+ return { listId: parsed.listId, objId: parsed.objId };
426
+ }
427
+ async function listInit(apiPrefix, listId, expectedAppId) {
428
+ const payload = await requestJson({
429
+ operation: 'ebuilder-form.context.resolve',
430
+ path: routeEbuilderPath(apiPrefix, '/form/list/listInit'),
431
+ body: { listId },
432
+ });
433
+ const data = unwrapWeaData(payload, '表格上下文');
434
+ const recordData = record(data);
435
+ const appId = stringValue(recordData.appId);
436
+ if (expectedAppId && appId && appId !== expectedAppId)
437
+ throw ebuilderValidationError('app_conflict', '表格上下文 appId 与已确认 appId 不一致');
438
+ const objId = stringValue(recordData.objid || recordData.objId);
439
+ if (!objId)
440
+ throw ebuilderBusinessError('list_context_invalid', '表格上下文缺少 objId');
441
+ return { appId: appId || expectedAppId, objId, data: recordData };
442
+ }
443
+ async function viewPageInfo(apiPrefix, pageId, operation) {
444
+ return requestJson({
445
+ operation,
446
+ method: 'GET',
447
+ path: routeEbuilderPath(apiPrefix, '/form/viewdesigner/viewPageInfo'),
448
+ query: { pageId, terminalType: 'PC', entranceType: 'RUN', runType: '' },
449
+ });
450
+ }
451
+ function parseNlistContext(payload, expectedAppId) {
452
+ unwrapWeaData(payload, '数据列表上下文');
453
+ const page = unwrapPage(payload);
454
+ const { component, config } = selectPrimaryNlistComponent(page);
455
+ const appId = stringValue(page.appId || page.appid);
456
+ if (expectedAppId && appId && appId !== expectedAppId)
457
+ throw ebuilderValidationError('app_conflict', '数据列表上下文 appId 与已确认 appId 不一致');
458
+ const listId = stringValue(config.ebListId);
459
+ const objId = stringValue(config.objId);
460
+ if (!listId || !objId)
461
+ throw ebuilderBusinessError('nlist_context_invalid', 'data.page.comps[1].config 缺少 ebListId 或 objId');
462
+ return { appId: appId || expectedAppId, listId, objId, page, component, config };
463
+ }
464
+ async function formViews(input) {
465
+ const context = await resolveContext(input);
466
+ const payload = await requestJson({
467
+ operation: 'ebuilder-form.views.list',
468
+ method: 'GET',
469
+ path: routeEbuilderPath(context.apiPrefix, `/form/viewlist/getViewlist/${context.objId}`),
470
+ query: { objId: context.objId },
471
+ });
472
+ const data = unwrapWeaData(payload, '表单视图');
473
+ const views = Array.isArray(data) ? data : [];
474
+ return {
475
+ context,
476
+ views: views.filter((item) => {
477
+ const type = stringValue(item?.viewType || item?.type || item?.pageType).toLowerCase();
478
+ return type === 'list' || type === 'nlist';
479
+ }),
480
+ raw: data,
481
+ };
482
+ }
483
+ async function listConfig(context, operation) {
484
+ if (!context.listId)
485
+ throw ebuilderValidationError('list_id_required', 'List 配置需要 listId');
486
+ const list = await listInit(context.apiPrefix, context.listId, context.appId);
487
+ const common = { apid: context.appId || list.appId, tpaid: context.listId, objId: context.objId };
488
+ const conditionId = stringValue(list.data.conditionId);
489
+ const [baseInfo, listFields, order, search, statistics, condition] = await Promise.all([
490
+ requestJson({ operation, method: 'GET', path: routeEbuilderPath(context.apiPrefix, `/form/list/getBaseInfo/${context.listId}`), query: common }),
491
+ requestJson({ operation, method: 'GET', path: routeEbuilderPath(context.apiPrefix, `/form/list/getListFields/${context.listId}`), query: { ...common, clientType: 'PC', isVirtualForm: 'false' } }),
492
+ requestJson({ operation, method: 'GET', path: routeEbuilderPath(context.apiPrefix, '/form/viewlist/getViewOrder'), query: { ...common, viewType: 'tableView', viewId: context.listId, isVirtualForm: 'false' } }),
493
+ requestJson({ operation, method: 'GET', path: routeEbuilderPath(context.apiPrefix, `/form/listFilter/getSearchFilters/${context.listId}`), query: { ...common, viewType: 'tableView', isVirtualForm: 'false' } }),
494
+ requestJson({ operation, method: 'GET', path: routeEbuilderPath(context.apiPrefix, `/form/listStatistics/getStatisticsList/${context.listId}`), query: { ...common, isVirtualForm: 'false' } }),
495
+ conditionId
496
+ ? requestJson({ operation, method: 'GET', path: routeEbuilderPath(context.apiPrefix, '/form/plugin/conditionEditor/getById'), query: { ...common, id: conditionId, type: 'search' } })
497
+ : Promise.resolve(null),
498
+ ]);
499
+ return {
500
+ context,
501
+ init: list.data,
502
+ config: {
503
+ basicSettings: dataOrRaw(baseInfo),
504
+ displayColumns: dataOrRaw(listFields),
505
+ sorting: dataOrRaw(order),
506
+ search: dataOrRaw(search),
507
+ statistics: dataOrRaw(statistics),
508
+ conditions: condition ? { configured: Boolean(dataOrRaw(condition)), rootData: dataOrRaw(condition) } : { configured: false },
509
+ },
510
+ };
511
+ }
512
+ async function nlistConfig(context, operation) {
513
+ if (!context.hostPageId)
514
+ throw ebuilderValidationError('host_page_id_required', 'NList 配置需要 hostPageId');
515
+ const payload = await viewPageInfo(context.apiPrefix, context.hostPageId, operation);
516
+ const parsed = parseNlistContext(payload, context.appId);
517
+ return {
518
+ context: { ...context, listId: parsed.listId, objId: parsed.objId },
519
+ page: parsed.page,
520
+ component: parsed.component,
521
+ config: parsed.config,
522
+ };
523
+ }
524
+ async function listQuery(input, operation) {
525
+ const context = await resolveContext({ ...input, listKind: input.listKind || input.context?.listKind || 'List' });
526
+ if (!context.listId)
527
+ throw ebuilderValidationError('list_id_required', 'List 分页查询需要 listId');
528
+ const list = await listInit(context.apiPrefix, context.listId, context.appId);
529
+ const pageNo = positiveInt(input.pageNo, 'pageNo', 1);
530
+ const configuredSize = positiveInt(list.data.pageSize, 'listInit.pageSize', 20, 200);
531
+ const pageSize = pageSizeInt(input.pageSize, configuredSize, 200);
532
+ const allPages = input.allPages === true;
533
+ const tableuid = stringValue(input.tableuid) || randomUUID().replace(/-/gu, '');
534
+ const searchParamData = nonEmptyRecord(input.searchParamData) || { searchType: '2', type: 'and', groupFields: {} };
535
+ const filterCondition = nonEmptyRecord(input.filterCondition) || {};
536
+ const passid = stringValue(input.passid) || context.objId;
537
+ const cid = stringValue(input.cid) || context.objId;
538
+ const body = {
539
+ current: pageNo,
540
+ pageSize,
541
+ requiredMatch: 1,
542
+ tableuid,
543
+ listId: context.listId,
544
+ searchParamData,
545
+ isFollow: 0,
546
+ isShareToMe: 0,
547
+ filterCondition,
548
+ terminal: 'PC',
549
+ ...(Array.isArray(input.orderParams) ? { orderParams: input.orderParams } : {}),
550
+ };
551
+ const query = { passid, cid };
552
+ const firstPage = await requestJson({ operation, path: routeEbuilderPath(context.apiPrefix, '/form/list/getListDatas'), query, body });
553
+ const totalPayload = await requestJson({ operation, path: routeEbuilderPath(context.apiPrefix, '/form/list/getListDataCount'), query, body });
554
+ const total = extractListTotal(totalPayload);
555
+ const pageCount = Math.ceil(total / pageSize);
556
+ if (allPages && pageCount >= 100) {
557
+ throw ebuilderValidationError('too_many_pages', '预计需要 100 页或更多,请增加筛选条件或缩小范围', { total, pageSize, pageCount });
558
+ }
559
+ const pages = [listPageResult(pageNo, firstPage)];
560
+ if (allPages) {
561
+ for (let next = 1; next <= pageCount; next += 1) {
562
+ if (next === pageNo)
563
+ continue;
564
+ const page = await requestJson({
565
+ operation,
566
+ path: routeEbuilderPath(context.apiPrefix, '/form/list/getListDatas'),
567
+ query,
568
+ body: { ...body, current: next },
569
+ });
570
+ pages.push(listPageResult(next, page));
571
+ }
572
+ pages.sort((a, b) => a.pageNo - b.pageNo);
573
+ }
574
+ const rows = pages.flatMap((page) => page.rows);
575
+ return {
576
+ context,
577
+ listUrl: buildListUrl(context),
578
+ tableuid,
579
+ columns: pages[0]?.columns || [],
580
+ pages,
581
+ rows,
582
+ pagination: {
583
+ mainRecordTotal: total,
584
+ mainPageCount: pageCount,
585
+ returnedRowCount: rows.length,
586
+ distinctStableIdCount: new Set(rows.map(stableRowId).filter(Boolean)).size,
587
+ },
588
+ };
589
+ }
590
+ async function nlistQuery(input, operation) {
591
+ const context = await resolveContext({ ...input, listKind: input.listKind || input.context?.listKind || 'NList' });
592
+ if (!context.hostPageId)
593
+ throw ebuilderValidationError('host_page_id_required', 'NList 分页查询需要 hostPageId');
594
+ const configResult = await nlistConfig(context, operation);
595
+ const page = record(configResult.page);
596
+ const component = record(configResult.component);
597
+ const config = record(configResult.config);
598
+ const compId = stringValue(input.compId) || stringValue(component.id || component.compId);
599
+ if (!compId)
600
+ throw ebuilderBusinessError('nlist_context_invalid', 'NList 组件缺少 compId');
601
+ const pageSize = pageSizeInt(input.pageSize, Number(config.pageSize || config.detailPageSize || 10), 200);
602
+ const pageNo = positiveInt(input.pageNo, 'pageNo', 1);
603
+ const tableuid = stringValue(input.tableuid) || randomUUID().replace(/-/gu, '');
604
+ const filter = input.filter === undefined ? [baseNlistFilter(compId)] : input.filter;
605
+ const fieldFilter = input.fieldFilter === undefined ? {} : input.fieldFilter;
606
+ const pageFilter = input.pageFilter === undefined ? { filter: {}, filterIsNull: true, esbFilter: [] } : input.pageFilter;
607
+ const customConfig = nonEmptyRecord(input.customConfig) || { superiorField: { enable: false } };
608
+ const refPluginPackageId = stringValue(input.refPluginPackageId || config.refPluginPackageId || component.refPluginPackageId) || '933846626099339271';
609
+ const pageAppId = stringValue(page.appId || page.appid || context.appId);
610
+ const commonFields = {
611
+ pageAppId,
612
+ pageId: context.hostPageId,
613
+ compId,
614
+ pageSize,
615
+ tableuid,
616
+ filter: JSON.stringify(filter),
617
+ fieldFilter: JSON.stringify(fieldFilter),
618
+ pageFilter: JSON.stringify(pageFilter),
619
+ refPluginPackageId,
620
+ customConfig: encodeNlistCustomConfig(customConfig),
621
+ };
622
+ const firstPage = await requestJson({
623
+ operation,
624
+ path: routeEbuilderPath(context.apiPrefix, '/form/eblist/getData'),
625
+ form: new URLSearchParams({ ...stringifyForm(commonFields), pageNo: String(pageNo) }),
626
+ });
627
+ const totalPayload = await requestJson({
628
+ operation,
629
+ path: routeEbuilderPath(context.apiPrefix, '/form/eblist/getCount'),
630
+ query: { passid: context.objId },
631
+ form: new URLSearchParams({ tableuid, pageId: context.hostPageId, compId, refPluginPackageId }),
632
+ });
633
+ const total = extractNlistTotal(totalPayload);
634
+ const pageCount = Math.ceil(total / pageSize);
635
+ if (input.allPages === true && pageCount >= 100) {
636
+ throw ebuilderValidationError('too_many_pages', '预计需要 100 页或更多,请增加筛选条件或缩小范围', { total, pageSize, pageCount });
637
+ }
638
+ const pages = [nlistPageResult(pageNo, firstPage)];
639
+ if (input.allPages === true) {
640
+ for (let next = 1; next <= pageCount; next += 1) {
641
+ if (next === pageNo)
642
+ continue;
643
+ const pagePayload = await requestJson({
644
+ operation,
645
+ path: routeEbuilderPath(context.apiPrefix, '/form/eblist/getData'),
646
+ form: new URLSearchParams({ ...stringifyForm(commonFields), pageNo: String(next) }),
647
+ });
648
+ pages.push(nlistPageResult(next, pagePayload));
649
+ }
650
+ pages.sort((a, b) => a.pageNo - b.pageNo);
651
+ }
652
+ const rows = pages.flatMap((pageItem) => pageItem.rows);
653
+ return {
654
+ context: { ...context, listId: stringValue(config.ebListId), objId: stringValue(config.objId) },
655
+ listUrl: buildListUrl(context),
656
+ tableuid,
657
+ pages,
658
+ rows,
659
+ pagination: {
660
+ mainRecordTotal: total,
661
+ mainPageCount: pageCount,
662
+ returnedRowCount: rows.length,
663
+ distinctStableIdCount: new Set(rows.map(stableRowId).filter(Boolean)).size,
664
+ },
665
+ };
666
+ }
667
+ async function fields(input, operation) {
668
+ const context = await resolveContext(input);
669
+ const form = new FormData();
670
+ form.set('groupId', stringValue(input.groupId) || context.appId);
671
+ form.set('sourceId', stringValue(input.sourceId) || context.objId);
672
+ form.set('sourceType', stringValue(input.sourceType) || 'FORM');
673
+ form.set('detailFieldsGroup', String(input.detailFieldsGroup ?? 'true'));
674
+ form.set('enableMerge', String(input.enableMerge ?? 'true'));
675
+ form.set('customParam', typeof input.customParam === 'string' ? input.customParam : JSON.stringify(input.customParam ?? { terminal: '' }));
676
+ form.set('params', typeof input.params === 'string' ? input.params : JSON.stringify(input.params ?? []));
677
+ return requestJson({
678
+ operation,
679
+ path: routeEbuilderPath(context.apiPrefix, '/common/ds/fields'),
680
+ formData: form,
681
+ bigIntStrings: true,
682
+ });
683
+ }
684
+ async function options(input, operation) {
685
+ const context = await resolveContext(input);
686
+ return requestJson({
687
+ operation,
688
+ method: 'GET',
689
+ path: routeEbuilderPath(context.apiPrefix, '/common/ds/options'),
690
+ query: {
691
+ fieldName: assertDecimalId(input.fieldName || input.fieldId, 'fieldName'),
692
+ objId: stringValue(input.objId) || context.objId,
693
+ sourceType: stringValue(input.sourceType) || 'FORM',
694
+ groupId: stringValue(input.groupId) || context.appId,
695
+ optionLevel: input.optionLevel ?? '1',
696
+ },
697
+ });
698
+ }
699
+ async function mapping(input, operation) {
700
+ const context = await resolveContext(input);
701
+ return requestJson({
702
+ operation,
703
+ method: 'GET',
704
+ path: routeEbuilderPath(context.apiPrefix, '/form/dataset/v1/getTableMapping'),
705
+ query: { objId: context.objId, reverse: input.reverse === true ? 'true' : 'false' },
706
+ });
707
+ }
708
+ async function browserResolve(input, operation) {
709
+ const moduleName = cleanPathSegment(input.module, 'module');
710
+ const browserType = cleanPathSegment(input.browserType, 'browserType');
711
+ const query = stringValue(input.query);
712
+ if (!query)
713
+ throw ebuilderValidationError('query_required', 'query 必填');
714
+ const payload = {
715
+ browserMultiple: input.multiple === true,
716
+ keyword: query,
717
+ pageNo: 1,
718
+ pageSize: input.limit ? Math.min(Number(input.limit), 50) : 20,
719
+ ...(input.extra && typeof input.extra === 'object' && !Array.isArray(input.extra) ? input.extra : {}),
720
+ };
721
+ const result = await requestJson({
722
+ operation,
723
+ path: `/api/${moduleName}/common/browser/complete/${browserType}`,
724
+ body: payload,
725
+ headers: { langtype: 'zh_CN' },
726
+ });
727
+ const matches = normalizeBrowserMatches(result, Number(input.limit || 20));
728
+ const exact = matches.filter((item) => stringValue(item.name).toLowerCase() === query.toLowerCase());
729
+ const selected = exact.length > 0 ? exact : matches;
730
+ return {
731
+ query,
732
+ browser: { module: moduleName, type: browserType },
733
+ matchCount: selected.length,
734
+ unique: selected.length === 1,
735
+ ambiguous: selected.length > 1,
736
+ matches: selected,
737
+ };
738
+ }
739
+ async function openapiRead(endpoint, input, operation) {
740
+ const context = await resolveContext(input);
741
+ const datajson = openapiReadDatajson(endpoint, context.objId, input);
742
+ const payload = await requestJson({
743
+ operation,
744
+ path: routeEbuilderPath(context.apiPrefix, OPENAPI_READ_PATHS[endpoint]),
745
+ body: { datajson },
746
+ });
747
+ return { context, result: parseOpenapiPayload(payload, endpoint) };
748
+ }
749
+ async function openapiWrite(endpoint, datajson, operation) {
750
+ const context = await resolveContext(datajson.__context && typeof datajson.__context === 'object' ? { context: datajson.__context } : datajson);
751
+ const payload = stripInternalKeys(datajson);
752
+ const result = await requestJson({
753
+ operation,
754
+ path: routeEbuilderPath(context.apiPrefix, OPENAPI_WRITE_PATHS[endpoint]),
755
+ body: { datajson: payload },
756
+ });
757
+ return { context, result: parseOpenapiPayload(result, endpoint) };
758
+ }
759
+ async function customRead(endpoint, input, operation) {
760
+ const context = await resolveContext(input);
761
+ const interfacePk = stringValue(input.interfacePk);
762
+ if (!interfacePk)
763
+ throw ebuilderValidationError('interface_pk_required', 'interfacePk 必填');
764
+ const datajson = customReadDatajson(endpoint, input, (await getSession()).userId);
765
+ const payload = await requestJson({
766
+ operation,
767
+ path: `${routeEbuilderPath(context.apiPrefix, CUSTOM_READ_PATHS[endpoint])}/${encodeURIComponent(interfacePk)}`,
768
+ body: { datajson },
769
+ });
770
+ return { context, result: parseCustomPayload(payload, endpoint) };
771
+ }
772
+ async function customWrite(endpoint, interfacePk, datajson, operation) {
773
+ const context = await resolveContext(datajson.__context && typeof datajson.__context === 'object' ? { context: datajson.__context } : datajson);
774
+ const payload = stripInternalKeys(datajson);
775
+ const actualInterfacePk = stringValue(interfacePk || datajson.interfacePk);
776
+ if (!actualInterfacePk)
777
+ throw ebuilderValidationError('interface_pk_required', 'interfacePk 必填');
778
+ const result = await requestJson({
779
+ operation,
780
+ path: `${routeEbuilderPath(context.apiPrefix, CUSTOM_WRITE_PATHS[endpoint])}/${encodeURIComponent(actualInterfacePk)}`,
781
+ body: { datajson: payload },
782
+ });
783
+ return { context, result: parseCustomPayload(result, endpoint) };
784
+ }
785
+ function buildDataUrl(context, dataId, deleted = false) {
786
+ const base = `${(sessionContext?.baseUrl || context.origin).replace(/\/+$/u, '')}/sp/ebdfpage/card/0/${context.objId}/${dataId}`;
787
+ return deleted ? `${base}?fromRecycle=1` : base;
788
+ }
789
+ function buildListUrl(context) {
790
+ const origin = (sessionContext?.baseUrl || context.origin).replace(/\/+$/u, '');
791
+ if (context.entryUrl) {
792
+ try {
793
+ return parseDirectListUrl(context.entryUrl, origin).entryUrl;
794
+ }
795
+ catch {
796
+ return context.entryUrl;
797
+ }
798
+ }
799
+ if (context.menuUrl) {
800
+ try {
801
+ const parsed = new URL(context.menuUrl, origin);
802
+ if (parsed.origin === origin)
803
+ return parsed.href;
804
+ }
805
+ catch {
806
+ if (context.menuUrl.startsWith('/') && !context.menuUrl.startsWith('//'))
807
+ return origin + context.menuUrl;
808
+ }
809
+ }
810
+ if (!context.listId || !context.listKind)
811
+ return null;
812
+ return context.listKind === 'NList'
813
+ ? `${origin}/sp/ebdfpage/viewport/${context.listId}`
814
+ : `${origin}/sp/ebdfpage/list/${context.listId}`;
815
+ }
816
+ return {
817
+ getSession,
818
+ requestJson,
819
+ resolveDeployment,
820
+ searchMenus,
821
+ resolveContext,
822
+ formViews,
823
+ listConfig,
824
+ nlistConfig,
825
+ listQuery,
826
+ nlistQuery,
827
+ fields,
828
+ options,
829
+ mapping,
830
+ browserResolve,
831
+ openapiRead,
832
+ openapiWrite,
833
+ customRead,
834
+ customWrite,
835
+ buildDataUrl,
836
+ buildListUrl,
837
+ };
838
+ }
839
+ function originOnly(raw) {
840
+ const url = new URL(raw);
841
+ if (!['http:', 'https:'].includes(url.protocol) || !url.host || url.username || url.password) {
842
+ throw ebuilderValidationError('origin_invalid', 'E10 origin 必须是完整 http/https 地址,且不能包含用户信息');
843
+ }
844
+ return `${url.protocol}//${url.host}`;
845
+ }
846
+ function originFromUrl(raw) {
847
+ if (!/^https?:/iu.test(raw))
848
+ return '';
849
+ return originOnly(raw);
850
+ }
851
+ function parseUrl(raw, base) {
852
+ try {
853
+ return /^https?:/iu.test(raw) ? new URL(raw) : new URL(raw, base);
854
+ }
855
+ catch {
856
+ throw ebuilderValidationError('url_invalid', '页面 URL 格式不合法');
857
+ }
858
+ }
859
+ function lowerSearchParams(url) {
860
+ const map = new Map();
861
+ for (const [key, value] of url.searchParams.entries()) {
862
+ const lower = key.toLowerCase();
863
+ const list = map.get(lower) || [];
864
+ list.push(value);
865
+ map.set(lower, list);
866
+ }
867
+ return map;
868
+ }
869
+ function assertApiPath(path) {
870
+ if (!path.startsWith('/api/') || path.startsWith('//') || path.includes('/../') || /^https?:/iu.test(path)) {
871
+ throw ebuilderValidationError('path_invalid', 'E10 API 只能调用 /api/ 同源相对路径');
872
+ }
873
+ if (path.startsWith('/api/bs/')) {
874
+ throw ebuilderValidationError('path_forbidden', 'eBuilder 表单模块禁止调用 /api/bs/ 接口');
875
+ }
876
+ }
877
+ function assertDecimalId(value, field) {
878
+ const text = stringValue(value);
879
+ if (!/^\d+$/u.test(text))
880
+ throw ebuilderValidationError('field_invalid', `${field} 必须是数字 ID 字符串`);
881
+ return text;
882
+ }
883
+ function optionalDecimalId(value, field) {
884
+ const text = stringValue(value);
885
+ if (!text)
886
+ return undefined;
887
+ return assertDecimalId(text, field);
888
+ }
889
+ function stringValue(value) {
890
+ if (value === undefined || value === null)
891
+ return '';
892
+ if (typeof value === 'object')
893
+ return '';
894
+ return String(value).trim();
895
+ }
896
+ function normalizeListKind(value) {
897
+ const text = stringValue(value).toLowerCase();
898
+ if (!text)
899
+ return '';
900
+ if (text === 'list')
901
+ return 'List';
902
+ if (text === 'nlist')
903
+ return 'NList';
904
+ throw ebuilderValidationError('list_kind_invalid', 'listKind 只能是 List 或 NList');
905
+ }
906
+ function record(value) {
907
+ return value && typeof value === 'object' && !Array.isArray(value) ? value : {};
908
+ }
909
+ function nonEmptyRecord(value) {
910
+ const next = record(value);
911
+ return Object.keys(next).length > 0 ? next : undefined;
912
+ }
913
+ function responseMessage(payload) {
914
+ const root = record(payload);
915
+ const message = record(root.message);
916
+ const datajson = record(record(root.data).datajson);
917
+ for (const value of [root.msg, root.message, message.msg, message.message, datajson.message, datajson.msg]) {
918
+ if (typeof value === 'string' && value.trim())
919
+ return value.trim().slice(0, 500);
920
+ }
921
+ return '';
922
+ }
923
+ function isSuccessCode(value) {
924
+ if (value === undefined || value === null)
925
+ return true;
926
+ if (value === true || value === 0 || value === 200)
927
+ return true;
928
+ return SUCCESS_CODES.has(String(value).trim().toLowerCase());
929
+ }
930
+ function unwrapWeaData(payload, label) {
931
+ const root = record(payload);
932
+ const message = record(root.message);
933
+ const code = root.code ?? message.code;
934
+ if (!isSuccessCode(code)) {
935
+ throw ebuilderBusinessError('business_error', `${label} 业务失败:${responseMessage(payload) || String(code)}`, { code: String(code), details: { result: payload } });
936
+ }
937
+ if (root.status === false || root.fail === true) {
938
+ throw ebuilderBusinessError('business_error', `${label} 业务失败:${responseMessage(payload) || 'status=false'}`, { details: { result: payload } });
939
+ }
940
+ if (!('data' in root))
941
+ throw ebuilderBusinessError('response_invalid', `${label} 响应缺少 data`, { details: { result: payload } });
942
+ return root.data;
943
+ }
944
+ function dataOrRaw(payload) {
945
+ try {
946
+ return unwrapWeaData(payload, '配置');
947
+ }
948
+ catch {
949
+ return payload;
950
+ }
951
+ }
952
+ function selectMenu(menus, keywords, menuId) {
953
+ if (menuId) {
954
+ const matches = menus.filter((menu) => stringValue(menu.menuId) === menuId);
955
+ if (matches.length !== 1)
956
+ throw ebuilderValidationError('menu_not_unique', `menuId=${menuId} 未唯一命中 eBuilder 菜单`, { candidates: menus.slice(0, 20).map(menuSummary) });
957
+ return matches[0];
958
+ }
959
+ const scored = menus
960
+ .map((menu) => ({ menu, score: menuScore(menu, keywords) }))
961
+ .filter((item) => item.score > 0)
962
+ .sort((a, b) => b.score - a.score);
963
+ if (scored.length === 0)
964
+ throw ebuilderValidationError('menu_not_found', '没有找到合适的表格或数据列表菜单', { candidates: menus.slice(0, 20).map(menuSummary) });
965
+ const best = scored[0].score;
966
+ const winners = scored.filter((item) => item.score === best);
967
+ if (winners.length !== 1) {
968
+ throw ebuilderValidationError('menu_ambiguous', '菜单搜索结果不唯一,请传 menuId 消歧', { candidates: winners.slice(0, 20).map((item) => menuSummary(item.menu)) });
969
+ }
970
+ return winners[0].menu;
971
+ }
972
+ function menuScore(menu, keywords) {
973
+ const labels = [menu.menuName, menu.refferName, menu.menuUrl, menu.pageType]
974
+ .map((item) => stringValue(item).toLowerCase())
975
+ .filter(Boolean);
976
+ return keywords.reduce((score, keyword) => {
977
+ const needle = keyword.toLowerCase();
978
+ if (!needle)
979
+ return score;
980
+ if (labels.some((label) => label === needle))
981
+ return score + 4;
982
+ if (labels.some((label) => label.includes(needle)))
983
+ return score + 2;
984
+ return score;
985
+ }, 0);
986
+ }
987
+ function menuSummary(menu) {
988
+ return {
989
+ menuId: stringValue(menu.menuId),
990
+ menuName: stringValue(menu.menuName),
991
+ refferName: stringValue(menu.refferName),
992
+ pageType: stringValue(menu.pageType),
993
+ pageId: stringValue(menu.pageId),
994
+ appId: stringValue(menu.appId),
995
+ };
996
+ }
997
+ function unwrapPage(payload) {
998
+ let current = unwrapWeaData(payload, '页面配置');
999
+ for (let index = 0; index < 4; index += 1) {
1000
+ const rec = record(current);
1001
+ if (rec.page && typeof rec.page === 'object')
1002
+ return record(rec.page);
1003
+ if (!rec.data || typeof rec.data !== 'object')
1004
+ break;
1005
+ current = rec.data;
1006
+ }
1007
+ throw ebuilderBusinessError('page_response_invalid', 'viewPageInfo 响应缺少 data.page');
1008
+ }
1009
+ function selectPrimaryNlistComponent(page) {
1010
+ const comps = Array.isArray(page.comps) ? page.comps : [];
1011
+ const component = record(comps[1]);
1012
+ if (!Object.keys(component).length)
1013
+ throw ebuilderBusinessError('nlist_component_missing', 'data.page.comps[1] 缺失');
1014
+ let config = component.config;
1015
+ if (typeof config === 'string') {
1016
+ try {
1017
+ config = JSON.parse(config);
1018
+ }
1019
+ catch {
1020
+ throw ebuilderBusinessError('nlist_config_invalid', 'data.page.comps[1].config 不是合法 JSON');
1021
+ }
1022
+ }
1023
+ const recordConfig = record(config);
1024
+ if (!Object.keys(recordConfig).length)
1025
+ throw ebuilderBusinessError('nlist_config_missing', 'data.page.comps[1].config 缺失');
1026
+ return { component, config: recordConfig };
1027
+ }
1028
+ function positiveInt(value, field, fallback, max) {
1029
+ const raw = value === undefined || value === null || value === '' ? fallback : value;
1030
+ const numeric = Number(raw);
1031
+ if (!Number.isInteger(numeric) || numeric < 1)
1032
+ throw ebuilderValidationError('field_invalid', `${field} 必须是正整数`);
1033
+ if (max !== undefined && numeric > max)
1034
+ return max;
1035
+ return numeric;
1036
+ }
1037
+ function pageSizeInt(value, fallback, max) {
1038
+ const numeric = positiveInt(value, 'pageSize', fallback || 10);
1039
+ if (numeric > max)
1040
+ throw ebuilderValidationError('page_size_too_large', `pageSize 最大为 ${max}`);
1041
+ return numeric;
1042
+ }
1043
+ function extractListTotal(payload) {
1044
+ const data = record(unwrapWeaData(payload, 'List 总数'));
1045
+ const value = data.total ?? record(data.data).total;
1046
+ const total = Number(value);
1047
+ if (!Number.isInteger(total) || total < 0)
1048
+ throw ebuilderBusinessError('count_invalid', 'getListDataCount.data.total 不是整数');
1049
+ return total;
1050
+ }
1051
+ function listPageResult(pageNo, payload) {
1052
+ const data = record(unwrapWeaData(payload, 'List 分页'));
1053
+ const displayData = data.displayData;
1054
+ if (Array.isArray(displayData))
1055
+ return { pageNo, rows: displayData, columns: Array.isArray(data.columns) ? data.columns : [] };
1056
+ if (displayData && typeof displayData === 'object')
1057
+ return { pageNo, rows: [], columns: Array.isArray(data.columns) ? data.columns : [], groupedDisplayData: displayData };
1058
+ return { pageNo, rows: [], columns: Array.isArray(data.columns) ? data.columns : [] };
1059
+ }
1060
+ function extractNlistTotal(payload) {
1061
+ const data = record(unwrapWeaData(payload, 'NList 总数'));
1062
+ const total = Number(data.total);
1063
+ if (!Number.isInteger(total) || total < 0)
1064
+ throw ebuilderBusinessError('count_invalid', 'getCount.data.total 不是整数');
1065
+ return total;
1066
+ }
1067
+ function nlistPageResult(pageNo, payload) {
1068
+ const data = record(unwrapWeaData(payload, 'NList 分页'));
1069
+ if (!Array.isArray(data.list))
1070
+ throw ebuilderBusinessError('rows_invalid', 'getData.data.list 不是数组');
1071
+ return { pageNo, rows: data.list };
1072
+ }
1073
+ function stableRowId(row) {
1074
+ const rec = record(row);
1075
+ return stringValue(rec.id || rec.form_data_id || rec.ebrecord_rowkey || rec.rowKey);
1076
+ }
1077
+ function baseNlistFilter(compId) {
1078
+ return {
1079
+ conditionComId: `${compId}_filter_EXCLUDECACHE`,
1080
+ conditionType: 'and',
1081
+ dataType: 'classification',
1082
+ id: randomUUID().replace(/-/gu, ''),
1083
+ useConfig: true,
1084
+ };
1085
+ }
1086
+ function stringifyForm(value) {
1087
+ return Object.fromEntries(Object.entries(value).map(([key, item]) => [key, item === undefined || item === null ? '' : String(item)]));
1088
+ }
1089
+ function normalizeBrowserMatches(payload, limit) {
1090
+ const root = record(payload);
1091
+ const candidates = [
1092
+ root.data,
1093
+ record(root.data).data,
1094
+ record(root.data).list,
1095
+ record(root.result).data,
1096
+ root.result,
1097
+ ];
1098
+ const rows = candidates.find(Array.isArray);
1099
+ if (!rows)
1100
+ return [];
1101
+ return rows
1102
+ .filter((item) => Boolean(item && typeof item === 'object' && !Array.isArray(item)))
1103
+ .slice(0, limit)
1104
+ .map((item) => {
1105
+ const id = stringValue(item.id || item.optionId || item.key || item.value);
1106
+ const name = stringValue(item.name || item.content || item.title || item.label);
1107
+ return {
1108
+ id,
1109
+ name,
1110
+ dataOption: { optionId: id, content: name },
1111
+ details: {
1112
+ no: stringValue(item.no || item.code || item.workCode),
1113
+ department: stringValue(item.department || item.departmentName || item.orgName),
1114
+ path: stringValue(item.path || item.fullName || item.fullPath),
1115
+ },
1116
+ };
1117
+ })
1118
+ .filter((item) => item.id || item.name);
1119
+ }
1120
+ function openapiReadDatajson(endpoint, objId, input) {
1121
+ if (endpoint === 'detail') {
1122
+ return {
1123
+ header: { objId },
1124
+ mainTable: { id: assertDecimalId(input.dataId || record(input.mainTable).id, 'dataId') },
1125
+ operationinfo: {
1126
+ isReturnDetail: input.isReturnDetail ?? 'y',
1127
+ fieldNoFindIgnore: input.fieldNoFindIgnore ?? 'false',
1128
+ },
1129
+ };
1130
+ }
1131
+ if (endpoint === 'query') {
1132
+ return {
1133
+ header: { objId },
1134
+ mainTable: record(input.mainTable),
1135
+ pageInfo: {
1136
+ pageNo: positiveInt(input.pageNo, 'pageNo', 1, 1_000),
1137
+ pageSize: pageSizeInt(input.pageSize, 10, 1_000),
1138
+ },
1139
+ operationinfo: {
1140
+ isReturnDetail: input.isReturnDetail ?? 'n',
1141
+ fieldNoFindIgnore: input.fieldNoFindIgnore ?? 'false',
1142
+ },
1143
+ };
1144
+ }
1145
+ return {
1146
+ header: { objId },
1147
+ mainTable: record(input.mainTable),
1148
+ operationinfo: { fieldNoFindIgnore: input.fieldNoFindIgnore ?? 'false' },
1149
+ };
1150
+ }
1151
+ function customReadDatajson(endpoint, input, operator) {
1152
+ if (endpoint === 'detail') {
1153
+ return {
1154
+ mainTable: { id: assertDecimalId(input.dataId || record(input.mainTable).id, 'dataId') },
1155
+ header: { operator },
1156
+ };
1157
+ }
1158
+ if (endpoint === 'query') {
1159
+ return {
1160
+ mainTable: record(input.mainTable),
1161
+ pageInfo: {
1162
+ pageNo: String(positiveInt(input.pageNo, 'pageNo', 1)),
1163
+ pageSize: String(pageSizeInt(input.pageSize, 10, 1_000)),
1164
+ },
1165
+ header: { operator },
1166
+ };
1167
+ }
1168
+ return { mainTable: record(input.mainTable), header: { operator } };
1169
+ }
1170
+ function parseOpenapiPayload(payload, label) {
1171
+ const root = record(payload);
1172
+ const message = record(root.message);
1173
+ if (!isSuccessCode(message.code)) {
1174
+ throw ebuilderBusinessError('business_error', responseMessage(payload) || `OpenAPI ${label} 失败`, { code: stringValue(message.code), details: { result: payload } });
1175
+ }
1176
+ const datajson = record(record(root.data).datajson);
1177
+ if (!Object.keys(datajson).length)
1178
+ throw ebuilderBusinessError('response_invalid', `OpenAPI ${label} 响应缺少 data.datajson`, { details: { result: payload } });
1179
+ if (datajson.status === false) {
1180
+ throw ebuilderBusinessError('business_error', responseMessage(payload) || `OpenAPI ${label} status=false`, { details: { result: payload } });
1181
+ }
1182
+ return {
1183
+ status: datajson.status !== false,
1184
+ message: responseMessage(payload) || 'success',
1185
+ dataIds: normalizeDataIds(datajson.dataIds),
1186
+ datas: datajson.datas,
1187
+ raw: payload,
1188
+ };
1189
+ }
1190
+ function parseCustomPayload(payload, label) {
1191
+ const root = record(payload);
1192
+ const datajson = record(root.datajson || record(root.data).datajson);
1193
+ if (!Object.keys(datajson).length && !('datajson' in root)) {
1194
+ throw ebuilderBusinessError('response_invalid', `自定义接口 ${label} 响应缺少 datajson`, { details: { result: payload } });
1195
+ }
1196
+ return {
1197
+ status: true,
1198
+ message: responseMessage(payload) || 'success',
1199
+ dataIds: normalizeDataIds(datajson.dataIds),
1200
+ datas: datajson.datas,
1201
+ raw: payload,
1202
+ };
1203
+ }
1204
+ function normalizeDataIds(value) {
1205
+ if (value === undefined || value === null)
1206
+ return [];
1207
+ if (Array.isArray(value))
1208
+ return value.map((item) => stringValue(item)).filter(Boolean);
1209
+ return String(value).split(',').map((item) => item.trim()).filter(Boolean);
1210
+ }
1211
+ function stripInternalKeys(value) {
1212
+ const next = { ...value };
1213
+ delete next.__context;
1214
+ delete next.context;
1215
+ delete next.apiPrefix;
1216
+ delete next.appId;
1217
+ delete next.objId;
1218
+ delete next.listId;
1219
+ delete next.hostPageId;
1220
+ delete next.listKind;
1221
+ delete next.menuId;
1222
+ delete next.menuName;
1223
+ delete next.menuUrl;
1224
+ delete next.entryUrl;
1225
+ delete next.url;
1226
+ delete next.menu;
1227
+ delete next.menuKeywords;
1228
+ delete next.interfacePk;
1229
+ delete next.dataId;
1230
+ return next;
1231
+ }
1232
+ function cleanPathSegment(value, field) {
1233
+ const text = stringValue(value).replace(/^\/+|\/+$/gu, '');
1234
+ if (!/^[A-Za-z0-9_/-]+$/u.test(text) || text.includes('..') || text === '') {
1235
+ throw ebuilderValidationError('field_invalid', `${field} 不是合法接口路径片段`);
1236
+ }
1237
+ return text;
1238
+ }