openyida 2026.9.13 → 2026.9.15
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/lib/app/canvas-compile.js +37 -4
- package/lib/app/create-form/batch.js +54 -6
- package/lib/app/create-form.js +89 -11
- package/lib/app/display-page-readback.js +63 -5
- package/lib/app/get-schema.js +158 -5
- package/lib/app/publish.js +127 -5
- package/lib/core/agent-capabilities.js +6 -7
- package/lib/core/command-manifest.js +2 -2
- package/lib/core/locales/en.js +9 -0
- package/lib/core/locales/zh.js +9 -0
- package/lib/core/utils.js +358 -0
- package/package.json +1 -1
- package/yida-skills/skills/yida-app/SKILL.md +11 -5
- package/yida-skills/skills/yida-app/workflow/plan/step-4-deliver.md +5 -3
- package/yida-skills/skills/yida-app/workflow/plan/workflow.md +4 -3
- package/yida-skills/skills/yida-app/workflow/step-2-design.md +2 -2
- package/yida-skills/skills/yida-app/workflow/step-4-forms-processes.md +5 -2
- package/yida-skills/skills/yida-app/workflow/step-9-output-finish.md +18 -14
- package/yida-skills/skills/yida-create-form-page/SKILL.md +18 -3
- package/yida-skills/skills/yida-create-form-page/references/batch-forms.md +4 -3
- package/yida-skills/skills/yida-design/references/ask-human-interaction-contract.md +22 -19
- package/yida-skills/skills/yida-design/references/design-mode.md +1 -1
- package/yida-skills/skills/yida-design/sub_skill/yida-design-plan/references/build-plan-schema.md +1 -1
- package/yida-skills/skills/yida-design/workflow/step-4-wireframe-interaction.md +1 -1
- package/yida-skills/skills/yida-integration/SKILL.md +1 -0
- package/yida-skills/skills/yida-nav-shell/SKILL.md +4 -4
- package/yida-skills/skills/yida-prd/workflow/output-prd.md +2 -2
- package/yida-skills/skills/yida-prd/workflow/step-2-information-architecture.md +2 -2
- package/yida-skills/skills/yida-requirement-analysis/SKILL.md +1 -1
- package/yida-skills/skills/yida-requirement-analysis/workflow/prepare-brief.md +17 -18
|
@@ -24,12 +24,13 @@
|
|
|
24
24
|
* 3) 第三方依赖以 `window.<别名>` 形式引用(antd→window.antd、react→window.React …),
|
|
25
25
|
* 这些 UMD 依赖由画布运行时依据 importedModules 白名单按需注入。
|
|
26
26
|
*
|
|
27
|
-
* 因此本地编译 = Babel 把 JSX/TS 转成
|
|
28
|
-
* 把 export default 改写成画布入口 `YidaComp` → 正则抽出依赖包名。
|
|
27
|
+
* 因此本地编译 = Babel 把 JSX/TS 转成 JS → 把 import 改写成 window 别名引用、
|
|
28
|
+
* 把 export default 改写成画布入口 `YidaComp` → UglifyJS 压缩 → 正则抽出依赖包名。
|
|
29
29
|
*/
|
|
30
30
|
|
|
31
31
|
const Babel = require('@babel/standalone');
|
|
32
32
|
const globals = require('globals');
|
|
33
|
+
const UglifyJS = require('uglify-js');
|
|
33
34
|
const {
|
|
34
35
|
assertNoEmojiInArtifactName,
|
|
35
36
|
assertNoEmojiInText,
|
|
@@ -877,6 +878,36 @@ function assertCanvasRuntimeParseable(runtimeCode, options = {}) {
|
|
|
877
878
|
}
|
|
878
879
|
}
|
|
879
880
|
|
|
881
|
+
/**
|
|
882
|
+
* 压缩画布运行态代码,并显式保留装配器读取的 YidaComp 入口名。
|
|
883
|
+
*/
|
|
884
|
+
function minifyCanvasRuntime(runtimeCode, options = {}) {
|
|
885
|
+
let result;
|
|
886
|
+
try {
|
|
887
|
+
result = UglifyJS.minify(runtimeCode, {
|
|
888
|
+
compress: true,
|
|
889
|
+
mangle: {
|
|
890
|
+
reserved: ['YidaComp'],
|
|
891
|
+
},
|
|
892
|
+
});
|
|
893
|
+
} catch (error) {
|
|
894
|
+
result = { error };
|
|
895
|
+
}
|
|
896
|
+
if (!result || result.error || typeof result.code !== 'string' || !result.code) {
|
|
897
|
+
const detail = result && result.error && result.error.message
|
|
898
|
+
? result.error.message
|
|
899
|
+
: '压缩结果为空';
|
|
900
|
+
throw new CliError(`Code Canvas runtimeCode 压缩失败: ${detail}`, {
|
|
901
|
+
code: 'OPENYIDA_CANVAS_MINIFY_FAILED',
|
|
902
|
+
details: {
|
|
903
|
+
stage: 'canvas_minify',
|
|
904
|
+
sourcePath: options.sourcePath || '',
|
|
905
|
+
},
|
|
906
|
+
});
|
|
907
|
+
}
|
|
908
|
+
return result.code;
|
|
909
|
+
}
|
|
910
|
+
|
|
880
911
|
/**
|
|
881
912
|
* 本地编译 Code Canvas 源码。
|
|
882
913
|
* @param {string} source 原始 React/JSX/TSX 源码
|
|
@@ -984,11 +1015,12 @@ function compileCanvasLocal(source, options = {}) {
|
|
|
984
1015
|
configFile: false,
|
|
985
1016
|
});
|
|
986
1017
|
|
|
987
|
-
const
|
|
988
|
-
assertNoEmojiInText(
|
|
1018
|
+
const unminifiedRuntimeCode = stage2.code || '';
|
|
1019
|
+
assertNoEmojiInText(unminifiedRuntimeCode, {
|
|
989
1020
|
artifact: options.sourcePath ? options.sourcePath + ' runtime' : 'Code Canvas runtime',
|
|
990
1021
|
code: 'OPENYIDA_CANVAS_SOURCE_EMOJI_FORBIDDEN',
|
|
991
1022
|
});
|
|
1023
|
+
const runtimeCode = minifyCanvasRuntime(unminifiedRuntimeCode, options);
|
|
992
1024
|
assertCanvasRuntimeParseable(runtimeCode, options);
|
|
993
1025
|
assertDependencyManifestConsistent(runtimeCode, importedModules, options);
|
|
994
1026
|
return {
|
|
@@ -1059,6 +1091,7 @@ module.exports = {
|
|
|
1059
1091
|
resolveWindowAlias,
|
|
1060
1092
|
shouldAllowUnsupportedBareImports,
|
|
1061
1093
|
assertCanvasRuntimeParseable,
|
|
1094
|
+
minifyCanvasRuntime,
|
|
1062
1095
|
findBareDependencyGlobalIssues,
|
|
1063
1096
|
findSelfReferentialDependencyBindingIssues,
|
|
1064
1097
|
findDependencyManifestIssues,
|
|
@@ -225,6 +225,25 @@ function readbackMismatch(formUuid, form, schema) {
|
|
|
225
225
|
});
|
|
226
226
|
}
|
|
227
227
|
|
|
228
|
+
function buildPartialFailureRecovery(results) {
|
|
229
|
+
const items = Object.values(results || {});
|
|
230
|
+
const hasUnknownWrite = items.some(item =>
|
|
231
|
+
item && (item.status === 'running' || (item.status === 'failed' && !item.formUuid))
|
|
232
|
+
);
|
|
233
|
+
const recoveryAction = hasUnknownWrite
|
|
234
|
+
? 'inspect_unknown_write_then_reconcile'
|
|
235
|
+
: 'rerun_unchanged_plan';
|
|
236
|
+
return { recoveryAction, nextAction: recoveryAction };
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
function buildDeliveryUrls(baseUrl, appType, formUuid) {
|
|
240
|
+
const normalizedBaseUrl = String(baseUrl || '').replace(/\/+$/, '');
|
|
241
|
+
if (!normalizedBaseUrl || !appType || !formUuid) { return {}; }
|
|
242
|
+
const appUrl = `${normalizedBaseUrl}/${appType}/workbench`;
|
|
243
|
+
const formUrl = `${appUrl}/${formUuid}`;
|
|
244
|
+
return { url: formUrl, formUrl, appUrl };
|
|
245
|
+
}
|
|
246
|
+
|
|
228
247
|
function execute(args, { execFile: execFileImpl = execFile } = {}) {
|
|
229
248
|
return new Promise((resolve, reject) => {
|
|
230
249
|
execFileImpl(process.execPath, [path.resolve(__dirname, '../../../bin/yida.js'), ...args, '--quiet'], {
|
|
@@ -317,13 +336,15 @@ async function run(args, dependencies = {}) {
|
|
|
317
336
|
try {
|
|
318
337
|
const state = fs.existsSync(stateFile) ? JSON.parse(fs.readFileSync(stateFile, 'utf8')) : { fingerprint, appType: options.appType, results: {} };
|
|
319
338
|
if (state.fingerprint !== fingerprint) { invalid('state belongs to a different plan; reconcile existing resources before preparing a new batch'); }
|
|
320
|
-
await call(['login', '--check-only', '--json']);
|
|
339
|
+
const loginStatus = await call(['login', '--check-only', '--json']);
|
|
340
|
+
const baseUrl = loginStatus?.base_url || loginStatus?.baseUrl || '';
|
|
321
341
|
// Read back completed resources before their IDs can be used by dependent forms.
|
|
322
342
|
for (const form of forms.filter(item => state.results[item.key]?.status === 'success')) {
|
|
323
343
|
const item = state.results[form.key];
|
|
324
344
|
const schema = await call(['get-schema', options.appType, item.formUuid, '--field-map-json']);
|
|
325
345
|
if (schema.formUuid !== item.formUuid || !Array.isArray(schema.fields)) { invalid(`schema: ${form.key}`); }
|
|
326
346
|
item.fields = schema.fields;
|
|
347
|
+
Object.assign(item, buildDeliveryUrls(baseUrl, options.appType, item.formUuid));
|
|
327
348
|
}
|
|
328
349
|
const resolve = (key, field) => {
|
|
329
350
|
const item = state.results[key];
|
|
@@ -336,6 +357,7 @@ async function run(args, dependencies = {}) {
|
|
|
336
357
|
await schedule(forms, options.concurrency, state.results, async form => {
|
|
337
358
|
let formUuid = form.formUuid || state.results[form.key]?.formUuid;
|
|
338
359
|
let shouldResume = Boolean(!form.formUuid && formUuid);
|
|
360
|
+
let delivery = buildDeliveryUrls(baseUrl, options.appType, formUuid);
|
|
339
361
|
const resolvedFields = mapReferences(form.fields, resolve);
|
|
340
362
|
if (!formUuid) {
|
|
341
363
|
const argv = ['create-form', 'create', options.appType, form.title, JSON.stringify(resolvedFields), '--no-open'];
|
|
@@ -344,6 +366,12 @@ async function run(args, dependencies = {}) {
|
|
|
344
366
|
const created = await call(argv);
|
|
345
367
|
if (typeof created.formUuid !== 'string' || !created.formUuid.startsWith('FORM')) { invalid(`create result: ${form.key}`); }
|
|
346
368
|
formUuid = created.formUuid;
|
|
369
|
+
delivery = {
|
|
370
|
+
...buildDeliveryUrls(baseUrl, options.appType, formUuid),
|
|
371
|
+
...(created.url ? { url: created.url } : {}),
|
|
372
|
+
...(created.formUrl ? { formUrl: created.formUrl } : {}),
|
|
373
|
+
...(created.appUrl ? { appUrl: created.appUrl } : {}),
|
|
374
|
+
};
|
|
347
375
|
} catch (error) {
|
|
348
376
|
const createdFormUuid = error.output?.formUuid || error.output?.details?.formUuid;
|
|
349
377
|
if (typeof createdFormUuid !== 'string' || !createdFormUuid.startsWith('FORM')) {
|
|
@@ -357,25 +385,43 @@ async function run(args, dependencies = {}) {
|
|
|
357
385
|
save(state);
|
|
358
386
|
let resumed = false;
|
|
359
387
|
if (shouldResume) {
|
|
360
|
-
await call(['create-form', 'resume', options.appType, formUuid, JSON.stringify(resolvedFields), '--json']);
|
|
388
|
+
const resumeOutput = await call(['create-form', 'resume', options.appType, formUuid, JSON.stringify(resolvedFields), '--json']);
|
|
389
|
+
delivery = {
|
|
390
|
+
...buildDeliveryUrls(baseUrl, options.appType, formUuid),
|
|
391
|
+
...(resumeOutput.url ? { url: resumeOutput.url } : {}),
|
|
392
|
+
...(resumeOutput.formUrl ? { formUrl: resumeOutput.formUrl } : {}),
|
|
393
|
+
...(resumeOutput.appUrl ? { appUrl: resumeOutput.appUrl } : {}),
|
|
394
|
+
};
|
|
361
395
|
resumed = true;
|
|
362
396
|
}
|
|
363
397
|
let schema = await call(['get-schema', options.appType, formUuid, '--field-map-json']);
|
|
364
398
|
if (!readbackMatchesExpectedFields(schema, resolvedFields) && !resumed && !form.formUuid) {
|
|
365
|
-
await call(['create-form', 'resume', options.appType, formUuid, JSON.stringify(resolvedFields), '--json']);
|
|
399
|
+
const resumeOutput = await call(['create-form', 'resume', options.appType, formUuid, JSON.stringify(resolvedFields), '--json']);
|
|
400
|
+
delivery = {
|
|
401
|
+
...buildDeliveryUrls(baseUrl, options.appType, formUuid),
|
|
402
|
+
...(resumeOutput.url ? { url: resumeOutput.url } : {}),
|
|
403
|
+
...(resumeOutput.formUrl ? { formUrl: resumeOutput.formUrl } : {}),
|
|
404
|
+
...(resumeOutput.appUrl ? { appUrl: resumeOutput.appUrl } : {}),
|
|
405
|
+
};
|
|
366
406
|
resumed = true;
|
|
367
407
|
schema = await call(['get-schema', options.appType, formUuid, '--field-map-json']);
|
|
368
408
|
}
|
|
369
409
|
if (schema.formUuid !== formUuid || !readbackMatchesExpectedFields(schema, resolvedFields)) {
|
|
370
410
|
throw readbackMismatch(formUuid, { ...form, fields: resolvedFields }, schema);
|
|
371
411
|
}
|
|
372
|
-
return { formUuid, fields: schema.fields };
|
|
412
|
+
return { formUuid, fields: schema.fields, ...delivery };
|
|
373
413
|
}, () => save(state));
|
|
374
414
|
const success = Object.values(state.results).every(item => item.status === 'success');
|
|
375
|
-
const output = {
|
|
415
|
+
const output = {
|
|
416
|
+
success,
|
|
417
|
+
groups,
|
|
418
|
+
stateFile,
|
|
419
|
+
results: state.results,
|
|
420
|
+
...((baseUrl && options.appType) ? { appUrl: `${String(baseUrl).replace(/\/+$/, '')}/${options.appType}/workbench` } : {}),
|
|
421
|
+
};
|
|
376
422
|
if (!success) {
|
|
377
423
|
output.errorCode = 'FORM_BATCH_PARTIAL_FAILURE';
|
|
378
|
-
output
|
|
424
|
+
Object.assign(output, buildPartialFailureRecovery(state.results));
|
|
379
425
|
}
|
|
380
426
|
console.log(JSON.stringify(output));
|
|
381
427
|
if (!output.success) { process.exitCode = 1; }
|
|
@@ -397,5 +443,7 @@ module.exports = {
|
|
|
397
443
|
execute,
|
|
398
444
|
expectedReadbackFields,
|
|
399
445
|
readbackMatchesExpectedFields,
|
|
446
|
+
buildPartialFailureRecovery,
|
|
447
|
+
buildDeliveryUrls,
|
|
400
448
|
validateStaticDefinition,
|
|
401
449
|
};
|
package/lib/app/create-form.js
CHANGED
|
@@ -4858,7 +4858,10 @@ async function saveFormSchema(authRef, appType, formUuid, schema, version, stepO
|
|
|
4858
4858
|
formUuid,
|
|
4859
4859
|
result: sanitizeFailureResult(saveResult),
|
|
4860
4860
|
});
|
|
4861
|
-
if (failureContext) {
|
|
4861
|
+
if (failureContext && failureContext.deferFailureOutput) {
|
|
4862
|
+
// Resume may safely resolve an HTTP 5xx by exact readback. Defer the
|
|
4863
|
+
// failure payload so a recovered command emits one authoritative result.
|
|
4864
|
+
} else if (failureContext) {
|
|
4862
4865
|
emitCreateFormPostCreateFailure(Object.assign({}, failureContext, {
|
|
4863
4866
|
stage: 'saveFormSchema',
|
|
4864
4867
|
error: saveError,
|
|
@@ -4879,6 +4882,13 @@ async function saveFormSchema(authRef, appType, formUuid, schema, version, stepO
|
|
|
4879
4882
|
|
|
4880
4883
|
// ── create 模式主流程 ─────────────────────────────────
|
|
4881
4884
|
|
|
4885
|
+
function buildFormDeliveryUrls(baseUrl, appType, formUuid) {
|
|
4886
|
+
return {
|
|
4887
|
+
appUrl: baseUrl + '/' + appType + '/workbench',
|
|
4888
|
+
formUrl: baseUrl + '/' + appType + '/workbench/' + formUuid,
|
|
4889
|
+
};
|
|
4890
|
+
}
|
|
4891
|
+
|
|
4882
4892
|
async function mainValidateFields(parsedArgs) {
|
|
4883
4893
|
const { fieldsJsonOrFile } = parsedArgs;
|
|
4884
4894
|
assertNoEmojiInDefinitionFileName(fieldsJsonOrFile);
|
|
@@ -5034,13 +5044,13 @@ async function mainCreate(parsedArgs, authRef) {
|
|
|
5034
5044
|
}
|
|
5035
5045
|
|
|
5036
5046
|
// 输出结果
|
|
5037
|
-
const formUrl = authRef.baseUrl
|
|
5047
|
+
const { appUrl, formUrl } = buildFormDeliveryUrls(authRef.baseUrl, appType, formUuid);
|
|
5038
5048
|
result(true, t('create_form.create_success'), [
|
|
5039
5049
|
['Form UUID', formUuid],
|
|
5040
5050
|
['URL', formUrl],
|
|
5041
5051
|
]);
|
|
5042
5052
|
console.log(JSON.stringify(withBrowserHandoff(
|
|
5043
|
-
{ success: true, formUuid, formTitle, appType, fieldCount, icon: formIcon, iconSource: iconResolution.source, url: formUrl },
|
|
5053
|
+
{ success: true, formUuid, formTitle, appType, fieldCount, icon: formIcon, iconSource: iconResolution.source, url: formUrl, formUrl, appUrl },
|
|
5044
5054
|
formUrl,
|
|
5045
5055
|
{ stage: 'create_form_success', title: formTitle },
|
|
5046
5056
|
parsedArgs.browserOpenMode
|
|
@@ -5127,6 +5137,7 @@ async function createFormForLegacyProcessQuiet(context, input) {
|
|
|
5127
5137
|
}
|
|
5128
5138
|
const saveResult = await saveFormSchema(authRef, appType, formUuid, schema, serverRevision, 4);
|
|
5129
5139
|
const navIconResult = await updateCreatedFormNavigationIcon(authRef, appType, formUuid, formIcon);
|
|
5140
|
+
const { appUrl, formUrl } = buildFormDeliveryUrls(authRef.baseUrl, appType, formUuid);
|
|
5130
5141
|
return {
|
|
5131
5142
|
success: true,
|
|
5132
5143
|
appType,
|
|
@@ -5138,7 +5149,9 @@ async function createFormForLegacyProcessQuiet(context, input) {
|
|
|
5138
5149
|
navIconResult,
|
|
5139
5150
|
icon: formIcon,
|
|
5140
5151
|
iconSource: iconResolution.source,
|
|
5141
|
-
url:
|
|
5152
|
+
url: formUrl,
|
|
5153
|
+
formUrl,
|
|
5154
|
+
appUrl,
|
|
5142
5155
|
};
|
|
5143
5156
|
}
|
|
5144
5157
|
|
|
@@ -5319,7 +5332,7 @@ async function mainAddOption(parsedArgs, authRef) {
|
|
|
5319
5332
|
// Step 4: 保存 Schema
|
|
5320
5333
|
await saveFormSchema(authRef, appType, formUuid, schema, version, 4);
|
|
5321
5334
|
|
|
5322
|
-
const formUrl = authRef.baseUrl
|
|
5335
|
+
const { appUrl, formUrl } = buildFormDeliveryUrls(authRef.baseUrl, appType, formUuid);
|
|
5323
5336
|
result(true, '选项追加成功', [
|
|
5324
5337
|
['Form UUID', formUuid],
|
|
5325
5338
|
['Field', fieldLabel],
|
|
@@ -5339,6 +5352,8 @@ async function mainAddOption(parsedArgs, authRef) {
|
|
|
5339
5352
|
skipped: skippedOptions,
|
|
5340
5353
|
totalOptions: existingDataSource.length,
|
|
5341
5354
|
url: formUrl,
|
|
5355
|
+
formUrl,
|
|
5356
|
+
appUrl,
|
|
5342
5357
|
}));
|
|
5343
5358
|
}
|
|
5344
5359
|
|
|
@@ -5438,7 +5453,7 @@ async function mainBindDataSource(parsedArgs, authRef) {
|
|
|
5438
5453
|
|
|
5439
5454
|
await saveFormSchema(authRef, appType, formUuid, schema, version, 4);
|
|
5440
5455
|
|
|
5441
|
-
const formUrl = authRef.baseUrl
|
|
5456
|
+
const { appUrl, formUrl } = buildFormDeliveryUrls(authRef.baseUrl, appType, formUuid);
|
|
5442
5457
|
result(true, '字段数据源保存成功', [
|
|
5443
5458
|
['Form UUID', formUuid],
|
|
5444
5459
|
['Field', fieldLabel],
|
|
@@ -5459,6 +5474,8 @@ async function mainBindDataSource(parsedArgs, authRef) {
|
|
|
5459
5474
|
options: normalized.options.length,
|
|
5460
5475
|
filterLocal: targetComponent.props.filterLocal,
|
|
5461
5476
|
pageUrl: formUrl,
|
|
5477
|
+
formUrl,
|
|
5478
|
+
appUrl,
|
|
5462
5479
|
},
|
|
5463
5480
|
formUrl,
|
|
5464
5481
|
{ stage: 'bind_datasource_success', title: formUuid },
|
|
@@ -6109,6 +6126,59 @@ async function readResumeSchema(authRef, appType, formUuid) {
|
|
|
6109
6126
|
return { schemaResult, schema, version: extractSchemaServerRevision(schemaResult) };
|
|
6110
6127
|
}
|
|
6111
6128
|
|
|
6129
|
+
function isRetryableResumeSaveServerFailure(errorObject) {
|
|
6130
|
+
const status = Number(errorObject?.details?.result?.__httpStatus);
|
|
6131
|
+
return errorObject?.code === 'CREATE_FORM_SAVE_SCHEMA_FAILED' &&
|
|
6132
|
+
Number.isInteger(status) && status >= 500 && status <= 599;
|
|
6133
|
+
}
|
|
6134
|
+
|
|
6135
|
+
async function saveResumeSchemaWithSafeRetry(authRef, appType, formUuid, schema, version, fields) {
|
|
6136
|
+
const save = (value, revision) => saveFormSchema(
|
|
6137
|
+
authRef, appType, formUuid, value, revision, 4, { deferFailureOutput: true }
|
|
6138
|
+
);
|
|
6139
|
+
try {
|
|
6140
|
+
await save(schema, version);
|
|
6141
|
+
return '';
|
|
6142
|
+
} catch (originalError) {
|
|
6143
|
+
if (!isRetryableResumeSaveServerFailure(originalError)) { throw originalError; }
|
|
6144
|
+
|
|
6145
|
+
let readback;
|
|
6146
|
+
try { readback = await readResumeSchema(authRef, appType, formUuid); } catch (_) { throw originalError; }
|
|
6147
|
+
const root = readback.schema.pages[0].componentsTree && readback.schema.pages[0].componentsTree[0];
|
|
6148
|
+
const container = root ? findFormContainer(root) : null;
|
|
6149
|
+
if (!container) { throw originalError; }
|
|
6150
|
+
const evidence = collectResumeFieldEvidence(container.children);
|
|
6151
|
+
const verified = buildResumeChanges(
|
|
6152
|
+
evidence,
|
|
6153
|
+
collectResumeDesiredFields(fields, { verifyNestedFields: true })
|
|
6154
|
+
);
|
|
6155
|
+
if (verified.conflicts.length > 0) { throw originalError; }
|
|
6156
|
+
if (verified.missing.length === 0) { return 'save_schema_recovered_by_readback'; }
|
|
6157
|
+
const retryPlan = buildResumeChanges(evidence, collectResumeDesiredFields(fields));
|
|
6158
|
+
if (retryPlan.conflicts.length > 0 || retryPlan.missing.length === 0) { throw originalError; }
|
|
6159
|
+
const applied = applyChangesToSchema(
|
|
6160
|
+
readback.schema,
|
|
6161
|
+
retryPlan.missing.map(function (field) { return { action: 'add', field }; }),
|
|
6162
|
+
{ verbose: true }
|
|
6163
|
+
);
|
|
6164
|
+
if ((applied.diagnostics || []).length > 0) { throw originalError; }
|
|
6165
|
+
const updatedContainer = findFormContainer(readback.schema.pages[0].componentsTree[0]);
|
|
6166
|
+
fillSerialNumberFormulas(updatedContainer.children, resolveCorpId(authRef.authData), appType, formUuid);
|
|
6167
|
+
|
|
6168
|
+
try {
|
|
6169
|
+
await save(readback.schema, readback.version);
|
|
6170
|
+
} catch (retryError) {
|
|
6171
|
+
originalError.details = Object.assign({}, originalError.details, {
|
|
6172
|
+
safeRetryAttempted: true,
|
|
6173
|
+
retryErrorCode: retryError?.code || 'CREATE_FORM_SAVE_SCHEMA_FAILED',
|
|
6174
|
+
retryResult: sanitizeFailureResult(retryError?.details?.result),
|
|
6175
|
+
});
|
|
6176
|
+
throw originalError;
|
|
6177
|
+
}
|
|
6178
|
+
return 'save_schema_retried';
|
|
6179
|
+
}
|
|
6180
|
+
}
|
|
6181
|
+
|
|
6112
6182
|
async function mainResume(parsedArgs, authRef) {
|
|
6113
6183
|
const { appType, formUuid, fieldsJsonOrFile } = parsedArgs;
|
|
6114
6184
|
assertNoEmojiInDefinitionFileName(fieldsJsonOrFile);
|
|
@@ -6139,8 +6209,11 @@ async function mainResume(parsedArgs, authRef) {
|
|
|
6139
6209
|
if (resumeValidationRules.length > 0) {
|
|
6140
6210
|
applySmartValidations(firstRead.schema, resumeValidationRules);
|
|
6141
6211
|
}
|
|
6142
|
-
|
|
6212
|
+
const saveRecoveryStage = await saveResumeSchemaWithSafeRetry(
|
|
6213
|
+
authRef, appType, formUuid, firstRead.schema, firstRead.version, fields
|
|
6214
|
+
);
|
|
6143
6215
|
completedStages.push('rebuild_blank_schema', 'save_schema');
|
|
6216
|
+
if (saveRecoveryStage) { completedStages.push(saveRecoveryStage); }
|
|
6144
6217
|
recoveredBlankShell = true;
|
|
6145
6218
|
plan = {
|
|
6146
6219
|
existing: [],
|
|
@@ -6189,8 +6262,11 @@ async function mainResume(parsedArgs, authRef) {
|
|
|
6189
6262
|
const corpId = resolveCorpId(authRef.authData);
|
|
6190
6263
|
const updatedContainer = findFormContainer(firstRead.schema.pages[0].componentsTree[0]);
|
|
6191
6264
|
fillSerialNumberFormulas(updatedContainer.children, corpId, appType, formUuid);
|
|
6192
|
-
|
|
6265
|
+
const saveRecoveryStage = await saveResumeSchemaWithSafeRetry(
|
|
6266
|
+
authRef, appType, formUuid, firstRead.schema, firstRead.version, fields
|
|
6267
|
+
);
|
|
6193
6268
|
completedStages.push('add_missing_fields', 'save_schema');
|
|
6269
|
+
if (saveRecoveryStage) { completedStages.push(saveRecoveryStage); }
|
|
6194
6270
|
}
|
|
6195
6271
|
|
|
6196
6272
|
const finalRead = await readResumeSchema(authRef, appType, formUuid);
|
|
@@ -6211,7 +6287,7 @@ async function mainResume(parsedArgs, authRef) {
|
|
|
6211
6287
|
});
|
|
6212
6288
|
}
|
|
6213
6289
|
completedStages.push('verify_final_schema');
|
|
6214
|
-
const formUrl = authRef.baseUrl
|
|
6290
|
+
const { appUrl, formUrl } = buildFormDeliveryUrls(authRef.baseUrl, appType, formUuid);
|
|
6215
6291
|
const output = {
|
|
6216
6292
|
success: true,
|
|
6217
6293
|
appType,
|
|
@@ -6223,6 +6299,8 @@ async function mainResume(parsedArgs, authRef) {
|
|
|
6223
6299
|
finalFieldCount: collectResumeFieldEvidence(finalContainer.children).length,
|
|
6224
6300
|
recoveredBlankShell,
|
|
6225
6301
|
url: formUrl,
|
|
6302
|
+
formUrl,
|
|
6303
|
+
appUrl,
|
|
6226
6304
|
};
|
|
6227
6305
|
console.log(JSON.stringify(withBrowserHandoff(
|
|
6228
6306
|
output,
|
|
@@ -6384,14 +6462,14 @@ async function mainUpdate(parsedArgs, authRef) {
|
|
|
6384
6462
|
await saveFormSchema(authRef, appType, formUuid, schema, version, 6);
|
|
6385
6463
|
|
|
6386
6464
|
// 输出结果
|
|
6387
|
-
const formUrl = authRef.baseUrl
|
|
6465
|
+
const { appUrl, formUrl } = buildFormDeliveryUrls(authRef.baseUrl, appType, formUuid);
|
|
6388
6466
|
result(true, t('create_form.update_success'), [
|
|
6389
6467
|
['Form UUID', formUuid],
|
|
6390
6468
|
['URL', formUrl],
|
|
6391
6469
|
['Changes', String(appliedChanges.length)],
|
|
6392
6470
|
]);
|
|
6393
6471
|
console.log(JSON.stringify(withBrowserHandoff(
|
|
6394
|
-
{ success: true, formUuid, appType, changesApplied: appliedChanges.length, changes: appliedChanges, url: formUrl },
|
|
6472
|
+
{ success: true, formUuid, appType, changesApplied: appliedChanges.length, changes: appliedChanges, url: formUrl, formUrl, appUrl },
|
|
6395
6473
|
formUrl,
|
|
6396
6474
|
{ stage: 'update_form_success', title: formUuid },
|
|
6397
6475
|
parsedArgs.browserOpenMode
|
|
@@ -23,10 +23,30 @@ function fingerprint(value) {
|
|
|
23
23
|
return crypto.createHash('sha256').update(normalized).digest('hex');
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
+
function rawFingerprint(value) {
|
|
27
|
+
if (typeof value !== 'string' || !value) {
|
|
28
|
+
return '';
|
|
29
|
+
}
|
|
30
|
+
return crypto.createHash('sha256').update(value, 'utf8').digest('hex');
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function positiveSize(value) {
|
|
34
|
+
const size = Number(value);
|
|
35
|
+
return Number.isFinite(size) && size > 0 ? size : 0;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function sha256(value) {
|
|
39
|
+
const digest = String(value || '').toLowerCase();
|
|
40
|
+
return /^[a-f0-9]{64}$/.test(digest) ? digest : '';
|
|
41
|
+
}
|
|
42
|
+
|
|
26
43
|
function createEmptyDisplayInfo() {
|
|
27
44
|
return {
|
|
28
45
|
hasYidaCodeCanvas: false,
|
|
46
|
+
hasCodeBundle: false,
|
|
29
47
|
hasNativeJsx: false,
|
|
48
|
+
codeBundleCount: 0,
|
|
49
|
+
bundleIds: [],
|
|
30
50
|
runtimeCodeBytes: 0,
|
|
31
51
|
sourceCodeBytes: 0,
|
|
32
52
|
compiledCodeBytes: 0,
|
|
@@ -34,6 +54,8 @@ function createEmptyDisplayInfo() {
|
|
|
34
54
|
componentCount: 0,
|
|
35
55
|
canvasRuntimeCode: '',
|
|
36
56
|
canvasSourceCode: '',
|
|
57
|
+
canvasRuntimeSha256: '',
|
|
58
|
+
canvasSourceSha256: '',
|
|
37
59
|
nativeCompiledCode: '',
|
|
38
60
|
nativeSourceCode: '',
|
|
39
61
|
};
|
|
@@ -79,12 +101,37 @@ function traverseDisplayNodes(node, info) {
|
|
|
79
101
|
|
|
80
102
|
if (node.componentName === 'YidaCodeCanvas') {
|
|
81
103
|
const props = node.props || {};
|
|
104
|
+
const codeBundle = props.codeBundle && typeof props.codeBundle === 'object'
|
|
105
|
+
? props.codeBundle
|
|
106
|
+
: null;
|
|
82
107
|
info.hasYidaCodeCanvas = true;
|
|
83
108
|
info.componentCount++;
|
|
84
|
-
|
|
85
|
-
|
|
109
|
+
if (typeof props.runtimeCode === 'string' && props.runtimeCode) {
|
|
110
|
+
info.canvasRuntimeCode = props.runtimeCode;
|
|
111
|
+
}
|
|
112
|
+
if (typeof props.code === 'string' && props.code) {
|
|
113
|
+
info.canvasSourceCode = props.code;
|
|
114
|
+
}
|
|
86
115
|
info.runtimeCodeBytes += codeBytes(props.runtimeCode);
|
|
87
116
|
info.sourceCodeBytes += codeBytes(props.code);
|
|
117
|
+
if (codeBundle) {
|
|
118
|
+
const bundleId = String(codeBundle.bundleId || '');
|
|
119
|
+
const runtime = codeBundle.runtime && typeof codeBundle.runtime === 'object'
|
|
120
|
+
? codeBundle.runtime
|
|
121
|
+
: {};
|
|
122
|
+
const source = codeBundle.source && typeof codeBundle.source === 'object'
|
|
123
|
+
? codeBundle.source
|
|
124
|
+
: {};
|
|
125
|
+
info.hasCodeBundle = true;
|
|
126
|
+
info.codeBundleCount++;
|
|
127
|
+
if (bundleId && !info.bundleIds.includes(bundleId)) {
|
|
128
|
+
info.bundleIds.push(bundleId);
|
|
129
|
+
}
|
|
130
|
+
info.runtimeCodeBytes += positiveSize(runtime.size);
|
|
131
|
+
info.sourceCodeBytes += positiveSize(source.size);
|
|
132
|
+
info.canvasRuntimeSha256 = sha256(runtime.sha256) || info.canvasRuntimeSha256;
|
|
133
|
+
info.canvasSourceSha256 = sha256(source.sha256) || info.canvasSourceSha256;
|
|
134
|
+
}
|
|
88
135
|
addImportedModules(info.importedModules, props.importedModules);
|
|
89
136
|
} else if (node.componentName === 'Jsx') {
|
|
90
137
|
info.hasNativeJsx = true;
|
|
@@ -133,7 +180,9 @@ function hasExpectedDisplayComponent(info, publishMode) {
|
|
|
133
180
|
return false;
|
|
134
181
|
}
|
|
135
182
|
if (publishMode === 'canvas') {
|
|
136
|
-
return info.hasYidaCodeCanvas &&
|
|
183
|
+
return info.hasYidaCodeCanvas && (
|
|
184
|
+
info.runtimeCodeBytes > 0 || !!info.canvasRuntimeSha256
|
|
185
|
+
);
|
|
137
186
|
}
|
|
138
187
|
return info.hasNativeJsx && info.compiledCodeBytes > 0;
|
|
139
188
|
}
|
|
@@ -144,7 +193,10 @@ function summarizeDisplayPublishInfo(info) {
|
|
|
144
193
|
}
|
|
145
194
|
return {
|
|
146
195
|
hasYidaCodeCanvas: info.hasYidaCodeCanvas,
|
|
196
|
+
hasCodeBundle: info.hasCodeBundle,
|
|
147
197
|
hasNativeJsx: info.hasNativeJsx,
|
|
198
|
+
codeBundleCount: info.codeBundleCount,
|
|
199
|
+
bundleIds: info.bundleIds.slice(),
|
|
148
200
|
runtimeCodeBytes: info.runtimeCodeBytes,
|
|
149
201
|
sourceCodeBytes: info.sourceCodeBytes,
|
|
150
202
|
compiledCodeBytes: info.compiledCodeBytes,
|
|
@@ -159,8 +211,13 @@ function verifyPublishedContentMatch(readbackSchema, expectedSchemaContent, publ
|
|
|
159
211
|
const displayComponentPresent = hasExpectedDisplayComponent(readbackInfo, publishMode);
|
|
160
212
|
const readbackArtifact = getPublishArtifact(readbackInfo, publishMode);
|
|
161
213
|
const expectedArtifact = getPublishArtifact(expectedInfo, publishMode);
|
|
162
|
-
const
|
|
163
|
-
const
|
|
214
|
+
const codeBundleReadback = publishMode === 'canvas' && readbackInfo && readbackInfo.hasCodeBundle;
|
|
215
|
+
const readbackFingerprint = codeBundleReadback
|
|
216
|
+
? readbackInfo.canvasRuntimeSha256
|
|
217
|
+
: fingerprint(readbackArtifact);
|
|
218
|
+
const expectedFingerprint = codeBundleReadback
|
|
219
|
+
? rawFingerprint(expectedArtifact)
|
|
220
|
+
: fingerprint(expectedArtifact);
|
|
164
221
|
|
|
165
222
|
return {
|
|
166
223
|
readbackInfo,
|
|
@@ -180,6 +237,7 @@ function verifyPublishedContentMatch(readbackSchema, expectedSchemaContent, publ
|
|
|
180
237
|
module.exports = {
|
|
181
238
|
extractDisplayPublishInfo,
|
|
182
239
|
fingerprint,
|
|
240
|
+
rawFingerprint,
|
|
183
241
|
hasExpectedDisplayComponent,
|
|
184
242
|
parseSchemaContent,
|
|
185
243
|
summarizeDisplayPublishInfo,
|