openyida 2026.7.22 → 2026.7.23-2

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.
@@ -29,6 +29,10 @@
29
29
  */
30
30
 
31
31
  const Babel = require('@babel/standalone');
32
+ const {
33
+ assertNoEmojiInArtifactName,
34
+ assertNoEmojiInText,
35
+ } = require('../core/no-emoji-guard');
32
36
 
33
37
  /**
34
38
  * 依赖 → window 别名白名单。
@@ -260,6 +264,16 @@ function esmToWindowPlugin({ types: t }, options = {}) {
260
264
  * @returns {{ runtimeCode: string, importedModules: string }}
261
265
  */
262
266
  function compileCanvasLocal(source, options = {}) {
267
+ if (options.sourcePath) {
268
+ assertNoEmojiInArtifactName(options.sourcePath, {
269
+ code: 'OPENYIDA_PAGE_FILENAME_EMOJI_FORBIDDEN',
270
+ });
271
+ }
272
+ assertNoEmojiInText(source, {
273
+ artifact: options.sourcePath || 'Code Canvas source',
274
+ code: 'OPENYIDA_CANVAS_SOURCE_EMOJI_FORBIDDEN',
275
+ });
276
+
263
277
  const importedModules = extractImportedModules(source);
264
278
 
265
279
  // 第一步:剥类型 + 转 JSX(classic runtime,产出 React.createElement,
@@ -304,6 +318,10 @@ function compileCanvasLocal(source, options = {}) {
304
318
  });
305
319
 
306
320
  const runtimeCode = stage2.code || '';
321
+ assertNoEmojiInText(runtimeCode, {
322
+ artifact: options.sourcePath ? options.sourcePath + ' runtime' : 'Code Canvas runtime',
323
+ code: 'OPENYIDA_CANVAS_SOURCE_EMOJI_FORBIDDEN',
324
+ });
307
325
  return {
308
326
  runtimeCode,
309
327
  importedModules: JSON.stringify(importedModules),
@@ -17,6 +17,26 @@ function readJsonInput(value, options) {
17
17
  return fs.readFileSync(resolvedPath, 'utf-8');
18
18
  }
19
19
 
20
+ function normalizeCreateFields(fields) {
21
+ if (
22
+ !Array.isArray(fields)
23
+ || fields.length === 0
24
+ || !fields.every((item) => (
25
+ item
26
+ && typeof item === 'object'
27
+ && !Array.isArray(item)
28
+ && String(item.action || '').toLowerCase() === 'add'
29
+ && item.field
30
+ && typeof item.field === 'object'
31
+ && !Array.isArray(item.field)
32
+ ))
33
+ ) {
34
+ return fields;
35
+ }
36
+
37
+ return fields.map((item) => item.field);
38
+ }
39
+
20
40
  function createDefinitionReaders(dependencies) {
21
41
  const {
22
42
  fs,
@@ -43,9 +63,9 @@ function createDefinitionReaders(dependencies) {
43
63
  let columns = 1;
44
64
 
45
65
  if (Array.isArray(parsed)) {
46
- fields = parsed;
66
+ fields = normalizeCreateFields(parsed);
47
67
  } else if (typeof parsed === 'object' && parsed !== null) {
48
- fields = parsed.fields || [];
68
+ fields = normalizeCreateFields(parsed.fields || []);
49
69
  columns = parsed.columns !== undefined ? parsed.columns : 1;
50
70
  if (Array.isArray(parsed.validations)) {
51
71
  validations = parsed.validations;
@@ -64,6 +64,10 @@ const { createAuthRef } = require('../core/yida-client');
64
64
  const { CliError } = require('../core/cli-error');
65
65
  const { t } = require('../core/i18n');
66
66
  const { safeParseJson } = require('../core/safe-json');
67
+ const {
68
+ assertNoEmojiInArtifactName,
69
+ assertNoEmojiInValue,
70
+ } = require('../core/no-emoji-guard');
67
71
  const { buildYidaI18n, normalizeYidaLocale, resolveContentLocale } = require('../core/yida-i18n');
68
72
  const { banner, step, label, success, fail, warn, info, error, result, usage, hint, listItem } = require('../core/chalk');
69
73
  const { parseOpenOption: parseBrowserOpenOption, withBrowserHandoff } = require('../core/browser-handoff');
@@ -95,7 +99,11 @@ function parseOpenOption(inputArgs) {
95
99
  const OPTION_FIELD_TYPES = ['RadioField', 'SelectField', 'CheckboxField', 'MultiSelectField'];
96
100
 
97
101
  function throwCreateFormError(message, code, details) {
98
- throw new CliError(message, {
102
+ throw createCreateFormError(message, code, details);
103
+ }
104
+
105
+ function createCreateFormError(message, code, details) {
106
+ return new CliError(message, {
99
107
  code: code || 'CREATE_FORM_FAILED',
100
108
  details,
101
109
  });
@@ -210,6 +218,28 @@ const FIELD_TYPE_ALIAS = {
210
218
  serialnumberfield: 'SerialNumberField',
211
219
  };
212
220
 
221
+ const SUPPORTED_BUSINESS_FIELD_TYPES = [
222
+ 'TextField',
223
+ 'TextareaField',
224
+ 'RadioField',
225
+ 'SelectField',
226
+ 'CheckboxField',
227
+ 'MultiSelectField',
228
+ 'NumberField',
229
+ 'RateField',
230
+ 'DateField',
231
+ 'CascadeDateField',
232
+ 'EmployeeField',
233
+ 'DepartmentSelectField',
234
+ 'CountrySelectField',
235
+ 'AddressField',
236
+ 'AttachmentField',
237
+ 'ImageField',
238
+ 'TableField',
239
+ 'AssociationFormField',
240
+ 'SerialNumberField',
241
+ ];
242
+
213
243
  const FORM_PRESENTATION_TYPE_ALIAS = {
214
244
  Divider: 'Divider',
215
245
  divider: 'Divider',
@@ -262,11 +292,28 @@ function normalizeComponentAlias(field) {
262
292
  return String(rawAlias).trim();
263
293
  }
264
294
 
295
+ function readFormDefinitionType(field) {
296
+ if (!field || typeof field !== 'object' || Array.isArray(field)) {
297
+ return '';
298
+ }
299
+ if (field.type !== undefined && field.type !== null && field.type !== '') {
300
+ return String(field.type).trim();
301
+ }
302
+ if (field.componentName !== undefined && field.componentName !== null && field.componentName !== '') {
303
+ return String(field.componentName).trim();
304
+ }
305
+ if (field.componentType !== undefined && field.componentType !== null && field.componentType !== '') {
306
+ return String(field.componentType).trim();
307
+ }
308
+ return '';
309
+ }
310
+
265
311
  function normalizeFormDefinitionType(field) {
266
- if (!field || !field.type) {
312
+ const rawType = readFormDefinitionType(field);
313
+ if (!rawType) {
267
314
  return '';
268
315
  }
269
- return FORM_PRESENTATION_TYPE_ALIAS[field.type] || FIELD_TYPE_ALIAS[field.type] || field.type;
316
+ return FORM_PRESENTATION_TYPE_ALIAS[rawType] || FIELD_TYPE_ALIAS[rawType] || rawType;
270
317
  }
271
318
 
272
319
  function isFormPresentationComponent(componentName) {
@@ -277,6 +324,219 @@ function isFormPresentationDefinition(field) {
277
324
  return isFormPresentationComponent(normalizeFormDefinitionType(field));
278
325
  }
279
326
 
327
+ function isSupportedBusinessFieldType(componentName) {
328
+ return SUPPORTED_BUSINESS_FIELD_TYPES.indexOf(componentName) !== -1;
329
+ }
330
+
331
+ function getDefinitionDisplayName(field) {
332
+ if (!field || typeof field !== 'object') {
333
+ return '';
334
+ }
335
+ return field.label || field.title || readFormDefinitionType(field) || '';
336
+ }
337
+
338
+ function buildInvalidFieldDefinitionDetails(field, fieldPath, extra) {
339
+ const details = Object.assign({
340
+ path: fieldPath || 'fields',
341
+ label: field && typeof field === 'object' ? field.label : undefined,
342
+ title: field && typeof field === 'object' ? field.title : undefined,
343
+ type: readFormDefinitionType(field),
344
+ }, extra || {});
345
+ Object.keys(details).forEach(function (key) {
346
+ if (details[key] === undefined || details[key] === '') {
347
+ delete details[key];
348
+ }
349
+ });
350
+ return details;
351
+ }
352
+
353
+ function throwInvalidFieldDefinition(message, code, field, fieldPath, extra) {
354
+ throwCreateFormError(
355
+ message,
356
+ code || 'CREATE_FORM_INVALID_FIELD_DEFINITION',
357
+ buildInvalidFieldDefinitionDetails(field, fieldPath, extra)
358
+ );
359
+ }
360
+
361
+ function validatePlainFieldObject(field, fieldPath) {
362
+ if (!field || typeof field !== 'object' || Array.isArray(field)) {
363
+ throwInvalidFieldDefinition(
364
+ '字段定义必须是对象: ' + fieldPath,
365
+ 'CREATE_FORM_INVALID_FIELD_DEFINITION',
366
+ field,
367
+ fieldPath
368
+ );
369
+ }
370
+ }
371
+
372
+ function validateContainerChildrenArray(field, fieldPath, componentName) {
373
+ if (field.children === undefined || field.children === null) {
374
+ return [];
375
+ }
376
+ if (!Array.isArray(field.children)) {
377
+ throwInvalidFieldDefinition(
378
+ componentName + '.children 必须是数组: ' + fieldPath,
379
+ 'CREATE_FORM_INVALID_CONTAINER_CHILDREN',
380
+ field,
381
+ fieldPath,
382
+ { componentName }
383
+ );
384
+ }
385
+ return field.children;
386
+ }
387
+
388
+ function hasNonEmptyAssociationFormUuid(field) {
389
+ const associationForm = field && field.associationForm;
390
+ if (!associationForm || typeof associationForm !== 'object' || Array.isArray(associationForm)) {
391
+ return false;
392
+ }
393
+ return typeof associationForm.formUuid === 'string' && associationForm.formUuid.trim().length > 0;
394
+ }
395
+
396
+ function validateAssociationFormFieldDefinition(field, fieldPath) {
397
+ if (hasNonEmptyAssociationFormUuid(field)) {
398
+ return;
399
+ }
400
+ throwInvalidFieldDefinition(
401
+ 'AssociationFormField.associationForm.formUuid 必须是非空字符串: ' + fieldPath,
402
+ 'CREATE_FORM_ASSOCIATION_FORM_UUID_MISSING',
403
+ field,
404
+ fieldPath,
405
+ { componentName: 'AssociationFormField' }
406
+ );
407
+ }
408
+
409
+ function validateFieldDefinition(field, fieldPath, options) {
410
+ const validationOptions = options || {};
411
+ validatePlainFieldObject(field, fieldPath);
412
+
413
+ const componentName = normalizeFormDefinitionType(field);
414
+ if (!componentName) {
415
+ throwInvalidFieldDefinition(
416
+ '字段定义缺少 type/componentName/componentType: ' + fieldPath,
417
+ 'CREATE_FORM_FIELD_TYPE_MISSING',
418
+ field,
419
+ fieldPath
420
+ );
421
+ }
422
+
423
+ if (isFormPresentationComponent(componentName)) {
424
+ if (validationOptions.disallowPresentation) {
425
+ throwInvalidFieldDefinition(
426
+ 'TableField.children 不支持展示布局组件: ' + componentName,
427
+ 'CREATE_FORM_TABLE_CHILD_PRESENTATION_UNSUPPORTED',
428
+ field,
429
+ fieldPath,
430
+ { componentName }
431
+ );
432
+ }
433
+ if (componentName === 'Column' && !validationOptions.allowColumn) {
434
+ throwInvalidFieldDefinition(
435
+ 'Column 只能作为 ColumnContainer.children 的 Column 对象使用: ' + fieldPath,
436
+ 'CREATE_FORM_COLUMN_OUTSIDE_COLUMNS_LAYOUT',
437
+ field,
438
+ fieldPath,
439
+ { componentName }
440
+ );
441
+ }
442
+ if (componentName === 'ColumnsLayout') {
443
+ validateColumnsLayoutDefinition(field, fieldPath);
444
+ } else if (componentName === 'PageSection') {
445
+ validateContainerChildrenArray(field, fieldPath, componentName).forEach(function (child, childIndex) {
446
+ validateFieldDefinition(child, fieldPath + '.children[' + childIndex + ']');
447
+ });
448
+ } else if (componentName === 'Column') {
449
+ validateContainerChildrenArray(field, fieldPath, componentName).forEach(function (child, childIndex) {
450
+ validateFieldDefinition(child, fieldPath + '.children[' + childIndex + ']');
451
+ });
452
+ }
453
+ return componentName;
454
+ }
455
+
456
+ if (!isSupportedBusinessFieldType(componentName)) {
457
+ throwInvalidFieldDefinition(
458
+ '不支持的字段类型: ' + componentName,
459
+ 'CREATE_FORM_UNSUPPORTED_FIELD_TYPE',
460
+ field,
461
+ fieldPath,
462
+ { componentName }
463
+ );
464
+ }
465
+
466
+ if (componentName === 'AssociationFormField') {
467
+ validateAssociationFormFieldDefinition(field, fieldPath);
468
+ }
469
+
470
+ if (componentName === 'TableField') {
471
+ validateTableFieldChildren(field, fieldPath, validationOptions);
472
+ }
473
+
474
+ return componentName;
475
+ }
476
+
477
+ function validateColumnsLayoutDefinition(field, fieldPath) {
478
+ const columns = validateContainerChildrenArray(field, fieldPath, 'ColumnsLayout');
479
+ columns.forEach(function (columnDefinition, columnIndex) {
480
+ const columnPath = fieldPath + '.children[' + columnIndex + ']';
481
+ if (Array.isArray(columnDefinition)) {
482
+ columnDefinition.forEach(function (child, childIndex) {
483
+ validateFieldDefinition(child, columnPath + '[' + childIndex + ']');
484
+ });
485
+ return;
486
+ }
487
+ if (
488
+ columnDefinition &&
489
+ typeof columnDefinition === 'object' &&
490
+ !Array.isArray(columnDefinition) &&
491
+ normalizeFormDefinitionType(columnDefinition) === 'Column'
492
+ ) {
493
+ validateFieldDefinition(columnDefinition, columnPath, { allowColumn: true });
494
+ return;
495
+ }
496
+ throwInvalidFieldDefinition(
497
+ 'ColumnContainer.children 只能包含二维字段数组或 Column 对象: ' + columnPath,
498
+ 'CREATE_FORM_INVALID_COLUMNS_LAYOUT_CHILDREN',
499
+ columnDefinition,
500
+ columnPath,
501
+ { componentName: normalizeFormDefinitionType(columnDefinition) || readFormDefinitionType(columnDefinition) }
502
+ );
503
+ });
504
+ }
505
+
506
+ function validateTableFieldChildren(field, fieldPath, options) {
507
+ if (field.children === undefined || field.children === null) {
508
+ return;
509
+ }
510
+ if (!Array.isArray(field.children)) {
511
+ throwInvalidFieldDefinition(
512
+ 'TableField.children 必须是字段数组: ' + fieldPath,
513
+ 'CREATE_FORM_INVALID_TABLE_CHILDREN',
514
+ field,
515
+ fieldPath,
516
+ { componentName: 'TableField' }
517
+ );
518
+ }
519
+ field.children.forEach(function (child, childIndex) {
520
+ validateFieldDefinition(child, fieldPath + '.children[' + childIndex + ']', Object.assign({}, options, {
521
+ disallowPresentation: true,
522
+ }));
523
+ });
524
+ }
525
+
526
+ function validateFormFieldDefinitions(fields, rootPath) {
527
+ if (!Array.isArray(fields)) {
528
+ throwInvalidFieldDefinition(
529
+ '字段定义必须是数组',
530
+ 'CREATE_FORM_INVALID_FIELD_DEFINITION',
531
+ null,
532
+ rootPath || 'fields'
533
+ );
534
+ }
535
+ fields.forEach(function (field, index) {
536
+ validateFieldDefinition(field, (rootPath || 'fields') + '[' + index + ']');
537
+ });
538
+ }
539
+
280
540
  function normalizeI18nValue(value, fallback) {
281
541
  if (value && typeof value === 'object') {
282
542
  return value;
@@ -465,7 +725,16 @@ function buildFormNodeComponents(fields) {
465
725
  // ── 生成字段组件 ─────────────────────────────────────
466
726
 
467
727
  function buildFieldComponent(field) {
468
- const componentName = FIELD_TYPE_ALIAS[field.type] || field.type;
728
+ const componentName = normalizeFormDefinitionType(field);
729
+ if (!isSupportedBusinessFieldType(componentName)) {
730
+ throwInvalidFieldDefinition(
731
+ componentName ? '不支持的字段类型: ' + componentName : '字段定义缺少 type/componentName/componentType',
732
+ componentName ? 'CREATE_FORM_UNSUPPORTED_FIELD_TYPE' : 'CREATE_FORM_FIELD_TYPE_MISSING',
733
+ field,
734
+ 'field',
735
+ { componentName }
736
+ );
737
+ }
469
738
  const fieldId = generateFieldId(componentName);
470
739
  const nodeId = nextNodeId();
471
740
 
@@ -3836,20 +4105,14 @@ function ensureComponentsMapForComponent(schema, component) {
3836
4105
  }
3837
4106
  }
3838
4107
 
3839
- function getDefinitionDisplayName(field) {
3840
- if (!field || typeof field !== 'object') {
3841
- return '';
3842
- }
3843
- return field.label || field.title || field.type || '';
3844
- }
3845
-
3846
4108
  function countDataFieldDefinitions(fields) {
3847
4109
  let count = 0;
3848
4110
  (fields || []).forEach(function (field) {
3849
4111
  if (!field || typeof field !== 'object' || Array.isArray(field)) {
3850
4112
  return;
3851
4113
  }
3852
- if (!isFormPresentationDefinition(field)) {
4114
+ const componentName = normalizeFormDefinitionType(field);
4115
+ if (isSupportedBusinessFieldType(componentName)) {
3853
4116
  count++;
3854
4117
  }
3855
4118
  if (Array.isArray(field.children)) {
@@ -3881,11 +4144,12 @@ function applyChangesToSchema(schema, changes) {
3881
4144
  const actionDesc = t('create_form.action_label', changeIndex + 1, change.action);
3882
4145
 
3883
4146
  if (change.action === 'add') {
3884
- if (!change.field || !change.field.type || (!change.field.label && !change.field.title && !isFormPresentationDefinition(change.field))) {
4147
+ if (!change.field || !normalizeFormDefinitionType(change.field) || (!change.field.label && !change.field.title && !isFormPresentationDefinition(change.field))) {
3885
4148
  warn(actionDesc + t('create_form.add_missing_field'));
3886
4149
  return;
3887
4150
  }
3888
4151
 
4152
+ validateFormFieldDefinitions([change.field], 'changes[' + changeIndex + '].field');
3889
4153
  const newComponent = buildFormNodeComponent(change.field);
3890
4154
  ensureComponentsMapForComponent(schema, newComponent);
3891
4155
  const displayName = getDefinitionDisplayName(change.field);
@@ -4057,12 +4321,75 @@ function requireSchemaServerRevision(serverRevision, details) {
4057
4321
  return serverRevision;
4058
4322
  }
4059
4323
 
4324
+ function sanitizeFailureResult(result) {
4325
+ if (!result || typeof result !== 'object') {
4326
+ return result || null;
4327
+ }
4328
+ const sanitized = {};
4329
+ ['success', 'errorMsg', 'errorCode', 'code', 'message', '__httpStatus', '__needLogin', '__csrfExpired'].forEach(function (key) {
4330
+ if (Object.prototype.hasOwnProperty.call(result, key)) {
4331
+ sanitized[key] = result[key];
4332
+ }
4333
+ });
4334
+ return sanitized;
4335
+ }
4336
+
4337
+ function buildCreateFormPostCreateFailurePayload(context) {
4338
+ const errorObject = context && context.error;
4339
+ const payload = {
4340
+ success: false,
4341
+ appType: context.appType,
4342
+ formTitle: context.formTitle,
4343
+ formUuid: context.formUuid,
4344
+ stage: context.stage || 'postCreate',
4345
+ error: errorObject && errorObject.message ? errorObject.message : String(errorObject || 'request failed'),
4346
+ errorCode: errorObject && errorObject.code ? errorObject.code : (context.errorCode || 'CREATE_FORM_POST_CREATE_FAILED'),
4347
+ retryAdvice: t('create_form.create_post_failure_retry_advice', context.appType, context.formTitle),
4348
+ };
4349
+ if (context.fieldCount !== undefined) {
4350
+ payload.fieldCount = context.fieldCount;
4351
+ }
4352
+ return payload;
4353
+ }
4354
+
4355
+ function emitCreateFormPostCreateFailure(context) {
4356
+ const errorObject = context && context.error;
4357
+ if (errorObject && errorObject.__openyidaPostCreateFailureEmitted) {
4358
+ return;
4359
+ }
4360
+ console.log(JSON.stringify(buildCreateFormPostCreateFailurePayload(context)));
4361
+ if (errorObject && typeof errorObject === 'object') {
4362
+ errorObject.__openyidaPostCreateFailureEmitted = true;
4363
+ }
4364
+ }
4365
+
4366
+ function assertNoEmojiInFormDefinition(formTitle, fields, validations, artifact) {
4367
+ assertNoEmojiInValue({
4368
+ formTitle,
4369
+ fields,
4370
+ validations,
4371
+ }, {
4372
+ artifact: artifact || 'create-form input',
4373
+ code: 'OPENYIDA_FORM_DEFINITION_EMOJI_FORBIDDEN',
4374
+ });
4375
+ }
4376
+
4377
+ function assertNoEmojiInDefinitionFileName(value) {
4378
+ const raw = String(value || '').trim();
4379
+ if (!raw || raw.startsWith('{') || raw.startsWith('[')) {
4380
+ return;
4381
+ }
4382
+ assertNoEmojiInArtifactName(raw, {
4383
+ code: 'OPENYIDA_FORM_DEFINITION_FILENAME_EMOJI_FORBIDDEN',
4384
+ });
4385
+ }
4386
+
4060
4387
  // ── 保存 Schema 并更新表单配置(create/update 共用)──
4061
4388
  //
4062
4389
  // 封装了 saveFormSchema + updateFormConfig 两步,以及各自的 302 自动重登录重试。
4063
4390
  // 返回 { saveResult, configResult }。
4064
4391
 
4065
- async function saveSchemaAndUpdateConfig(authRef, appType, formUuid, schema, version, stepOffset) {
4392
+ async function saveSchemaAndUpdateConfig(authRef, appType, formUuid, schema, version, stepOffset, failureContext) {
4066
4393
  const saveStep = stepOffset || 4;
4067
4394
  const configStep = saveStep + 1;
4068
4395
 
@@ -4071,6 +4398,10 @@ async function saveSchemaAndUpdateConfig(authRef, appType, formUuid, schema, ver
4071
4398
  info(t('create_form.formula_prefix_fixed', fixedRefs));
4072
4399
  }
4073
4400
  ensureDividerThemeAction(schema);
4401
+ assertNoEmojiInValue(schema, {
4402
+ artifact: 'Yida form schema ' + formUuid,
4403
+ code: 'OPENYIDA_FORM_SCHEMA_EMOJI_FORBIDDEN',
4404
+ });
4074
4405
  const serverRevision = requireSchemaServerRevision(version, { appType, formUuid });
4075
4406
 
4076
4407
  step(saveStep, t('create_form.step_save_schema', saveStep));
@@ -4092,12 +4423,20 @@ async function saveSchemaAndUpdateConfig(authRef, appType, formUuid, schema, ver
4092
4423
  if (saveResult && !saveResult.__needLogin) {
4093
4424
  hint(t('common.response_detail', JSON.stringify(saveResult, null, 2)));
4094
4425
  }
4095
- console.log(JSON.stringify({ success: false, formUuid: formUuid, error: saveErrorMsg }));
4096
- throwCreateFormError(saveErrorMsg, 'CREATE_FORM_SAVE_SCHEMA_FAILED', {
4426
+ const saveError = createCreateFormError(saveErrorMsg, 'CREATE_FORM_SAVE_SCHEMA_FAILED', {
4097
4427
  appType,
4098
4428
  formUuid,
4099
- result: saveResult,
4429
+ result: sanitizeFailureResult(saveResult),
4100
4430
  });
4431
+ if (failureContext) {
4432
+ emitCreateFormPostCreateFailure(Object.assign({}, failureContext, {
4433
+ stage: 'saveFormSchema',
4434
+ error: saveError,
4435
+ }));
4436
+ } else {
4437
+ console.log(JSON.stringify({ success: false, formUuid: formUuid, error: saveErrorMsg }));
4438
+ }
4439
+ throw saveError;
4101
4440
  }
4102
4441
 
4103
4442
  success(t('create_form.schema_saved'));
@@ -4126,7 +4465,10 @@ async function mainCreate(parsedArgs, authRef) {
4126
4465
  label('Fields:', fieldsJsonOrFile);
4127
4466
 
4128
4467
  step(2, t('create_form.step_read_fields', 2));
4468
+ assertNoEmojiInDefinitionFileName(fieldsJsonOrFile);
4129
4469
  const { fields, columns, validations } = readFieldsDefinition(fieldsJsonOrFile);
4470
+ assertNoEmojiInFormDefinition(formTitle, fields, validations);
4471
+ validateFormFieldDefinitions(fields);
4130
4472
  const fieldCount = countDataFieldDefinitions(fields);
4131
4473
  success(t('create_form.fields_loaded', fieldCount));
4132
4474
  label('Columns:', String(columns));
@@ -4158,30 +4500,62 @@ async function mainCreate(parsedArgs, authRef) {
4158
4500
 
4159
4501
  const formUuid = createResult.content.formUuid || createResult.content;
4160
4502
  success(t('create_form.blank_created', formUuid));
4161
- const shellSchemaResult = await requestWithAutoLogin(function (auth) {
4162
- return sendGetRequest(
4163
- auth.baseUrl,
4164
- buildApiPath(appType, 'getFormSchema', { prefix: '_view', namespace: 'alibaba' }),
4165
- { formUuid: formUuid, schemaVersion: 'V5' }
4166
- );
4167
- }, authRef);
4168
- const serverRevision = extractSchemaServerRevision(shellSchemaResult);
4503
+ let configResult;
4504
+ let postCreateStage = 'getFormSchema';
4505
+ try {
4506
+ const shellSchemaResult = await requestWithAutoLogin(function (auth) {
4507
+ return sendGetRequest(
4508
+ auth.baseUrl,
4509
+ buildApiPath(appType, 'getFormSchema', { prefix: '_view', namespace: 'alibaba' }),
4510
+ { formUuid: formUuid, schemaVersion: 'V5' }
4511
+ );
4512
+ }, authRef);
4513
+ if (!shellSchemaResult || shellSchemaResult.success === false || shellSchemaResult.__needLogin || shellSchemaResult.__csrfExpired) {
4514
+ const schemaErrorMsg = shellSchemaResult
4515
+ ? shellSchemaResult.errorMsg || shellSchemaResult.message || t('common.unknown_error')
4516
+ : t('common.request_failed');
4517
+ throwCreateFormError(schemaErrorMsg, 'CREATE_FORM_GET_SCHEMA_FAILED', {
4518
+ appType,
4519
+ formUuid,
4520
+ result: sanitizeFailureResult(shellSchemaResult),
4521
+ });
4522
+ }
4523
+ const serverRevision = extractSchemaServerRevision(shellSchemaResult);
4169
4524
 
4170
- // Step 4 & 5: 生成 Schema 并保存,然后更新表单配置
4171
- const corpId = resolveCorpId(authRef.authData);
4172
- if (!corpId) {
4173
- warn(t('create_form.no_corp_id_warning'));
4174
- } else {
4175
- info(t('create_form.corp_id_ok', corpId));
4176
- }
4525
+ // Step 4 & 5: 生成 Schema 并保存,然后更新表单配置
4526
+ const corpId = resolveCorpId(authRef.authData);
4527
+ if (!corpId) {
4528
+ warn(t('create_form.no_corp_id_warning'));
4529
+ } else {
4530
+ info(t('create_form.corp_id_ok', corpId));
4531
+ }
4177
4532
 
4178
- const schema = buildFormSchema(formTitle, fields, formUuid, corpId, appType, layout, theme, labelAlign);
4179
- const createValidationRules = collectSmartValidationRulesFromFields(fields).concat(validations || []);
4180
- if (createValidationRules.length > 0) {
4181
- const appliedValidations = applySmartValidations(schema, createValidationRules);
4182
- info('已为创建表单写入 ' + appliedValidations.appliedRules.length + ' 条字段校验');
4533
+ postCreateStage = 'buildFormSchema';
4534
+ const schema = buildFormSchema(formTitle, fields, formUuid, corpId, appType, layout, theme, labelAlign);
4535
+ const createValidationRules = collectSmartValidationRulesFromFields(fields).concat(validations || []);
4536
+ if (createValidationRules.length > 0) {
4537
+ const appliedValidations = applySmartValidations(schema, createValidationRules);
4538
+ info('已为创建表单写入 ' + appliedValidations.appliedRules.length + ' 条字段校验');
4539
+ }
4540
+ postCreateStage = 'saveSchemaAndUpdateConfig';
4541
+ const saveAndConfigResult = await saveSchemaAndUpdateConfig(authRef, appType, formUuid, schema, serverRevision, 4, {
4542
+ appType,
4543
+ formTitle,
4544
+ formUuid,
4545
+ fieldCount,
4546
+ });
4547
+ configResult = saveAndConfigResult.configResult;
4548
+ } catch (err) {
4549
+ emitCreateFormPostCreateFailure({
4550
+ appType,
4551
+ formTitle,
4552
+ formUuid,
4553
+ fieldCount,
4554
+ stage: postCreateStage,
4555
+ error: err,
4556
+ });
4557
+ throw err;
4183
4558
  }
4184
- const { configResult } = await saveSchemaAndUpdateConfig(authRef, appType, formUuid, schema, serverRevision, 4);
4185
4559
 
4186
4560
  // 输出结果
4187
4561
  const formUrl = authRef.baseUrl + '/' + appType + '/workbench/' + formUuid;
@@ -4203,11 +4577,25 @@ async function mainCreate(parsedArgs, authRef) {
4203
4577
  ['URL', formUrl],
4204
4578
  ]);
4205
4579
  hint(t('create_form.schema_ok_config_failed'));
4206
- console.log(JSON.stringify(withBrowserHandoff(
4207
- { success: true, formUuid, formTitle, appType, fieldCount, url: formUrl, configWarning: configErrorMsg },
4208
- formUrl,
4209
- { stage: 'create_form_success', title: formTitle },
4210
- parsedArgs.browserOpenMode
4580
+ const configError = createCreateFormError(configErrorMsg, 'CREATE_FORM_UPDATE_CONFIG_FAILED', {
4581
+ appType,
4582
+ formUuid,
4583
+ result: sanitizeFailureResult(configResult),
4584
+ });
4585
+ console.log(JSON.stringify(Object.assign(
4586
+ buildCreateFormPostCreateFailurePayload({
4587
+ appType,
4588
+ formTitle,
4589
+ formUuid,
4590
+ fieldCount,
4591
+ stage: 'updateFormConfig',
4592
+ error: configError,
4593
+ }),
4594
+ {
4595
+ url: formUrl,
4596
+ schemaSaved: true,
4597
+ configWarning: configErrorMsg,
4598
+ }
4211
4599
  )));
4212
4600
  }
4213
4601
  }
@@ -4227,7 +4615,10 @@ async function createFormForLegacyProcess(context, input) {
4227
4615
  };
4228
4616
 
4229
4617
  const { appType, formTitle, fieldsJsonOrFile, layout, theme, labelAlign } = parsedArgs;
4618
+ assertNoEmojiInDefinitionFileName(fieldsJsonOrFile);
4230
4619
  const { fields, validations } = readFieldsDefinition(fieldsJsonOrFile);
4620
+ assertNoEmojiInFormDefinition(formTitle, fields, validations, 'legacy create-form input');
4621
+ validateFormFieldDefinitions(fields);
4231
4622
  const fieldCount = countDataFieldDefinitions(fields);
4232
4623
  const createResult = await requestWithAutoLogin(function (auth) {
4233
4624
  return sendPostRequest(
@@ -5193,6 +5584,8 @@ module.exports = {
5193
5584
  buildFormNodeComponent,
5194
5585
  countDataFieldDefinitions,
5195
5586
  collectComponentNames,
5587
+ normalizeFormDefinitionType,
5588
+ validateFormFieldDefinitions,
5196
5589
  ensureDividerThemeAction,
5197
5590
  applyChangesToSchema,
5198
5591
  },