openyida 2026.5.20 → 2026.5.21-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -323,6 +323,8 @@ For overseas apps, pass `--locale en_US` or `--locale ja_JP` on creation command
323
323
  | `openyida publish <sourceFile> <appType> <formUuid> [--compat] [--health-check] [--force] [--open\|--no-open]` | Compile and publish a custom display page; by default the target must be `formType=display` |
324
324
  | `openyida update-form-config <appType> <formUuid> <isRenderNav> <title> [--locale zh_CN\|en_US\|ja_JP]` | Update page/form display configuration |
325
325
 
326
+ `openyida publish` preserves existing custom page data sources by default. Before saving the new compiled JSX Schema, it reads the current page Schema and merges the Page-level `dataSource` with the built-in `urlParams` and `timestamp` sources, so manually configured data sources are not deleted during republish.
327
+
326
328
  ### Data, Permissions, and Sharing
327
329
 
328
330
  | Command | Description |
@@ -364,10 +366,12 @@ For overseas apps, pass `--locale en_US` or `--locale ja_JP` on creation command
364
366
  |---------|-------------|
365
367
  | `openyida copy [--force]` | Initialize the local `project/` workspace |
366
368
  | `openyida sample [--list]` | Emit sample templates |
369
+ | `openyida bridge start [--port 6736] [--open]` | Start the local OpenYida web bridge for `https://demo.aliwork.com/s/openyida` |
367
370
  | `openyida doctor [--fix]` | Diagnose and repair environment issues |
368
371
  | `openyida formula evaluate <formula\|file> [--schema file]` | Static-check formula syntax and field references |
369
372
  | `openyida update` | Update OpenYida through npm |
370
373
  | `openyida export-conversation [options]` | Export AI conversation history |
374
+ | `openyida feedback <setup\|url\|dismiss\|status>` | Create a public Yida feedback form, generate privacy-safe feedback links, and manage local reminder snooze state |
371
375
  | `openyida flash-to-prd --file <path> --name "<project>"` | Convert flash notes or meeting notes into a PRD prompt |
372
376
  | `openyida ai text --prompt "..."` | Call Yida's text generation AI API |
373
377
  | `openyida ai image --file <image> --app-type APP_XXX` | Upload an image and call the image recognition connector |
package/bin/yida.js CHANGED
@@ -206,6 +206,8 @@ function handleFirstRunGuide() {
206
206
  }
207
207
 
208
208
  function printLoginResult(result) {
209
+ noteLoginCommandResult(result);
210
+
209
211
  if (result && (result.status === 'need_qr_scan' || result.status === 'need_corp_selection')) {
210
212
  console.log(JSON.stringify(result));
211
213
  return;
@@ -248,6 +250,41 @@ function printLoginResult(result) {
248
250
  console.log(JSON.stringify(summary));
249
251
  }
250
252
 
253
+ function noteLoginCommandResult(result) {
254
+ try {
255
+ const { recordLoginEvent, printLoginLoopFeedbackHint } = require('../lib/feedback/feedback');
256
+ let status = 'failed';
257
+ let reason = 'login_failed';
258
+
259
+ if (result && (result.csrf_token || (result.status === 'ok' && result.can_auto_use))) {
260
+ status = 'success';
261
+ reason = 'login_ok';
262
+ } else if (result && result.status === 'need_qr_scan') {
263
+ status = 'need_qr_scan';
264
+ reason = 'need_qr_scan';
265
+ } else if (result && result.status === 'need_codex_browser_login') {
266
+ status = 'need_browser_login';
267
+ reason = 'need_browser_login';
268
+ } else if (result && result.status === 'need_corp_selection') {
269
+ status = 'success';
270
+ reason = 'need_corp_selection';
271
+ } else if (result && result.status) {
272
+ reason = result.status;
273
+ }
274
+
275
+ const loopStatus = recordLoginEvent(status, {
276
+ mode: args.join(' ') || 'default',
277
+ reason,
278
+ command: 'openyida login',
279
+ });
280
+ if (status !== 'success') {
281
+ printLoginLoopFeedbackHint(loopStatus);
282
+ }
283
+ } catch {
284
+ // Login feedback tracking is best-effort and must not affect CLI output.
285
+ }
286
+ }
287
+
251
288
  function isAgentConversationEnvironment() {
252
289
  const { detectActiveTool } = require('../lib/core/utils');
253
290
  return !!detectActiveTool();
@@ -370,6 +407,12 @@ async function main() {
370
407
  break;
371
408
  }
372
409
 
410
+ case 'bridge': {
411
+ const { run } = require('../lib/bridge/bridge');
412
+ await run(args);
413
+ break;
414
+ }
415
+
373
416
  case 'env': {
374
417
  if (shouldUseEnvManagement(args)) {
375
418
  const { run } = require('../lib/core/env-cmd');
@@ -428,7 +471,7 @@ async function main() {
428
471
  } else if (loginArgs.includes('--qr')) {
429
472
  const { qrLogin } = require('../lib/auth/qr-login');
430
473
  const result = await qrLogin({ corpId: getArgValue(loginArgs, '--corp-id') });
431
- console.log(JSON.stringify(result));
474
+ printLoginResult(result);
432
475
  } else if (shouldUseAgentLogin(loginArgs)) {
433
476
  const cachedResult = checkLoginOnly({ includeSecrets: true });
434
477
  if (cachedResult.status === 'ok') {
@@ -473,7 +516,7 @@ async function main() {
473
516
  }
474
517
  const { qrLogin } = require('../lib/auth/qr-login');
475
518
  const result = await qrLogin({ corpId: getArgValue(loginArgs, '--corp-id') });
476
- console.log(JSON.stringify(result));
519
+ printLoginResult(result);
477
520
  }
478
521
  break;
479
522
  }
@@ -629,24 +672,15 @@ async function main() {
629
672
  }
630
673
 
631
674
  case 'publish': {
632
- // 参数顺序:<源文件路径> <appType> <formUuid>
633
- // publish.js 内部读取顺序:argv[2]=appType, argv[3]=formUuid, argv[4]=sourceFile
634
675
  const passThroughFlags = new Set(['--skip-lint', '--health-check', '--check', '--open', '--no-open', '--compat', '--modern', '--force']);
635
- const forwardedFlags = args.filter(arg => passThroughFlags.has(arg));
636
676
  const filteredArgs = args.filter(arg => !passThroughFlags.has(arg));
637
677
  if (filteredArgs.length < 3) {
638
678
  warn(t('cli.publish_usage'));
639
679
  warn(t('cli.publish_example'));
640
680
  process.exit(1);
641
681
  }
642
- const [sourceFile, appType, formUuid] = filteredArgs;
643
- process.argv = [
644
- process.argv[0], process.argv[1],
645
- appType, formUuid, sourceFile,
646
- ...forwardedFlags
647
- ];
648
682
  const publishMain = require('../lib/app/publish');
649
- await publishMain();
683
+ await publishMain(args);
650
684
  break;
651
685
  }
652
686
 
@@ -1007,6 +1041,12 @@ async function main() {
1007
1041
  break;
1008
1042
  }
1009
1043
 
1044
+ case 'feedback': {
1045
+ const { run: runFeedback } = require('../lib/feedback/feedback');
1046
+ await runFeedback(args);
1047
+ break;
1048
+ }
1049
+
1010
1050
  case 'task-center': {
1011
1051
  const { run: runTaskCenter } = require('../lib/core/task-center');
1012
1052
  await runTaskCenter(args);
@@ -3,10 +3,10 @@
3
3
  * publish.js - 宜搭自定义页面发布工具(Node.js 版)
4
4
  *
5
5
  * 用法:
6
- * openyida publish <appType> <formUuid> <源文件路径>
6
+ * openyida publish <源文件路径> <appType> <formUuid>
7
7
  *
8
8
  * 示例:
9
- * openyida publish APP_XXX FORM-XXX pages/xxx.js
9
+ * openyida publish pages/xxx.js APP_XXX FORM-XXX
10
10
  *
11
11
  * 流程:
12
12
  * 1. 读取源文件,通过内置 babel-transform 编译 + UglifyJS 压缩
@@ -20,7 +20,16 @@ const path = require('path');
20
20
  const https = require('https');
21
21
  const http = require('http');
22
22
  const querystring = require('querystring');
23
- const { findProjectRoot, isLoginExpired, isCsrfTokenExpired, loadCookieData, triggerLogin, refreshCsrfToken } = require('../core/utils');
23
+ const {
24
+ findProjectRoot,
25
+ isLoginExpired,
26
+ isCsrfTokenExpired,
27
+ loadCookieData,
28
+ triggerLogin,
29
+ refreshCsrfToken,
30
+ httpGet,
31
+ requestWithAutoLogin,
32
+ } = require('../core/utils');
24
33
  const { t } = require('../core/i18n');
25
34
  const { banner, step, label, success, fail, warn, info, error, result, usage, hint } = require('../core/chalk');
26
35
  const { compileSource } = require('./page-compiler');
@@ -38,8 +47,34 @@ const PREFIX = '_view';
38
47
 
39
48
  // ── 参数解析 ─────────────────────────────────────────
40
49
 
41
- function parseArgs() {
42
- const openOption = parseOpenOption(process.argv.slice(2));
50
+ function looksLikeAppType(value) {
51
+ return /^APP_[A-Z0-9]+$/i.test(value || '');
52
+ }
53
+
54
+ function looksLikeFormUuid(value) {
55
+ return /^FORM-[A-Z0-9]+$/i.test(value || '');
56
+ }
57
+
58
+ function normalizePublishArgs(positionals) {
59
+ const [first, second, third] = positionals;
60
+
61
+ if (looksLikeAppType(first) && looksLikeFormUuid(second)) {
62
+ return {
63
+ appType: first,
64
+ formUuid: second,
65
+ sourceFile: third,
66
+ };
67
+ }
68
+
69
+ return {
70
+ sourceFile: first,
71
+ appType: second,
72
+ formUuid: third,
73
+ };
74
+ }
75
+
76
+ function parseArgs(argv = process.argv.slice(2)) {
77
+ const openOption = parseOpenOption(argv);
43
78
  const args = openOption.args;
44
79
  const skipLint = args.includes('--skip-lint');
45
80
  const healthCheck = args.includes('--health-check') || args.includes('--check');
@@ -51,10 +86,11 @@ function parseArgs() {
51
86
  usage(t('publish.usage'), t('publish.example'));
52
87
  process.exit(1);
53
88
  }
89
+ const { appType, formUuid, sourceFile } = normalizePublishArgs(filteredArgs);
54
90
  return {
55
- appType: filteredArgs[0],
56
- formUuid: filteredArgs[1],
57
- sourceFile: filteredArgs[2],
91
+ appType,
92
+ formUuid,
93
+ sourceFile,
58
94
  skipLint,
59
95
  healthCheck,
60
96
  compat,
@@ -88,16 +124,240 @@ function generateSuffix() {
88
124
 
89
125
  // ── 2. 构建 Schema ──────────────────────────────────
90
126
 
91
- function buildSchemaContent(sourceCode, compiledCode, formUuid) {
127
+ function getGlobalDataSourceFitConfig() {
128
+ const fitCompiled = "'use strict';\n\nvar __preParser__ = function fit(response) {\n var content = response.content !== undefined ? response.content : response;\n var error = {\n message: response.errorMsg || response.errors && response.errors[0] && response.errors[0].msg || response.content || '远程数据源请求出错,success is false'\n };\n var success = true;\n if (response.success !== undefined) {\n success = response.success;\n } else if (response.hasError !== undefined) {\n success = !response.hasError;\n }\n return {\n content: content,\n success: success,\n error: error\n };\n};";
129
+ const fitSource = "function fit(response) {\r\n const content = (response.content !== undefined) ? response.content : response;\r\n const error = {\r\n message: response.errorMsg ||\r\n (response.errors && response.errors[0] && response.errors[0].msg) ||\r\n response.content || '远程数据源请求出错,success is false',\r\n };\r\n let success = true;\r\n if (response.success !== undefined) {\r\n success = response.success;\r\n } else if (response.hasError !== undefined) {\r\n success = !response.hasError;\r\n }\r\n return {\r\n content,\r\n success,\r\n error,\r\n };\r\n}";
130
+
131
+ return {
132
+ fit: {
133
+ compiled: fitCompiled,
134
+ source: fitSource,
135
+ type: 'js',
136
+ error: {},
137
+ },
138
+ };
139
+ }
140
+
141
+ function buildDefaultPageDataSource(formUuid) {
142
+ const urlParamsDataSource = {
143
+ id: 'VCB660714833IBHEOXK376TA7XJH2AXUWR8MMW',
144
+ name: 'urlParams',
145
+ description: '当前页面地址的参数:如 aliwork.com/APP_XXX/workbench?id=1&name=宜搭,可通过 this.state.urlParams.name 获取到宜搭',
146
+ formUuid: formUuid,
147
+ protocal: 'URI',
148
+ isReadonly: true,
149
+ };
150
+ const timestampDataSource = {
151
+ id: '',
152
+ name: 'timestamp',
153
+ description: '',
154
+ formUuid: formUuid,
155
+ protocal: 'VALUE',
156
+ initialData: '',
157
+ };
158
+
159
+ return {
160
+ offline: [],
161
+ globalConfig: getGlobalDataSourceFitConfig(),
162
+ online: [urlParamsDataSource, timestampDataSource],
163
+ list: [urlParamsDataSource, timestampDataSource],
164
+ sync: true,
165
+ };
166
+ }
167
+
168
+ function cloneJson(value) {
169
+ if (value === undefined || value === null) {
170
+ return value;
171
+ }
172
+ return JSON.parse(JSON.stringify(value));
173
+ }
174
+
175
+ function getDataSourceIdentity(item) {
176
+ if (!item || typeof item !== 'object') {
177
+ return '';
178
+ }
179
+ if (isBuiltInPageDataSource(item)) {
180
+ return 'builtin:' + item.name;
181
+ }
182
+ if (item.id) {
183
+ return 'id:' + item.id;
184
+ }
185
+ if (item.name && item.protocal) {
186
+ return 'name:' + item.name + '|protocal:' + item.protocal;
187
+ }
188
+ if (item.name) {
189
+ return 'name:' + item.name;
190
+ }
191
+ return JSON.stringify(item);
192
+ }
193
+
194
+ function mergeDataSourceArray(existingItems, generatedItems) {
195
+ const merged = Array.isArray(existingItems) ? cloneJson(existingItems) : [];
196
+ const seen = new Set(merged.map(getDataSourceIdentity).filter(Boolean));
197
+
198
+ (Array.isArray(generatedItems) ? generatedItems : []).forEach((item) => {
199
+ const identity = getDataSourceIdentity(item);
200
+ if (!identity || !seen.has(identity)) {
201
+ merged.push(cloneJson(item));
202
+ if (identity) {
203
+ seen.add(identity);
204
+ }
205
+ }
206
+ });
207
+
208
+ return merged;
209
+ }
210
+
211
+ function mergePageDataSource(existingDataSource, generatedDataSource) {
212
+ if (!existingDataSource || typeof existingDataSource !== 'object') {
213
+ return cloneJson(generatedDataSource);
214
+ }
215
+
216
+ const existing = cloneJson(existingDataSource);
217
+ const generated = cloneJson(generatedDataSource || {});
218
+ const merged = Object.assign({}, generated, existing);
219
+
220
+ merged.offline = mergeDataSourceArray(existing.offline, generated.offline);
221
+ merged.online = mergeDataSourceArray(existing.online, generated.online);
222
+ merged.list = mergeDataSourceArray(existing.list, generated.list);
223
+ merged.globalConfig = Object.assign(
224
+ {},
225
+ generated.globalConfig || {},
226
+ existing.globalConfig || {}
227
+ );
228
+ merged.sync = existing.sync !== undefined ? existing.sync : generated.sync;
229
+
230
+ return merged;
231
+ }
232
+
233
+ function extractSchemaContent(schemaResult) {
234
+ if (!schemaResult) {
235
+ return null;
236
+ }
237
+
238
+ let content = schemaResult.content !== undefined ? schemaResult.content : schemaResult;
239
+ if (typeof content === 'string') {
240
+ try {
241
+ content = JSON.parse(content);
242
+ } catch {
243
+ return null;
244
+ }
245
+ }
246
+
247
+ if (content && typeof content === 'object' && content.pages) {
248
+ return content;
249
+ }
250
+ if (schemaResult.pages) {
251
+ return schemaResult;
252
+ }
253
+ return null;
254
+ }
255
+
256
+ function extractPageDataSource(schema) {
257
+ if (!schema || !Array.isArray(schema.pages)) {
258
+ return null;
259
+ }
260
+
261
+ function findPageDataSource(node) {
262
+ if (!node || typeof node !== 'object') {
263
+ return null;
264
+ }
265
+ if (node.componentName === 'Page' && node.dataSource) {
266
+ return node.dataSource;
267
+ }
268
+ const children = Array.isArray(node.children) ? node.children : [];
269
+ for (const child of children) {
270
+ const dataSource = findPageDataSource(child);
271
+ if (dataSource) {
272
+ return dataSource;
273
+ }
274
+ }
275
+ return null;
276
+ }
277
+
278
+ for (const page of schema.pages) {
279
+ const tree = page && Array.isArray(page.componentsTree) ? page.componentsTree : [];
280
+ for (const component of tree) {
281
+ const dataSource = findPageDataSource(component);
282
+ if (dataSource) {
283
+ return dataSource;
284
+ }
285
+ }
286
+ }
287
+ return null;
288
+ }
289
+
290
+ function isBuiltInPageDataSource(item) {
291
+ return !!(item && (item.name === 'urlParams' || item.name === 'timestamp'));
292
+ }
293
+
294
+ function countCustomPageDataSources(dataSource) {
295
+ if (!dataSource || typeof dataSource !== 'object') {
296
+ return 0;
297
+ }
298
+
299
+ const identities = new Set();
300
+ ['offline', 'online', 'list'].forEach((key) => {
301
+ (Array.isArray(dataSource[key]) ? dataSource[key] : []).forEach((item) => {
302
+ if (!isBuiltInPageDataSource(item)) {
303
+ const identity = getDataSourceIdentity(item);
304
+ if (identity) {
305
+ identities.add(identity);
306
+ }
307
+ }
308
+ });
309
+ });
310
+ return identities.size;
311
+ }
312
+
313
+ async function fetchExistingSchemaContent(appType, formUuid, authRef) {
314
+ const result = await requestWithAutoLogin((auth) => {
315
+ return httpGet(
316
+ auth.baseUrl,
317
+ `/alibaba/web/${appType}/${PREFIX}/query/formdesign/getFormSchema.json`,
318
+ { formUuid, schemaVersion: SCHEMA_VERSION },
319
+ auth.cookies
320
+ );
321
+ }, authRef);
322
+
323
+ if (!result || result.success === false || result.__needLogin || result.__csrfExpired) {
324
+ const errorMsg = result ? result.errorMsg || t('common.unknown_error') : t('common.request_failed');
325
+ throw new Error(errorMsg);
326
+ }
327
+
328
+ const schema = extractSchemaContent(result);
329
+ if (!schema) {
330
+ throw new Error(t('create_form.schema_parse_failed'));
331
+ }
332
+
333
+ return schema;
334
+ }
335
+
336
+ async function readExistingPageDataSource(appType, formUuid, authRef) {
337
+ info(t('publish.data_source_fetching'));
338
+ const existingSchema = await fetchExistingSchemaContent(appType, formUuid, authRef);
339
+ const dataSource = extractPageDataSource(existingSchema);
340
+ const customCount = countCustomPageDataSources(dataSource);
341
+
342
+ if (customCount > 0) {
343
+ success(t('publish.data_source_preserved', String(customCount)));
344
+ } else {
345
+ info(t('publish.data_source_none'));
346
+ }
347
+
348
+ return dataSource;
349
+ }
350
+
351
+ function buildSchemaContent(sourceCode, compiledCode, formUuid, options = {}) {
92
352
  info(t('publish.building_schema'));
93
353
  const nextNodeId = createNodeIdGenerator();
94
354
 
95
355
  // 构造函数代码(固定模板)
96
356
  const constructorCode = "function constructor() {\nvar module = { exports: {} };\nvar _this = this;\nthis.__initMethods__(module.exports, module);\nObject.keys(module.exports).forEach(function(item) {\n if(typeof module.exports[item] === 'function'){\n _this[item] = module.exports[item];\n }\n});\n\n}";
97
-
98
- // 全局数据源 fit 函数(固定模板)
99
- const fitCompiled = "'use strict';\n\nvar __preParser__ = function fit(response) {\n var content = response.content !== undefined ? response.content : response;\n var error = {\n message: response.errorMsg || response.errors && response.errors[0] && response.errors[0].msg || response.content || '远程数据源请求出错,success is false'\n };\n var success = true;\n if (response.success !== undefined) {\n success = response.success;\n } else if (response.hasError !== undefined) {\n success = !response.hasError;\n }\n return {\n content: content,\n success: success,\n error: error\n };\n};";
100
- const fitSource = "function fit(response) {\r\n const content = (response.content !== undefined) ? response.content : response;\r\n const error = {\r\n message: response.errorMsg ||\r\n (response.errors && response.errors[0] && response.errors[0].msg) ||\r\n response.content || '远程数据源请求出错,success is false',\r\n };\r\n let success = true;\r\n if (response.success !== undefined) {\r\n success = response.success;\r\n } else if (response.hasError !== undefined) {\r\n success = !response.hasError;\r\n }\r\n return {\r\n content,\r\n success,\r\n error,\r\n };\r\n}";
357
+ const pageDataSource = mergePageDataSource(
358
+ options.existingDataSource,
359
+ buildDefaultPageDataSource(formUuid)
360
+ );
101
361
 
102
362
  const schema = {
103
363
  schemaType: 'superform',
@@ -156,54 +416,7 @@ function buildSchemaContent(sourceCode, compiledCode, formUuid) {
156
416
  compiled: 'function (exports, module) { /*set actions code here*/ }',
157
417
  },
158
418
  },
159
- dataSource: {
160
- offline: [],
161
- globalConfig: {
162
- fit: {
163
- compiled: fitCompiled,
164
- source: fitSource,
165
- type: 'js',
166
- error: {},
167
- },
168
- },
169
- online: [
170
- {
171
- id: 'VCB660714833IBHEOXK376TA7XJH2AXUWR8MMW',
172
- name: 'urlParams',
173
- description: '当前页面地址的参数:如 aliwork.com/APP_XXX/workbench?id=1&name=宜搭,可通过 this.state.urlParams.name 获取到宜搭',
174
- formUuid: formUuid,
175
- protocal: 'URI',
176
- isReadonly: true,
177
- },
178
- {
179
- id: '',
180
- name: 'timestamp',
181
- description: '',
182
- formUuid: formUuid,
183
- protocal: 'VALUE',
184
- initialData: '',
185
- },
186
- ],
187
- list: [
188
- {
189
- id: 'VCB660714833IBHEOXK376TA7XJH2AXUWR8MMW',
190
- name: 'urlParams',
191
- description: '当前页面地址的参数:如 aliwork.com/APP_XXX/workbench?id=1&name=宜搭,可通过 this.state.urlParams.name 获取到宜搭',
192
- formUuid: formUuid,
193
- protocal: 'URI',
194
- isReadonly: true,
195
- },
196
- {
197
- id: '',
198
- name: 'timestamp',
199
- description: '',
200
- formUuid: formUuid,
201
- protocal: 'VALUE',
202
- initialData: '',
203
- },
204
- ],
205
- sync: true,
206
- },
419
+ dataSource: pageDataSource,
207
420
  lifeCycles: {
208
421
  constructor: {
209
422
  type: 'js',
@@ -647,8 +860,8 @@ function sendHealthCheckRequest(pageUrl, cookies) {
647
860
 
648
861
  // ── 主流程 ────────────────────────────────────────────
649
862
 
650
- async function main() {
651
- const { appType, formUuid, sourceFile, skipLint, healthCheck, compat, force, browserOpenMode } = parseArgs();
863
+ async function main(argv) {
864
+ const { appType, formUuid, sourceFile, skipLint, healthCheck, compat, force, browserOpenMode } = parseArgs(argv);
652
865
 
653
866
  let sourcePath = path.resolve(sourceFile);
654
867
  if (!fs.existsSync(sourcePath)) {
@@ -687,8 +900,6 @@ async function main() {
687
900
 
688
901
  step(1, t('publish.step_compile'));
689
902
  const { sourceCode, compiledCode } = compileSource(sourcePath);
690
- const schemaContent = buildSchemaContent(sourceCode, compiledCode, formUuid);
691
- success(t('publish.schema_built'));
692
903
 
693
904
  step(2, t('common.step_login', 2));
694
905
  let cookieData = loadCookieData();
@@ -709,6 +920,20 @@ async function main() {
709
920
  cookies = authRef.cookies;
710
921
  baseUrl = authRef.baseUrl;
711
922
 
923
+ let existingDataSource = null;
924
+ try {
925
+ existingDataSource = await readExistingPageDataSource(appType, formUuid, authRef);
926
+ } catch (dataSourceError) {
927
+ fail(t('publish.data_source_fetch_failed', dataSourceError.message));
928
+ process.exit(1);
929
+ }
930
+ csrfToken = authRef.csrfToken;
931
+ cookies = authRef.cookies;
932
+ baseUrl = authRef.baseUrl;
933
+
934
+ const schemaContent = buildSchemaContent(sourceCode, compiledCode, formUuid, { existingDataSource });
935
+ success(t('publish.schema_built'));
936
+
712
937
  banner(t('publish.title'));
713
938
  label('Base URL:', baseUrl);
714
939
  label('App Type:', appType);
@@ -825,10 +1050,18 @@ if (require.main === module) {
825
1050
  } else {
826
1051
  // 如果作为模块被 require,导出 main 函数
827
1052
  module.exports = main;
1053
+ module.exports.parseArgs = parseArgs;
1054
+ module.exports.normalizePublishArgs = normalizePublishArgs;
828
1055
  module.exports.findDuplicateSourceMismatches = findDuplicateSourceMismatches;
829
1056
  module.exports.sendHealthCheckRequest = sendHealthCheckRequest;
830
1057
  module.exports.normalizeFormType = normalizeFormType;
831
1058
  module.exports.findPublishTarget = findPublishTarget;
832
1059
  module.exports.isCustomPageTarget = isCustomPageTarget;
833
1060
  module.exports.verifyPublishTarget = verifyPublishTarget;
1061
+ module.exports.buildDefaultPageDataSource = buildDefaultPageDataSource;
1062
+ module.exports.mergePageDataSource = mergePageDataSource;
1063
+ module.exports.extractSchemaContent = extractSchemaContent;
1064
+ module.exports.extractPageDataSource = extractPageDataSource;
1065
+ module.exports.countCustomPageDataSources = countCustomPageDataSources;
1066
+ module.exports.buildSchemaContent = buildSchemaContent;
834
1067
  }