openyida 2026.7.23-2 → 2026.7.25
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.
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
* 这些 UMD 依赖由画布运行时依据 importedModules 白名单按需注入。
|
|
26
26
|
*
|
|
27
27
|
* 因此本地编译 = Babel 把 JSX/TS 转成 ES5 → 把 import 改写成 window 别名引用、
|
|
28
|
-
* 把 export default
|
|
28
|
+
* 把 export default 改写成画布入口 `YidaComp` → 正则抽出依赖包名。
|
|
29
29
|
*/
|
|
30
30
|
|
|
31
31
|
const Babel = require('@babel/standalone');
|
|
@@ -152,6 +152,15 @@ function esmToWindowPlugin({ types: t }, options = {}) {
|
|
|
152
152
|
);
|
|
153
153
|
}
|
|
154
154
|
|
|
155
|
+
function hasProgramBinding(path, name) {
|
|
156
|
+
const program = path.findParent((parentPath) => parentPath.isProgram());
|
|
157
|
+
return Boolean(program && program.scope.hasBinding(name));
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function isExistingYidaCompDefault(path, decl) {
|
|
161
|
+
return t.isIdentifier(decl, { name: 'YidaComp' }) && hasProgramBinding(path, 'YidaComp');
|
|
162
|
+
}
|
|
163
|
+
|
|
155
164
|
return {
|
|
156
165
|
name: 'yida-esm-to-window',
|
|
157
166
|
visitor: {
|
|
@@ -217,6 +226,10 @@ function esmToWindowPlugin({ types: t }, options = {}) {
|
|
|
217
226
|
const decl = path.node.declaration;
|
|
218
227
|
if (t.isFunctionDeclaration(decl) || t.isClassDeclaration(decl)) {
|
|
219
228
|
if (decl.id) {
|
|
229
|
+
if (decl.id.name === 'YidaComp') {
|
|
230
|
+
path.replaceWith(decl);
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
220
233
|
// 具名声明:保留声明本体,再补 `var YidaComp = <name>;`
|
|
221
234
|
const idName = decl.id.name;
|
|
222
235
|
path.replaceWithMultiple([
|
|
@@ -235,6 +248,10 @@ function esmToWindowPlugin({ types: t }, options = {}) {
|
|
|
235
248
|
);
|
|
236
249
|
return;
|
|
237
250
|
}
|
|
251
|
+
if (isExistingYidaCompDefault(path, decl)) {
|
|
252
|
+
path.remove();
|
|
253
|
+
return;
|
|
254
|
+
}
|
|
238
255
|
// export default <表达式>
|
|
239
256
|
path.replaceWith(
|
|
240
257
|
t.variableDeclaration('var', [t.variableDeclarator(t.identifier('YidaComp'), decl)])
|
|
@@ -258,6 +275,32 @@ function esmToWindowPlugin({ types: t }, options = {}) {
|
|
|
258
275
|
};
|
|
259
276
|
}
|
|
260
277
|
|
|
278
|
+
function buildCanvasRuntimeWrapper(runtimeCode) {
|
|
279
|
+
return (
|
|
280
|
+
'return function(iframeWindow, parentWindow){ const window = iframeWindow; '
|
|
281
|
+
+ runtimeCode
|
|
282
|
+
+ ' return YidaComp; }'
|
|
283
|
+
);
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
function assertCanvasRuntimeParseable(runtimeCode, options = {}) {
|
|
287
|
+
try {
|
|
288
|
+
// 只编译 wrapper,不执行 factory 或用户组件,避免触发源码里的运行期副作用。
|
|
289
|
+
// eslint-disable-next-line no-new-func
|
|
290
|
+
new Function(buildCanvasRuntimeWrapper(runtimeCode));
|
|
291
|
+
} catch (error) {
|
|
292
|
+
const detail = error && error.message ? error.message : String(error);
|
|
293
|
+
const sourceLabel = options.sourcePath ? ` (${options.sourcePath})` : '';
|
|
294
|
+
const compileError = new Error(
|
|
295
|
+
`Code Canvas runtimeCode 无法通过画布运行时装配校验${sourceLabel}: ${detail}`
|
|
296
|
+
+ '。请检查默认导出、YidaComp 入口或 window 等运行时保留变量是否重复声明。'
|
|
297
|
+
);
|
|
298
|
+
compileError.code = 'OPENYIDA_CANVAS_RUNTIME_PARSE_FAILED';
|
|
299
|
+
compileError.cause = error;
|
|
300
|
+
throw compileError;
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
261
304
|
/**
|
|
262
305
|
* 本地编译 Code Canvas 源码。
|
|
263
306
|
* @param {string} source 原始 React/JSX/TSX 源码
|
|
@@ -322,6 +365,7 @@ function compileCanvasLocal(source, options = {}) {
|
|
|
322
365
|
artifact: options.sourcePath ? options.sourcePath + ' runtime' : 'Code Canvas runtime',
|
|
323
366
|
code: 'OPENYIDA_CANVAS_SOURCE_EMOJI_FORBIDDEN',
|
|
324
367
|
});
|
|
368
|
+
assertCanvasRuntimeParseable(runtimeCode, options);
|
|
325
369
|
return {
|
|
326
370
|
runtimeCode,
|
|
327
371
|
importedModules: JSON.stringify(importedModules),
|
|
@@ -356,5 +400,6 @@ module.exports = {
|
|
|
356
400
|
extractImportedModules,
|
|
357
401
|
resolveWindowAlias,
|
|
358
402
|
shouldAllowUnsupportedBareImports,
|
|
403
|
+
assertCanvasRuntimeParseable,
|
|
359
404
|
MODULE_ALIAS_MAP,
|
|
360
405
|
};
|
package/lib/app/create-form.js
CHANGED
|
@@ -4334,6 +4334,109 @@ function sanitizeFailureResult(result) {
|
|
|
4334
4334
|
return sanitized;
|
|
4335
4335
|
}
|
|
4336
4336
|
|
|
4337
|
+
function getUpdateFormConfigRetryDelays() {
|
|
4338
|
+
const rawDelays = process.env.OPENYIDA_UPDATE_FORM_CONFIG_RETRY_DELAYS_MS;
|
|
4339
|
+
if (rawDelays === undefined) {
|
|
4340
|
+
return [500, 1000];
|
|
4341
|
+
}
|
|
4342
|
+
return String(rawDelays)
|
|
4343
|
+
.split(',')
|
|
4344
|
+
.map(function (part) { return Number(part.trim()); })
|
|
4345
|
+
.filter(function (delay) { return Number.isFinite(delay) && delay >= 0; });
|
|
4346
|
+
}
|
|
4347
|
+
|
|
4348
|
+
function waitForUpdateFormConfigRetry(delayMs) {
|
|
4349
|
+
if (!delayMs) {
|
|
4350
|
+
return Promise.resolve();
|
|
4351
|
+
}
|
|
4352
|
+
return new Promise(function (resolve) {
|
|
4353
|
+
setTimeout(resolve, delayMs);
|
|
4354
|
+
});
|
|
4355
|
+
}
|
|
4356
|
+
|
|
4357
|
+
function getUpdateFormConfigErrorText(result) {
|
|
4358
|
+
if (!result || typeof result !== 'object') {
|
|
4359
|
+
return '';
|
|
4360
|
+
}
|
|
4361
|
+
return [
|
|
4362
|
+
result.errorMsg,
|
|
4363
|
+
result.message,
|
|
4364
|
+
result.error,
|
|
4365
|
+
result.errorCode,
|
|
4366
|
+
result.code,
|
|
4367
|
+
]
|
|
4368
|
+
.filter(function (part) { return part !== undefined && part !== null; })
|
|
4369
|
+
.map(function (part) { return String(part); })
|
|
4370
|
+
.join(' ');
|
|
4371
|
+
}
|
|
4372
|
+
|
|
4373
|
+
function getUpdateFormConfigWarningMessage(result) {
|
|
4374
|
+
if (!result || typeof result !== 'object') {
|
|
4375
|
+
return t('common.request_failed');
|
|
4376
|
+
}
|
|
4377
|
+
return result.errorMsg || result.message || result.error || result.errorCode || result.code || t('common.unknown_error');
|
|
4378
|
+
}
|
|
4379
|
+
|
|
4380
|
+
function isRetryablePostSaveUpdateConfigResult(result) {
|
|
4381
|
+
if (!result || result.success || result.__needLogin || result.__csrfExpired) {
|
|
4382
|
+
return false;
|
|
4383
|
+
}
|
|
4384
|
+
const status = Number(result.__httpStatus || result.status || result.statusCode);
|
|
4385
|
+
if (status === 400 || status === 401 || status === 403 || status >= 500) {
|
|
4386
|
+
return false;
|
|
4387
|
+
}
|
|
4388
|
+
const text = getUpdateFormConfigErrorText(result);
|
|
4389
|
+
const lowerText = text.toLowerCase();
|
|
4390
|
+
const normalizedText = lowerText.replace(/[\s_-]/g, '');
|
|
4391
|
+
if (/csrf|auth|login|permission|forbidden|unauthori[sz]ed|invalid|param|权限|登录|认证|授权|参数/.test(lowerText)) {
|
|
4392
|
+
return false;
|
|
4393
|
+
}
|
|
4394
|
+
if (text.indexOf('表单') !== -1 && text.indexOf('不存在') !== -1) {
|
|
4395
|
+
return true;
|
|
4396
|
+
}
|
|
4397
|
+
if (text.indexOf('表单') !== -1 && text.indexOf('未找到') !== -1) {
|
|
4398
|
+
return true;
|
|
4399
|
+
}
|
|
4400
|
+
if (
|
|
4401
|
+
normalizedText.indexOf('formnotfound') !== -1 ||
|
|
4402
|
+
normalizedText.indexOf('formnotexist') !== -1 ||
|
|
4403
|
+
normalizedText.indexOf('formdoesnotexist') !== -1
|
|
4404
|
+
) {
|
|
4405
|
+
return true;
|
|
4406
|
+
}
|
|
4407
|
+
return lowerText.indexOf('not found') !== -1 && /form|formuuid|form uuid/.test(lowerText);
|
|
4408
|
+
}
|
|
4409
|
+
|
|
4410
|
+
async function sendPostSaveUpdateConfigRequest(authRef, appType, formUuid, version) {
|
|
4411
|
+
const retryDelays = getUpdateFormConfigRetryDelays();
|
|
4412
|
+
for (let attemptIndex = 0; attemptIndex <= retryDelays.length; attemptIndex += 1) {
|
|
4413
|
+
let configResult;
|
|
4414
|
+
try {
|
|
4415
|
+
configResult = await requestWithAutoLogin(function (auth) {
|
|
4416
|
+
return sendUpdateConfigRequest(auth.baseUrl, appType, formUuid, version || 1, 0, auth);
|
|
4417
|
+
}, authRef);
|
|
4418
|
+
} catch (err) {
|
|
4419
|
+
return {
|
|
4420
|
+
success: false,
|
|
4421
|
+
errorMsg: err && err.message ? err.message : String(err || 'request failed'),
|
|
4422
|
+
errorCode: err && err.code ? err.code : 'CREATE_FORM_UPDATE_CONFIG_FAILED',
|
|
4423
|
+
};
|
|
4424
|
+
}
|
|
4425
|
+
if (configResult && configResult.success) {
|
|
4426
|
+
return configResult;
|
|
4427
|
+
}
|
|
4428
|
+
if (attemptIndex >= retryDelays.length || !isRetryablePostSaveUpdateConfigResult(configResult)) {
|
|
4429
|
+
return configResult;
|
|
4430
|
+
}
|
|
4431
|
+
await waitForUpdateFormConfigRetry(retryDelays[attemptIndex]);
|
|
4432
|
+
}
|
|
4433
|
+
return {
|
|
4434
|
+
success: false,
|
|
4435
|
+
errorMsg: t('common.request_failed'),
|
|
4436
|
+
errorCode: 'CREATE_FORM_UPDATE_CONFIG_FAILED',
|
|
4437
|
+
};
|
|
4438
|
+
}
|
|
4439
|
+
|
|
4337
4440
|
function buildCreateFormPostCreateFailurePayload(context) {
|
|
4338
4441
|
const errorObject = context && context.error;
|
|
4339
4442
|
const payload = {
|
|
@@ -4387,6 +4490,7 @@ function assertNoEmojiInDefinitionFileName(value) {
|
|
|
4387
4490
|
// ── 保存 Schema 并更新表单配置(create/update 共用)──
|
|
4388
4491
|
//
|
|
4389
4492
|
// 封装了 saveFormSchema + updateFormConfig 两步,以及各自的 302 自动重登录重试。
|
|
4493
|
+
// direct create-form 语义:saveFormSchema 成功后,updateFormConfig 只是后置配置通知。
|
|
4390
4494
|
// 返回 { saveResult, configResult }。
|
|
4391
4495
|
|
|
4392
4496
|
async function saveSchemaAndUpdateConfig(authRef, appType, formUuid, schema, version, stepOffset, failureContext) {
|
|
@@ -4447,9 +4551,7 @@ async function saveSchemaAndUpdateConfig(authRef, appType, formUuid, schema, ver
|
|
|
4447
4551
|
step(configStep, t('create_form.step_update_config', configStep));
|
|
4448
4552
|
info(t('create_form.sending_config'));
|
|
4449
4553
|
|
|
4450
|
-
const configResult = await
|
|
4451
|
-
return sendUpdateConfigRequest(auth.baseUrl, appType, formUuid, version || 1, 0, auth);
|
|
4452
|
-
}, authRef);
|
|
4554
|
+
const configResult = await sendPostSaveUpdateConfigRequest(authRef, appType, formUuid, version);
|
|
4453
4555
|
|
|
4454
4556
|
return { saveResult: saveResult, configResult: configResult };
|
|
4455
4557
|
}
|
|
@@ -4571,31 +4673,29 @@ async function mainCreate(parsedArgs, authRef) {
|
|
|
4571
4673
|
parsedArgs.browserOpenMode
|
|
4572
4674
|
)));
|
|
4573
4675
|
} else {
|
|
4574
|
-
const configErrorMsg = configResult
|
|
4575
|
-
result(
|
|
4676
|
+
const configErrorMsg = getUpdateFormConfigWarningMessage(configResult);
|
|
4677
|
+
result(true, t('create_form.create_success'), [
|
|
4576
4678
|
['Form UUID', formUuid],
|
|
4577
4679
|
['URL', formUrl],
|
|
4578
4680
|
]);
|
|
4681
|
+
warn(t('create_form.config_failed', configErrorMsg));
|
|
4579
4682
|
hint(t('create_form.schema_ok_config_failed'));
|
|
4580
|
-
|
|
4581
|
-
|
|
4582
|
-
|
|
4583
|
-
result: sanitizeFailureResult(configResult),
|
|
4584
|
-
});
|
|
4585
|
-
console.log(JSON.stringify(Object.assign(
|
|
4586
|
-
buildCreateFormPostCreateFailurePayload({
|
|
4683
|
+
console.log(JSON.stringify(withBrowserHandoff(
|
|
4684
|
+
{
|
|
4685
|
+
success: true,
|
|
4587
4686
|
appType,
|
|
4588
4687
|
formTitle,
|
|
4589
4688
|
formUuid,
|
|
4590
4689
|
fieldCount,
|
|
4591
|
-
stage: 'updateFormConfig',
|
|
4592
|
-
error: configError,
|
|
4593
|
-
}),
|
|
4594
|
-
{
|
|
4595
4690
|
url: formUrl,
|
|
4691
|
+
stage: 'updateFormConfig',
|
|
4596
4692
|
schemaSaved: true,
|
|
4597
4693
|
configWarning: configErrorMsg,
|
|
4598
|
-
|
|
4694
|
+
configResult: sanitizeFailureResult(configResult),
|
|
4695
|
+
},
|
|
4696
|
+
formUrl,
|
|
4697
|
+
{ stage: 'create_form_success', title: formTitle },
|
|
4698
|
+
parsedArgs.browserOpenMode
|
|
4599
4699
|
)));
|
|
4600
4700
|
}
|
|
4601
4701
|
}
|
|
@@ -5517,15 +5617,27 @@ async function mainUpdate(parsedArgs, authRef) {
|
|
|
5517
5617
|
parsedArgs.browserOpenMode
|
|
5518
5618
|
)));
|
|
5519
5619
|
} else {
|
|
5520
|
-
const configErrorMsg = configResult
|
|
5521
|
-
result(
|
|
5620
|
+
const configErrorMsg = getUpdateFormConfigWarningMessage(configResult);
|
|
5621
|
+
result(true, t('create_form.update_success'), [
|
|
5522
5622
|
['Form UUID', formUuid],
|
|
5523
5623
|
['URL', formUrl],
|
|
5524
5624
|
['Changes', String(appliedChanges.length)],
|
|
5525
5625
|
]);
|
|
5626
|
+
warn(t('create_form.config_failed', configErrorMsg));
|
|
5526
5627
|
hint(t('create_form.schema_ok_config_failed'));
|
|
5527
5628
|
console.log(JSON.stringify(withBrowserHandoff(
|
|
5528
|
-
{
|
|
5629
|
+
{
|
|
5630
|
+
success: true,
|
|
5631
|
+
formUuid,
|
|
5632
|
+
appType,
|
|
5633
|
+
changesApplied: appliedChanges.length,
|
|
5634
|
+
changes: appliedChanges,
|
|
5635
|
+
url: formUrl,
|
|
5636
|
+
stage: 'updateFormConfig',
|
|
5637
|
+
schemaSaved: true,
|
|
5638
|
+
configWarning: configErrorMsg,
|
|
5639
|
+
configResult: sanitizeFailureResult(configResult),
|
|
5640
|
+
},
|
|
5529
5641
|
formUrl,
|
|
5530
5642
|
{ stage: 'update_form_success', title: formUuid },
|
|
5531
5643
|
parsedArgs.browserOpenMode
|
package/package.json
CHANGED
|
@@ -24,6 +24,7 @@ Code Canvas 是宜搭的代码画布自定义页面链路:以 `YidaCodeCanvas`
|
|
|
24
24
|
- Canvas 源码写成 `.canvas.jsx` / `.canvas.tsx`,`openyida publish` 会自动走 Canvas 链路。
|
|
25
25
|
- 页面源码路径按 Bash cwd 选择:从仓库根执行命令时用 `project/pages/src/...`;如果 cwd 已是 `<workspace>/project`,用 `pages/src/...`,不要写成 `project/pages/src/...`。
|
|
26
26
|
- `runtimeCode` 在宿主页真实 `window` 中执行,入口必须返回 `YidaComp` / `YidaComp.default` / 组件函数。
|
|
27
|
+
- 推荐入口写法是 `function YidaComp(props) { ... }`,或 `const App = ...; export default App;`。CLI 已兼容 `const/let/class YidaComp; export default YidaComp`,但生成新代码时优先避开同名默认导出,减少不同 Canvas 运行态装配器下的重复声明风险。
|
|
27
28
|
- Canvas 组件没有普通页面实例上下文;数据读写通过 fetch、开放 API、连接器代理或显式 props 数据桥完成。
|
|
28
29
|
- 第三方依赖走白名单;React、antd、ahooks、d3、recharts、Radix、framer-motion 等可按规则 import。
|
|
29
30
|
- 宜搭运行态组件通过“原生组件桥 + fallback + 值归一化”接入;不改 `vc-deep-yida` 时,以宿主已存在的 `window.Deep` / `window.DeepYida` 探测为主,`window.YidaNativeComponents` 仅作为兼容入口。需要嵌入门户数据管理视图时探测 `DataManageViews`,并显式传入目标表单 `form.value/formUuid`。
|
|
@@ -724,6 +724,11 @@
|
|
|
724
724
|
],
|
|
725
725
|
"aliases": [
|
|
726
726
|
"登录态"
|
|
727
|
+
],
|
|
728
|
+
"command_ids": [
|
|
729
|
+
"agent-capabilities",
|
|
730
|
+
"login",
|
|
731
|
+
"auth"
|
|
727
732
|
]
|
|
728
733
|
},
|
|
729
734
|
{
|
|
@@ -739,6 +744,10 @@
|
|
|
739
744
|
],
|
|
740
745
|
"aliases": [
|
|
741
746
|
"登出"
|
|
747
|
+
],
|
|
748
|
+
"command_ids": [
|
|
749
|
+
"logout",
|
|
750
|
+
"auth"
|
|
742
751
|
]
|
|
743
752
|
},
|
|
744
753
|
{
|