openyida 2026.7.18 → 2026.7.21

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 (200) hide show
  1. package/README.md +37 -2
  2. package/bin/yida.js +113 -18
  3. package/docs/capabilities.md +202 -0
  4. package/lib/agent-center/agent-center.js +2 -3
  5. package/lib/app/app-list.js +3 -4
  6. package/lib/app/build-page.js +13 -6
  7. package/lib/app/check-page.js +16 -6
  8. package/lib/app/compile.js +17 -7
  9. package/lib/app/create-app.js +14 -2
  10. package/lib/app/create-form/api-path.js +25 -0
  11. package/lib/app/create-form/args.js +13 -0
  12. package/lib/app/create-form/commands.js +4 -0
  13. package/lib/app/create-form/definition-reader.js +23 -23
  14. package/lib/app/create-form/field-normalizers.js +26 -7
  15. package/lib/app/create-form/schema-patch.js +4 -1
  16. package/lib/app/create-form.js +155 -68
  17. package/lib/app/create-page.js +47 -10
  18. package/lib/app/generate-page.js +20 -1
  19. package/lib/app/get-form-config.js +2 -1
  20. package/lib/app/get-schema.js +67 -15
  21. package/lib/app/import-app.js +94 -42
  22. package/lib/app/list-forms.js +4 -8
  23. package/lib/app/page-ir.js +140 -0
  24. package/lib/app/publish.js +289 -556
  25. package/lib/app/schema-field-resolution.js +370 -0
  26. package/lib/app/services/app-reader.js +85 -0
  27. package/lib/app/services/app-service.js +235 -0
  28. package/lib/app/services/canvas-page-compiler.js +91 -0
  29. package/lib/app/services/canvas-page-schema-builder.js +149 -0
  30. package/lib/app/services/field-bindings.js +68 -0
  31. package/lib/app/services/form-compiler.js +1922 -0
  32. package/lib/app/services/form-mode-service.js +182 -0
  33. package/lib/app/services/form-schema-patcher.js +333 -0
  34. package/lib/app/services/form-schema-reader.js +59 -0
  35. package/lib/app/services/form-service.js +379 -0
  36. package/lib/app/services/form-validation.js +562 -0
  37. package/lib/app/services/native-page-compiler.js +92 -0
  38. package/lib/app/services/native-page-schema-builder.js +428 -0
  39. package/lib/app/services/page-resource-service.js +185 -0
  40. package/lib/app/update-app.js +22 -9
  41. package/lib/app/update-form-config.js +4 -2
  42. package/lib/asset/asset-cmd.js +19 -6
  43. package/lib/auth/oauth-loopback.js +376 -15
  44. package/lib/auth/token-auth.js +34 -3
  45. package/lib/auth/token-store.js +22 -4
  46. package/lib/basic-info/basic-info.js +2 -1
  47. package/lib/bridge/bridge.js +43 -18
  48. package/lib/core/agent-capabilities.js +5 -3
  49. package/lib/core/command-errors.js +38 -0
  50. package/lib/core/command-manifest.js +59 -1
  51. package/lib/core/copy.js +12 -0
  52. package/lib/core/doctor.js +15 -4
  53. package/lib/core/env-cmd.js +1 -1
  54. package/lib/core/env.js +7 -6
  55. package/lib/core/i18n.js +66 -8
  56. package/lib/core/legacy-schema-guard.js +408 -0
  57. package/lib/core/locales/en.js +38 -46
  58. package/lib/core/locales/zh.js +30 -46
  59. package/lib/core/sample.js +12 -2
  60. package/lib/core/utils.js +334 -66
  61. package/lib/core/yida-client.js +108 -5
  62. package/lib/corp-efficiency/corp-efficiency.js +2 -2
  63. package/lib/corp-manager/corp-manager.js +3 -3
  64. package/lib/formula/evaluate.js +11 -68
  65. package/lib/formula/field-refs.js +83 -0
  66. package/lib/i18n-management/i18n-management.js +2 -3
  67. package/lib/process/configure-process.js +44 -1992
  68. package/lib/process/create-process.js +54 -87
  69. package/lib/process/preview-process.js +20 -8
  70. package/lib/process/services/process-compiler.js +2056 -0
  71. package/lib/process/services/process-reader.js +232 -0
  72. package/lib/process/services/process-resource-service.js +571 -0
  73. package/lib/process/services/process-service.js +122 -0
  74. package/lib/process/services/process-stage-checkpoint.js +246 -0
  75. package/lib/report/append.js +7 -26
  76. package/lib/report/http.js +31 -5
  77. package/lib/report/index.js +10 -2
  78. package/lib/samples/yida-canvas-custom-page/business-list.canvas.jsx +63 -14
  79. package/lib/samples/yida-canvas-custom-page/dashboard-overview.canvas.jsx +176 -32
  80. package/lib/samples/yida-canvas-custom-page/dashboard-starter.canvas.jsx +5 -2
  81. package/lib/samples/yida-canvas-custom-page/data-management.canvas.jsx +228 -14
  82. package/lib/samples/yida-canvas-custom-page/portal-shell-home.canvas.jsx +2 -2
  83. package/lib/samples/yida-canvas-custom-page/split-pane-detail.canvas.jsx +206 -10
  84. package/lib/samples/yida-custom-page/data-management.oyd.jsx +14 -3
  85. package/lib/samples/yida-custom-page/design-tokens.js +2 -2
  86. package/lib/samples/yida-custom-page/detail-profile.oyd.jsx +1 -1
  87. package/lib/samples/yida-custom-page/portal-shell-home.oyd.jsx +7 -2
  88. package/lib/samples/yida-custom-page/split-pane-detail.oyd.jsx +2 -1
  89. package/lib/schema/adapters/app-adapter.js +210 -0
  90. package/lib/schema/adapters/form-adapter.js +1362 -0
  91. package/lib/schema/adapters/keys.js +19 -0
  92. package/lib/schema/adapters/page-adapter.js +566 -0
  93. package/lib/schema/adapters/process-adapter.js +519 -0
  94. package/lib/schema/applier.js +1878 -0
  95. package/lib/schema/apply-store.js +1083 -0
  96. package/lib/schema/command.js +645 -0
  97. package/lib/schema/dependency-graph.js +83 -0
  98. package/lib/schema/errors.js +481 -0
  99. package/lib/schema/hash.js +9 -0
  100. package/lib/schema/manifest-limits.js +177 -0
  101. package/lib/schema/manifest-loader.js +309 -0
  102. package/lib/schema/manifest-schema-v1.json +193 -0
  103. package/lib/schema/normalize-manifest.js +281 -0
  104. package/lib/schema/page-canvas-foundation.js +447 -0
  105. package/lib/schema/page-data-source-builder.js +561 -0
  106. package/lib/schema/page-foundation.js +556 -0
  107. package/lib/schema/page-source-loader.js +314 -0
  108. package/lib/schema/planner.js +691 -0
  109. package/lib/schema/remote-dispatch-boundary.js +39 -0
  110. package/lib/schema/remote-missing.js +21 -0
  111. package/lib/schema/remote-reader.js +189 -0
  112. package/lib/schema/resource-registry.js +157 -0
  113. package/lib/schema/server-revision.js +71 -0
  114. package/lib/schema/sort.js +28 -0
  115. package/lib/schema/state-store.js +589 -0
  116. package/package.json +10 -8
  117. package/scripts/check-release-risks.js +254 -0
  118. package/scripts/generate-command-docs.js +6 -1
  119. package/scripts/i18n-baseline.json +11 -11
  120. package/scripts/postinstall.js +8 -12
  121. package/scripts/validate-ci.sh +15 -11
  122. package/scripts/validate-i18n.js +30 -9
  123. package/scripts/validate-package-size.js +5 -2
  124. package/scripts/validate-skills.js +4 -0
  125. package/scripts/validate-structure.js +15 -0
  126. package/yida-skills/SKILL.md +117 -30
  127. package/yida-skills/references/schema-as-code-phase1.md +99 -0
  128. package/yida-skills/references/setup-and-env.md +59 -59
  129. package/yida-skills/references/task-retrospective.md +6 -6
  130. package/yida-skills/skills/yida-app/SKILL.md +78 -17
  131. package/yida-skills/skills/yida-canvas-custom-page/SKILL.md +18 -7
  132. package/yida-skills/skills/yida-canvas-custom-page/references/data-bridge-guide.md +3 -3
  133. package/yida-skills/skills/yida-canvas-custom-page/references/page-generation-guide.md +15 -0
  134. package/yida-skills/skills/yida-create-app/SKILL.md +21 -3
  135. package/yida-skills/skills/yida-create-form-page/SKILL.md +19 -1
  136. package/yida-skills/skills/yida-create-page/SKILL.md +16 -3
  137. package/yida-skills/skills/yida-create-process/SKILL.md +100 -4
  138. package/yida-skills/skills/yida-custom-page/SKILL.md +19 -4
  139. package/yida-skills/skills/yida-dashboard/SKILL.md +1 -1
  140. package/yida-skills/skills/yida-dashboard/references/pitfalls.md +1 -1
  141. package/yida-skills/skills/yida-get-schema/SKILL.md +49 -5
  142. package/yida-skills/skills/yida-i18n/SKILL.md +1 -1
  143. package/yida-skills/skills/yida-integration/SKILL.md +7 -1
  144. package/yida-skills/skills/yida-login/SKILL.md +50 -99
  145. package/yida-skills/skills/yida-page-config/SKILL.md +7 -1
  146. package/yida-skills/skills/yida-page-uiux/references/app/blueprint.md +0 -1
  147. package/yida-skills/skills/yida-page-uiux/references/app/navigation-patterns.md +0 -1
  148. package/yida-skills/skills/yida-page-uiux/references/app/role-journey.md +0 -1
  149. package/yida-skills/skills/yida-page-uiux/references/dashboard/app-blueprint.md +0 -1
  150. package/yida-skills/skills/yida-page-uiux/references/dashboard/chart-contracts.md +0 -1
  151. package/yida-skills/skills/yida-page-uiux/references/dashboard/component-contracts.md +0 -1
  152. package/yida-skills/skills/yida-page-uiux/references/dashboard/layout-patterns.md +0 -1
  153. package/yida-skills/skills/yida-page-uiux/references/dashboard/theme-recipes.md +0 -1
  154. package/yida-skills/skills/yida-page-uiux/references/detail/object-narrative.md +0 -1
  155. package/yida-skills/skills/yida-page-uiux/references/detail/section-patterns.md +0 -1
  156. package/yida-skills/skills/yida-page-uiux/references/detail/timeline-and-related.md +0 -1
  157. package/yida-skills/skills/yida-page-uiux/references/landing/assets-workflow.md +0 -1
  158. package/yida-skills/skills/yida-page-uiux/references/landing/industry-playbooks.md +0 -1
  159. package/yida-skills/skills/yida-page-uiux/references/landing/research-levels.md +0 -1
  160. package/yida-skills/skills/yida-page-uiux/references/landing/section-patterns.md +0 -1
  161. package/yida-skills/skills/yida-page-uiux/references/list/drawer-detail.md +0 -1
  162. package/yida-skills/skills/yida-page-uiux/references/list/empty-loading-error.md +0 -1
  163. package/yida-skills/skills/yida-page-uiux/references/list/filter-patterns.md +0 -1
  164. package/yida-skills/skills/yida-page-uiux/references/list/table-patterns.md +0 -1
  165. package/yida-skills/skills/yida-page-uiux/references/scenes/screen.md +0 -1
  166. package/yida-skills/skills/yida-page-uiux/references/workbench/entry-patterns.md +0 -1
  167. package/yida-skills/skills/yida-page-uiux/references/workbench/portal-layouts.md +0 -1
  168. package/yida-skills/skills/yida-page-uiux/references/workbench/task-feed.md +0 -1
  169. package/yida-skills/skills/yida-page-uiux/workflow/output-decision-block.md +1 -1
  170. package/yida-skills/skills/yida-process-rule/SKILL.md +35 -1
  171. package/yida-skills/skills/yida-publish-page/SKILL.md +46 -4
  172. package/yida-skills/skills/yida-report/SKILL.md +9 -235
  173. package/yida-skills/skills/yida-report/references/schema-builder-details.md +239 -0
  174. package/yida-skills/skills/yida-skill-evaluator/SKILL.md +170 -0
  175. package/yida-skills/skills-index.json +238 -54
  176. package/lib/core/locales/ar.js +0 -834
  177. package/lib/core/locales/de.js +0 -834
  178. package/lib/core/locales/es.js +0 -834
  179. package/lib/core/locales/fr.js +0 -834
  180. package/lib/core/locales/hi.js +0 -834
  181. package/lib/core/locales/ja.js +0 -1227
  182. package/lib/core/locales/ko.js +0 -836
  183. package/lib/core/locales/pt.js +0 -834
  184. package/lib/core/locales/vi.js +0 -834
  185. package/lib/core/locales/zh-HK.js +0 -1263
  186. package/project/pages/src/demo-birthday-game.oyd.jsx +0 -832
  187. package/project/pages/src/demo-chip-insight.oyd.jsx +0 -983
  188. package/project/pages/src/demo-compat-smoke.oyd.jsx +0 -58
  189. package/project/pages/src/demo-crm-batch-entry.oyd.jsx +0 -805
  190. package/project/pages/src/demo-crm-dashboard.oyd.jsx +0 -677
  191. package/project/pages/src/demo-future-vision-2026.oyd.jsx +0 -1102
  192. package/project/pages/src/demo-ppt.oyd.jsx +0 -1192
  193. package/project/pages/src/demo-salary-calculator.oyd.jsx +0 -904
  194. package/project/pages/src/openyida-knowledge-doc.oyd.jsx +0 -1714
  195. package/yida-skills/skills/large-file-write/SKILL.md +0 -93
  196. package/yida-skills/skills/large-file-write/references/write-patterns.md +0 -147
  197. package/yida-skills/skills/large-file-write/scripts/write.js +0 -157
  198. package/yida-skills/skills/sls-log-workbench/SKILL.md +0 -134
  199. package/yida-skills/skills/sls-log-workbench/references/workbench-reference.md +0 -130
  200. package/yida-skills/skills/sls-log-workbench/sls-query.js +0 -295
@@ -16,7 +16,7 @@ function buildCommonParams(auth, extra = {}) {
16
16
  _api: 'nattyFetch',
17
17
  _mock: 'false',
18
18
  _csrf_token: auth && auth.csrfToken,
19
- _locale_time_zone_offset: String(-new Date().getTimezoneOffset() * 60 * 1000),
19
+ _locale_time_zone_offset: '28800000',
20
20
  _stamp: Date.now(),
21
21
  ...extra,
22
22
  };
@@ -31,7 +31,9 @@ function unwrapYidaResponse(response, options = {}) {
31
31
  const detail = response && (response.errorMsg || response.message);
32
32
  throw new Error(detail || failMessage || `${action} failed`);
33
33
  }
34
- return response.content;
34
+ // 部分宜搭接口成功时不返回 content 字段(也可能不返回 success),
35
+ // 沿用项目中的兵底约定(content 缺失时回退为整个响应),避免静默返回 undefined。
36
+ return response.content !== undefined ? response.content : response;
35
37
  }
36
38
 
37
39
  function isUsableAuthData(authData) {
@@ -41,7 +43,9 @@ function isUsableAuthData(authData) {
41
43
  if (authData.auth_mode === 'token' || authData.authMode === 'token') {
42
44
  return true;
43
45
  }
44
- return false;
46
+ const cookies = Array.isArray(authData.cookies) ? authData.cookies : [];
47
+ const csrfToken = authData.csrf_token || authData.csrfToken || authData._csrf_token;
48
+ return cookies.length > 0 && typeof csrfToken === 'string' && csrfToken.length > 0;
45
49
  }
46
50
 
47
51
  function createAuthRef(authData) {
@@ -51,7 +55,7 @@ function createAuthRef(authData) {
51
55
  : triggerLogin();
52
56
  const safeAuthData = loadedAuthData || {};
53
57
 
54
- return {
58
+ const authRef = {
55
59
  baseUrl: resolveBaseUrl(safeAuthData),
56
60
  authData: safeAuthData,
57
61
  authMode: safeAuthData.auth_mode || safeAuthData.authMode || '',
@@ -59,6 +63,12 @@ function createAuthRef(authData) {
59
63
  corpId: safeAuthData.corp_id || '',
60
64
  userId: safeAuthData.user_id || '',
61
65
  };
66
+ if (Array.isArray(safeAuthData.cookies)) {
67
+ authRef.cookieData = safeAuthData;
68
+ authRef.cookies = safeAuthData.cookies;
69
+ authRef.csrfToken = safeAuthData.csrf_token || safeAuthData.csrfToken || safeAuthData._csrf_token || '';
70
+ }
71
+ return authRef;
62
72
  }
63
73
 
64
74
  function isTokenAuthRef(authRef) {
@@ -78,6 +88,22 @@ function isTokenAuthRef(authRef) {
78
88
  return authMode === 'token' || authSource === 'token';
79
89
  }
80
90
 
91
+ function isCookieAuthRef(authRef) {
92
+ if (!authRef) {
93
+ return false;
94
+ }
95
+ const authData = authRef.authData || authRef.cookieData || {};
96
+ const authMode = authRef.authMode || authRef.auth_mode || authData.auth_mode || authData.authMode;
97
+ const authSource = authRef.authSource || authRef.auth_source || authData.auth_source || authData.authSource;
98
+ return (
99
+ authMode === 'env' ||
100
+ authMode === 'cookie' ||
101
+ authSource === 'env' ||
102
+ authSource === 'cookie' ||
103
+ Array.isArray(authRef.cookies)
104
+ );
105
+ }
106
+
81
107
  function isAuthRefReady(authRef) {
82
108
  if (!authRef) {
83
109
  return false;
@@ -85,7 +111,14 @@ function isAuthRefReady(authRef) {
85
111
  if (isTokenAuthRef(authRef)) {
86
112
  return true;
87
113
  }
88
- return false;
114
+ return (
115
+ isCookieAuthRef(authRef) &&
116
+ typeof authRef.baseUrl === 'string' &&
117
+ authRef.baseUrl.length > 0 &&
118
+ typeof authRef.csrfToken === 'string' &&
119
+ authRef.csrfToken.length > 0 &&
120
+ Array.isArray(authRef.cookies)
121
+ );
89
122
  }
90
123
 
91
124
  class YidaClient {
@@ -115,6 +148,20 @@ class YidaClient {
115
148
  );
116
149
  }
117
150
 
151
+ async getOnce(requestPath, queryParams, options) {
152
+ const ref = requireReadAuth(this.authRef);
153
+ const resolvedRequestPath = typeof requestPath === 'function'
154
+ ? requestPath(ref)
155
+ : requestPath;
156
+ const resolvedQueryParams = typeof queryParams === 'function'
157
+ ? queryParams(ref)
158
+ : queryParams;
159
+ if (options === undefined) {
160
+ return httpGet(ref.baseUrl, resolvedRequestPath, resolvedQueryParams || null, ref.cookies);
161
+ }
162
+ return httpGet(ref.baseUrl, resolvedRequestPath, resolvedQueryParams || null, ref.cookies, options);
163
+ }
164
+
118
165
  async postForm(requestPath, bodyParams, options) {
119
166
  return requestWithAutoLogin(
120
167
  (ref) => {
@@ -136,6 +183,23 @@ class YidaClient {
136
183
  );
137
184
  }
138
185
 
186
+ async postFormOnce(requestPath, bodyParams, options) {
187
+ const ref = requireOneShotAuth(this.authRef);
188
+ const resolvedRequestPath = typeof requestPath === 'function'
189
+ ? requestPath(ref)
190
+ : requestPath;
191
+ const resolvedBodyParams = typeof bodyParams === 'function'
192
+ ? bodyParams(ref)
193
+ : bodyParams;
194
+ const postData = typeof resolvedBodyParams === 'string'
195
+ ? resolvedBodyParams
196
+ : querystring.stringify(resolvedBodyParams || {});
197
+ if (options === undefined) {
198
+ return httpPost(ref.baseUrl, resolvedRequestPath, postData, ref.cookies);
199
+ }
200
+ return httpPost(ref.baseUrl, resolvedRequestPath, postData, ref.cookies, options);
201
+ }
202
+
139
203
  async postJson(requestPath, bodyParams, options) {
140
204
  return requestWithAutoLogin(
141
205
  (ref) => {
@@ -176,6 +240,44 @@ class YidaClient {
176
240
  }
177
241
  }
178
242
 
243
+ function requireOneShotAuth(authRef) {
244
+ if (!authRef || typeof authRef.baseUrl !== 'string' || authRef.baseUrl.length === 0) {
245
+ const error = new Error('Schema write authentication is not ready.');
246
+ error.code = 'YIDA_WRITE_AUTH_NOT_READY';
247
+ throw error;
248
+ }
249
+ if (isTokenAuthRef(authRef)) {
250
+ return authRef;
251
+ }
252
+ if (
253
+ typeof authRef.csrfToken !== 'string' ||
254
+ authRef.csrfToken.length === 0 ||
255
+ !Array.isArray(authRef.cookies)
256
+ ) {
257
+ const error = new Error('Schema write authentication is not ready.');
258
+ error.code = 'YIDA_WRITE_AUTH_NOT_READY';
259
+ throw error;
260
+ }
261
+ return authRef;
262
+ }
263
+
264
+ function requireReadAuth(authRef) {
265
+ if (!authRef || typeof authRef.baseUrl !== 'string' || authRef.baseUrl.length === 0) {
266
+ const error = new Error('Yida read authentication is not ready.');
267
+ error.code = 'YIDA_READ_AUTH_NOT_READY';
268
+ throw error;
269
+ }
270
+ if (isTokenAuthRef(authRef)) {
271
+ return authRef;
272
+ }
273
+ if (!Array.isArray(authRef.cookies)) {
274
+ const error = new Error('Yida read authentication is not ready.');
275
+ error.code = 'YIDA_READ_AUTH_NOT_READY';
276
+ throw error;
277
+ }
278
+ return authRef;
279
+ }
280
+
179
281
  function createYidaClient(options = {}) {
180
282
  return new YidaClient(options);
181
283
  }
@@ -186,6 +288,7 @@ module.exports = {
186
288
  createAuthRef,
187
289
  createYidaClient,
188
290
  isAuthRefReady,
291
+ isCookieAuthRef,
189
292
  isTokenAuthRef,
190
293
  unwrapYidaResponse,
191
294
  };
@@ -16,6 +16,7 @@
16
16
  const { createAuthRef: createCoreAuthRef, createYidaClient, isAuthRefReady } = require('../core/yida-client');
17
17
  const { c, success, listItem } = require('../core/chalk');
18
18
  const { parseOpenOption, withBrowserHandoff } = require('../core/browser-handoff');
19
+ const { throwCommandError } = require('../core/command-errors');
19
20
 
20
21
  const API = {
21
22
  corpInfoManageCard: '/query/corpadmin/getCorpInfoManageCard.json',
@@ -619,8 +620,7 @@ async function run(args = []) {
619
620
  throw new Error(`未知的 corp-efficiency 子命令: ${subCommand}`);
620
621
  }
621
622
  } catch (err) {
622
- console.error(`企业效能命令失败:${err.message}`);
623
- process.exit(1);
623
+ throwCommandError(`企业效能命令失败:${err.message}`);
624
624
  }
625
625
  }
626
626
 
@@ -1,5 +1,7 @@
1
1
  'use strict';
2
2
 
3
+ const { throwUsage } = require('../core/command-errors');
4
+
3
5
  const {
4
6
  buildSubAdminConfig,
5
7
  listAdmins,
@@ -27,9 +29,7 @@ Examples:
27
29
  `;
28
30
 
29
31
  function fail(message) {
30
- console.error(message);
31
- console.error(USAGE);
32
- process.exit(1);
32
+ throwUsage(message, USAGE);
33
33
  }
34
34
 
35
35
  function parseCliOptions(tokens) {
@@ -1,7 +1,13 @@
1
1
  'use strict';
2
2
 
3
3
  const fs = require('fs');
4
+ const { throwCommandError, throwUsage } = require('../core/command-errors');
4
5
  const path = require('path');
6
+ const {
7
+ normalizeFormulaFieldRefs,
8
+ normalizeSubtableFieldId,
9
+ stripSubtableFieldPrefix,
10
+ } = require('./field-refs');
5
11
 
6
12
  const KNOWN_FUNCTIONS = new Set([
7
13
  'ABS',
@@ -48,12 +54,6 @@ const KNOWN_FUNCTIONS = new Set([
48
54
  'YEAR',
49
55
  ]);
50
56
 
51
- // Subtable child fields are stored flat in the schema (get-schema / save never
52
- // emit a "tableField_xxx.xxx" form), and Yida formulas reference them by their
53
- // plain fieldId. A "tableField_xxx." prefix inside #{...} is therefore always
54
- // an invalid reference that the web platform cannot resolve.
55
- const SUBTABLE_FIELD_PREFIX_PATTERN = /^tableField_[A-Za-z0-9]+\./;
56
-
57
57
  const CHINESE_PUNCTUATION = {
58
58
  ',': ',',
59
59
  ';': ',',
@@ -163,64 +163,6 @@ function collectSchemaFieldIds(value) {
163
163
  return fieldIds;
164
164
  }
165
165
 
166
- // Rewrite every `#{tableField_xxx.field}` reference inside a string to the
167
- // canonical `#{field}` form. Returns the corrected string plus how many refs
168
- // were fixed. Only the exact `#{tableField_<alnum>.<rest>}` shape is touched,
169
- // so unrelated text (e.g. a JS regex like `#{1,3}`) is left untouched.
170
- function stripSubtableFieldPrefix(str) {
171
- if (typeof str !== 'string' || str.indexOf('#{') === -1) {
172
- return { value: str, count: 0 };
173
- }
174
-
175
- let count = 0;
176
- const value = str.replace(/#\{([^}]*)\}/g, (whole, inner) => {
177
- const trimmed = inner.trim();
178
- if (SUBTABLE_FIELD_PREFIX_PATTERN.test(trimmed)) {
179
- count += 1;
180
- return `#{${trimmed.replace(SUBTABLE_FIELD_PREFIX_PATTERN, '')}}`;
181
- }
182
- return whole;
183
- });
184
-
185
- return { value, count };
186
- }
187
-
188
- // Recursively walk any schema node (object/array) and normalize subtable
189
- // prefixes in every string value in place. Returns the total number of refs
190
- // fixed. Covers formula, complexValue.formula, expression, linkage, and any
191
- // other string-valued property uniformly.
192
- function normalizeFormulaFieldRefs(node) {
193
- let total = 0;
194
-
195
- function visit(parent, key) {
196
- const current = parent[key];
197
- if (typeof current === 'string') {
198
- const { value, count } = stripSubtableFieldPrefix(current);
199
- if (count > 0) {
200
- parent[key] = value;
201
- total += count;
202
- }
203
- return;
204
- }
205
- if (Array.isArray(current)) {
206
- for (let i = 0; i < current.length; i++) {
207
- visit(current, i);
208
- }
209
- return;
210
- }
211
- if (current && typeof current === 'object') {
212
- for (const childKey of Object.keys(current)) {
213
- visit(current, childKey);
214
- }
215
- }
216
- }
217
-
218
- if (node && typeof node === 'object') {
219
- visit({ root: node }, 'root');
220
- }
221
- return total;
222
- }
223
-
224
166
  function scanOutsideStrings(formula, callback) {
225
167
  let quote = '';
226
168
  let escaped = false;
@@ -354,8 +296,9 @@ function collectFieldRefs(formula, diagnostics) {
354
296
  { fieldId, index }
355
297
  ));
356
298
  }
357
- if (SUBTABLE_FIELD_PREFIX_PATTERN.test(fieldId)) {
358
- const suggestion = fieldId.replace(SUBTABLE_FIELD_PREFIX_PATTERN, '');
299
+ const normalizedFieldId = normalizeSubtableFieldId(fieldId);
300
+ if (normalizedFieldId.changed) {
301
+ const suggestion = normalizedFieldId.value;
359
302
  diagnostics.push(createDiagnostic(
360
303
  'error',
361
304
  'field_ref_subtable_prefix',
@@ -646,7 +589,7 @@ async function run(args) {
646
589
  if (parsed.help || !parsed.formulaInput) {
647
590
  console.log(usage());
648
591
  if (!parsed.help) {
649
- process.exit(1);
592
+ throwUsage('Missing formula input', usage());
650
593
  }
651
594
  return;
652
595
  }
@@ -662,7 +605,7 @@ async function run(args) {
662
605
  }
663
606
 
664
607
  if (parsed.strict && !result.ok) {
665
- process.exit(1);
608
+ throwCommandError('Formula validation failed', { code: 'CLI_FORMULA_INVALID' });
666
609
  }
667
610
  }
668
611
 
@@ -0,0 +1,83 @@
1
+ 'use strict';
2
+
3
+ // Subtable child fields are stored flat in the schema (get-schema / save never
4
+ // emit a "tableField_xxx.xxx" form), and Yida formulas reference them by their
5
+ // plain fieldId. A "tableField_xxx." prefix inside #{...} is therefore always
6
+ // an invalid reference that the web platform cannot resolve.
7
+ const SUBTABLE_FIELD_PREFIX_PATTERN = /^tableField_[A-Za-z0-9]+\./;
8
+
9
+ function normalizeSubtableFieldId(fieldId) {
10
+ const value = String(fieldId || '');
11
+ if (!SUBTABLE_FIELD_PREFIX_PATTERN.test(value)) {
12
+ return { value, changed: false };
13
+ }
14
+ return {
15
+ value: value.replace(SUBTABLE_FIELD_PREFIX_PATTERN, ''),
16
+ changed: true,
17
+ };
18
+ }
19
+
20
+ // Rewrite every `#{tableField_xxx.field}` reference inside a string to the
21
+ // canonical `#{field}` form. Returns the corrected string plus how many refs
22
+ // were fixed. Only the exact `#{tableField_<alnum>.<rest>}` shape is touched,
23
+ // so unrelated text (e.g. a JS regex like `#{1,3}`) is left untouched.
24
+ function stripSubtableFieldPrefix(str) {
25
+ if (typeof str !== 'string' || str.indexOf('#{') === -1) {
26
+ return { value: str, count: 0 };
27
+ }
28
+
29
+ let count = 0;
30
+ const value = str.replace(/#\{([^}]*)\}/g, (whole, inner) => {
31
+ const trimmed = inner.trim();
32
+ const normalized = normalizeSubtableFieldId(trimmed);
33
+ if (normalized.changed) {
34
+ count += 1;
35
+ return `#{${normalized.value}}`;
36
+ }
37
+ return whole;
38
+ });
39
+
40
+ return { value, count };
41
+ }
42
+
43
+ // Recursively walk any schema node (object/array) and normalize subtable
44
+ // prefixes in every string value in place. Returns the total number of refs
45
+ // fixed. Covers formula, complexValue.formula, expression, linkage, and any
46
+ // other string-valued property uniformly.
47
+ function normalizeFormulaFieldRefs(node) {
48
+ let total = 0;
49
+
50
+ function visit(parent, key) {
51
+ const current = parent[key];
52
+ if (typeof current === 'string') {
53
+ const { value, count } = stripSubtableFieldPrefix(current);
54
+ if (count > 0) {
55
+ parent[key] = value;
56
+ total += count;
57
+ }
58
+ return;
59
+ }
60
+ if (Array.isArray(current)) {
61
+ for (let i = 0; i < current.length; i++) {
62
+ visit(current, i);
63
+ }
64
+ return;
65
+ }
66
+ if (current && typeof current === 'object') {
67
+ for (const childKey of Object.keys(current)) {
68
+ visit(current, childKey);
69
+ }
70
+ }
71
+ }
72
+
73
+ if (node && typeof node === 'object') {
74
+ visit({ root: node }, 'root');
75
+ }
76
+ return total;
77
+ }
78
+
79
+ module.exports = {
80
+ normalizeFormulaFieldRefs,
81
+ normalizeSubtableFieldId,
82
+ stripSubtableFieldPrefix,
83
+ };
@@ -2,6 +2,7 @@
2
2
 
3
3
  const { createAuthRef: createCoreAuthRef, createYidaClient, isAuthRefReady } = require('../core/yida-client');
4
4
  const { normalizeYidaLocale } = require('../core/yida-i18n');
5
+ const { throwUsage } = require('../core/command-errors');
5
6
 
6
7
  const SYSTEM_LANGUAGES = {
7
8
  zh_CN: { languageTag: 'zh_CN', languageName: '简体中文', autonym: '简体中文', support: 'full' },
@@ -43,9 +44,7 @@ Examples:
43
44
  `;
44
45
 
45
46
  function fail(message) {
46
- console.error(message);
47
- console.error(USAGE);
48
- process.exit(1);
47
+ throwUsage(message, USAGE);
49
48
  }
50
49
 
51
50
  function printJson(payload) {