openyida 2026.8.25-1 → 2026.8.26
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 +5 -1
- package/bin/yida.js +3 -0
- package/lib/app/canvas-compile.js +169 -0
- package/lib/app/create-form/api-path.js +11 -2
- package/lib/app/create-form.js +109 -339
- package/lib/app/create-page.js +25 -13
- package/lib/app/form-detail-style.js +3 -28
- package/lib/app/import-app.js +13 -31
- package/lib/app/publish.js +18 -74
- package/lib/core/command-manifest.js +9 -0
- package/lib/core/locales/en.js +21 -22
- package/lib/core/locales/zh.js +21 -20
- package/lib/core/query-data.js +111 -12
- package/lib/core/utils.js +41 -0
- package/lib/integration/integration-create.js +12 -0
- package/lib/integration/integration-update.js +113 -0
- package/package.json +2 -1
- package/yida-skills/references/yida-api.md +0 -29
- package/yida-skills/skills/yida-app/references/common-issues.md +2 -0
- package/yida-skills/skills/yida-app/workflow/step-7-page-code.md +1 -0
- package/yida-skills/skills/yida-app/workflow/step-8-publish-navigation.md +10 -8
- package/yida-skills/skills/yida-app/workflow/step-9-output-finish.md +8 -6
- package/yida-skills/skills/yida-canvas-custom-page/SKILL.md +14 -0
- package/yida-skills/skills/yida-canvas-custom-page/references/data-bridge-guide.md +2 -0
- package/yida-skills/skills/yida-canvas-custom-page/references/navigation-and-entry-guide.md +7 -5
- package/yida-skills/skills/yida-canvas-data-binding/SKILL.md +1 -0
- package/yida-skills/skills/yida-form-detail/references/injection-guide.md +0 -22
- package/yida-skills/skills/yida-integration/SKILL.md +23 -5
- package/yida-skills/skills/yida-publish-page/SKILL.md +3 -3
package/README.md
CHANGED
|
@@ -349,6 +349,9 @@ openyida connector list
|
|
|
349
349
|
openyida integration create APP_XXX FORM_XXX "Sync customer data"
|
|
350
350
|
openyida integration create APP_XXX FORM_XXX "Approval result notify" \
|
|
351
351
|
--events processFinish --approval-actions agree,disagree --receivers 123456
|
|
352
|
+
# Capability probe only; currently fails closed before auth/spec reads/remote writes
|
|
353
|
+
openyida integration update APP_XXX FORM_XXX LPROC_XXX \
|
|
354
|
+
--spec .cache/openyida/integration/desired-spec.json
|
|
352
355
|
openyida create-report APP_XXX "Sales Dashboard" .cache/openyida/reports/charts.json
|
|
353
356
|
openyida append-chart APP_XXX REPORT_XXX .cache/openyida/reports/chart.json
|
|
354
357
|
```
|
|
@@ -477,6 +480,7 @@ Run `openyida --help` or `openyida <command> --help` for detailed usage.
|
|
|
477
480
|
| Command | Description |
|
|
478
481
|
|---------|-------------|
|
|
479
482
|
| `openyida integration create <appType> ... [--spec file.json]` | Create integration automation flow |
|
|
483
|
+
| `openyida integration update <appType> <formUuid> <processCode> --spec <desired-spec.json> [--publish]` | Probe integration update capability (currently blocked without full readback) |
|
|
480
484
|
| `openyida integration list <appType> [--form-uuid <uuid>] [--status y\|n] [--json]` | List integration automation flows |
|
|
481
485
|
| `openyida integration enable <appType> <formUuid> <processCode>` | Enable integration automation flow |
|
|
482
486
|
| `openyida integration disable <appType> <formUuid> <processCode>` | Disable integration automation flow |
|
|
@@ -537,7 +541,7 @@ Form field definitions can include `alias` or `componentAlias` to populate Yida
|
|
|
537
541
|
|
|
538
542
|
#### Workflow, Reports, and Integrations
|
|
539
543
|
|
|
540
|
-
`openyida integration create` supports form events (`insert`, `update`, `delete`, `comment`) and approval events (`processFinish`, `activityTask`; aliases: `approval`, `approvalNode`). Approval events require `--approval-actions agree,disagree,terminated`; `activityTask` also requires `--approval-node-ids <nodeId,...>`.
|
|
544
|
+
`openyida integration create` supports form events (`insert`, `update`, `delete`, `comment`) and approval events (`processFinish`, `activityTask`; aliases: `approval`, `approvalNode`). Approval events require `--approval-actions agree,disagree,terminated`; `activityTask` also requires `--approval-node-ids <nodeId,...>`. Passing `--process-code` selects a full graph replacement and now requires explicit `--replace`; it is not a safe update. `integration update` currently performs capability detection only: because full platform `processJson` + `viewJson` readback is unproven, it writes a redacted local probe artifact and returns `PLATFORM_PROBE_REQUIRED` before authentication, spec reading, or remote writes. It does not edit a flow.
|
|
541
545
|
|
|
542
546
|
## Agent Skills
|
|
543
547
|
|
package/bin/yida.js
CHANGED
|
@@ -1173,6 +1173,9 @@ async function main() {
|
|
|
1173
1173
|
if (subCommand === 'create') {
|
|
1174
1174
|
const { run: runIntegration } = require('../lib/integration/integration-create');
|
|
1175
1175
|
await runIntegration(subArgs);
|
|
1176
|
+
} else if (subCommand === 'update') {
|
|
1177
|
+
const { run: runIntegrationUpdate } = require('../lib/integration/integration-update');
|
|
1178
|
+
await runIntegrationUpdate(subArgs);
|
|
1176
1179
|
} else if (subCommand === 'list') {
|
|
1177
1180
|
const { runList } = require('../lib/integration/integration-list');
|
|
1178
1181
|
await runList(subArgs);
|
|
@@ -29,6 +29,7 @@
|
|
|
29
29
|
*/
|
|
30
30
|
|
|
31
31
|
const Babel = require('@babel/standalone');
|
|
32
|
+
const globals = require('globals');
|
|
32
33
|
const {
|
|
33
34
|
assertNoEmojiInArtifactName,
|
|
34
35
|
assertNoEmojiInText,
|
|
@@ -46,6 +47,7 @@ const {
|
|
|
46
47
|
findFormDetailLinkIssues,
|
|
47
48
|
formatFormDetailLinkMessage,
|
|
48
49
|
} = require('./form-detail-link-guard');
|
|
50
|
+
const { t } = require('../core/i18n');
|
|
49
51
|
|
|
50
52
|
/**
|
|
51
53
|
* 依赖 → window 别名白名单,用于把 import 改写到运行时全局对象。
|
|
@@ -132,6 +134,15 @@ const BARE_DEPENDENCY_GLOBALS = Object.freeze({
|
|
|
132
134
|
},
|
|
133
135
|
});
|
|
134
136
|
|
|
137
|
+
// 以标准 ECMAScript + Browser 环境定义为完整白名单;Canvas 装配器额外
|
|
138
|
+
// 注入 iframeWindow / parentWindow。Node 专属全局不属于页面运行时,不能放行。
|
|
139
|
+
const CANVAS_RUNTIME_GLOBAL_IDENTIFIERS = new Set([
|
|
140
|
+
...Object.keys(globals.builtin),
|
|
141
|
+
...Object.keys(globals.browser),
|
|
142
|
+
'iframeWindow',
|
|
143
|
+
'parentWindow',
|
|
144
|
+
]);
|
|
145
|
+
|
|
135
146
|
const LUCIDE_REACT_PACKAGE = 'lucide-react';
|
|
136
147
|
const LUCIDE_DYNAMIC_ICON_ALIAS = 'DynamicIcon';
|
|
137
148
|
const LUCIDE_MODULE_ALIAS = 'LucideReact';
|
|
@@ -267,6 +278,159 @@ function assertNoBareDependencyGlobals(source, options = {}) {
|
|
|
267
278
|
});
|
|
268
279
|
}
|
|
269
280
|
|
|
281
|
+
function findUnboundIdentifierIssues(source, options = {}) {
|
|
282
|
+
const issuesByName = new Map();
|
|
283
|
+
const allowedIdentifiers = new Set(CANVAS_RUNTIME_GLOBAL_IDENTIFIERS);
|
|
284
|
+
(options.implicitIdentifiers || []).forEach((name) => allowedIdentifiers.add(name));
|
|
285
|
+
|
|
286
|
+
const guardPlugin = () => ({
|
|
287
|
+
name: 'yida-canvas-unbound-identifier-guard',
|
|
288
|
+
visitor: {
|
|
289
|
+
ReferencedIdentifier(path) {
|
|
290
|
+
const name = path.node.name;
|
|
291
|
+
if (allowedIdentifiers.has(name) || path.scope.hasBinding(name) || issuesByName.has(name)) {
|
|
292
|
+
return;
|
|
293
|
+
}
|
|
294
|
+
issuesByName.set(name, {
|
|
295
|
+
name,
|
|
296
|
+
line: path.node.loc && path.node.loc.start ? path.node.loc.start.line : 0,
|
|
297
|
+
column: path.node.loc && path.node.loc.start ? path.node.loc.start.column : 0,
|
|
298
|
+
});
|
|
299
|
+
},
|
|
300
|
+
},
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
Babel.transform(source, {
|
|
304
|
+
filename: 'canvas.tsx',
|
|
305
|
+
presets: [
|
|
306
|
+
['typescript', { allExtensions: true, isTSX: true, allowDeclareFields: true }],
|
|
307
|
+
['react', { runtime: 'classic' }],
|
|
308
|
+
],
|
|
309
|
+
plugins: [guardPlugin],
|
|
310
|
+
sourceType: 'module',
|
|
311
|
+
compact: false,
|
|
312
|
+
babelrc: false,
|
|
313
|
+
configFile: false,
|
|
314
|
+
});
|
|
315
|
+
|
|
316
|
+
return Array.from(issuesByName.values());
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
function formatUnboundIdentifierMessage(issues) {
|
|
320
|
+
const summary = issues
|
|
321
|
+
.map(issue => `${issue.name} (${issue.line}:${issue.column + 1})`)
|
|
322
|
+
.join(', ');
|
|
323
|
+
return t('publish.canvas_unbound_identifiers', summary);
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
function assertNoUnboundIdentifiers(source, options = {}) {
|
|
327
|
+
const issues = findUnboundIdentifierIssues(source, options);
|
|
328
|
+
if (issues.length === 0) {
|
|
329
|
+
return;
|
|
330
|
+
}
|
|
331
|
+
throw new CliError(formatUnboundIdentifierMessage(issues), {
|
|
332
|
+
code: 'OPENYIDA_CANVAS_UNBOUND_IDENTIFIER',
|
|
333
|
+
details: {
|
|
334
|
+
stage: 'canvas_compile',
|
|
335
|
+
sourcePath: options.sourcePath || '',
|
|
336
|
+
issues,
|
|
337
|
+
},
|
|
338
|
+
});
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
function findUnavailableCanvasInstanceApiIssues(source) {
|
|
342
|
+
const issues = [];
|
|
343
|
+
const seen = new Set();
|
|
344
|
+
|
|
345
|
+
function addIssue(path, api) {
|
|
346
|
+
if (seen.has(api)) {
|
|
347
|
+
return;
|
|
348
|
+
}
|
|
349
|
+
seen.add(api);
|
|
350
|
+
issues.push({
|
|
351
|
+
api,
|
|
352
|
+
line: path.node.loc && path.node.loc.start ? path.node.loc.start.line : 0,
|
|
353
|
+
column: path.node.loc && path.node.loc.start ? path.node.loc.start.column : 0,
|
|
354
|
+
});
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
const guardPlugin = ({ types: types }) => ({
|
|
358
|
+
name: 'yida-canvas-instance-api-guard',
|
|
359
|
+
visitor: {
|
|
360
|
+
MemberExpression(path) {
|
|
361
|
+
const object = path.node.object;
|
|
362
|
+
const property = path.node.property;
|
|
363
|
+
const propertyName = path.node.computed && types.isStringLiteral(property)
|
|
364
|
+
? property.value
|
|
365
|
+
: (types.isIdentifier(property) ? property.name : '');
|
|
366
|
+
|
|
367
|
+
if (types.isMemberExpression(object)) {
|
|
368
|
+
const owner = object.object;
|
|
369
|
+
const ownerProperty = object.property;
|
|
370
|
+
const ownerPropertyName = object.computed && types.isStringLiteral(ownerProperty)
|
|
371
|
+
? ownerProperty.value
|
|
372
|
+
: (types.isIdentifier(ownerProperty) ? ownerProperty.name : '');
|
|
373
|
+
if (
|
|
374
|
+
types.isIdentifier(owner, { name: 'props' }) &&
|
|
375
|
+
ownerPropertyName === 'utils' &&
|
|
376
|
+
propertyName === 'getDataList'
|
|
377
|
+
) {
|
|
378
|
+
addIssue(path, 'props.utils.getDataList');
|
|
379
|
+
return;
|
|
380
|
+
}
|
|
381
|
+
if (
|
|
382
|
+
types.isThisExpression(owner) &&
|
|
383
|
+
ownerPropertyName === 'utils' &&
|
|
384
|
+
propertyName === 'yida'
|
|
385
|
+
) {
|
|
386
|
+
addIssue(path, 'this.utils.yida');
|
|
387
|
+
return;
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
if (!types.isThisExpression(object)) {
|
|
391
|
+
return;
|
|
392
|
+
}
|
|
393
|
+
if (propertyName === 'dataSourceMap') {
|
|
394
|
+
addIssue(path, 'this.dataSourceMap');
|
|
395
|
+
} else if (propertyName === '$') {
|
|
396
|
+
addIssue(path, 'this.$');
|
|
397
|
+
}
|
|
398
|
+
},
|
|
399
|
+
},
|
|
400
|
+
});
|
|
401
|
+
|
|
402
|
+
Babel.transform(source, {
|
|
403
|
+
filename: 'canvas.tsx',
|
|
404
|
+
presets: [
|
|
405
|
+
['typescript', { allExtensions: true, isTSX: true, allowDeclareFields: true }],
|
|
406
|
+
['react', { runtime: 'classic' }],
|
|
407
|
+
],
|
|
408
|
+
plugins: [guardPlugin],
|
|
409
|
+
sourceType: 'module',
|
|
410
|
+
compact: false,
|
|
411
|
+
babelrc: false,
|
|
412
|
+
configFile: false,
|
|
413
|
+
});
|
|
414
|
+
|
|
415
|
+
return issues;
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
function assertNoUnavailableCanvasInstanceApis(source, options = {}) {
|
|
419
|
+
const issues = findUnavailableCanvasInstanceApiIssues(source);
|
|
420
|
+
if (issues.length === 0) {
|
|
421
|
+
return;
|
|
422
|
+
}
|
|
423
|
+
const summary = issues.map(issue => issue.api).join(', ');
|
|
424
|
+
throw new CliError(t('publish.canvas_instance_api_unavailable', summary), {
|
|
425
|
+
code: 'OPENYIDA_CANVAS_INSTANCE_API_UNAVAILABLE',
|
|
426
|
+
details: {
|
|
427
|
+
stage: 'canvas_compile',
|
|
428
|
+
sourcePath: options.sourcePath || '',
|
|
429
|
+
issues,
|
|
430
|
+
},
|
|
431
|
+
});
|
|
432
|
+
}
|
|
433
|
+
|
|
270
434
|
/**
|
|
271
435
|
* 从源码里正则抽取裸包名(过滤相对/绝对路径),去重排序。
|
|
272
436
|
* 对齐画布运行时的依赖抽取行为。
|
|
@@ -617,6 +781,7 @@ function compileCanvasLocal(source, options = {}) {
|
|
|
617
781
|
},
|
|
618
782
|
});
|
|
619
783
|
}
|
|
784
|
+
assertNoUnavailableCanvasInstanceApis(source, options);
|
|
620
785
|
assertNoBareDependencyGlobals(source, options);
|
|
621
786
|
assertNoManualDependencyGlobals(source, options);
|
|
622
787
|
|
|
@@ -643,6 +808,10 @@ function compileCanvasLocal(source, options = {}) {
|
|
|
643
808
|
// 'react' 进入依赖清单(运行时据此注入 React UMD)。
|
|
644
809
|
const usesJsxRuntime = /\bReact\.createElement\b|\bReact\.Fragment\b/.test(intermediate);
|
|
645
810
|
const hasReactBinding = /\b(var|let|const)\s+React\b/.test(intermediate) || /\bimport\s+React\b/.test(intermediate);
|
|
811
|
+
assertNoUnboundIdentifiers(source, {
|
|
812
|
+
sourcePath: options.sourcePath,
|
|
813
|
+
implicitIdentifiers: usesJsxRuntime ? ['React'] : [],
|
|
814
|
+
});
|
|
646
815
|
if (usesJsxRuntime && !hasReactBinding) {
|
|
647
816
|
intermediate = "import React from 'react';\n" + intermediate;
|
|
648
817
|
if (!importedModules.includes('react')) {
|
|
@@ -10,14 +10,23 @@
|
|
|
10
10
|
* @param {object} [options] 可选项
|
|
11
11
|
* @param {string} [options.prefix] 额外的路径前缀(会以 / 拼接)
|
|
12
12
|
* @param {string} [options.namespace] 命名空间,默认 dingtalk
|
|
13
|
+
* @param {'formdesign'|'formnav'} [options.queryModule] 查询模块,默认 formdesign
|
|
13
14
|
* @param {boolean} [options.addTimestamp] 是否追加 _stamp 时间戳查询参数
|
|
14
15
|
* @returns {string} 拼接完成的 API 请求路径
|
|
15
16
|
*/
|
|
16
17
|
function buildApiPath(appType, apiName, options = {}) {
|
|
17
|
-
const {
|
|
18
|
+
const {
|
|
19
|
+
prefix = '',
|
|
20
|
+
namespace = 'dingtalk',
|
|
21
|
+
queryModule = 'formdesign',
|
|
22
|
+
addTimestamp = false,
|
|
23
|
+
} = options;
|
|
24
|
+
if (queryModule !== 'formdesign' && queryModule !== 'formnav') {
|
|
25
|
+
throw new Error(`Unsupported query module: ${queryModule}`);
|
|
26
|
+
}
|
|
18
27
|
const prefixPath = prefix ? `/${prefix}` : '';
|
|
19
28
|
const timestamp = addTimestamp ? `?_stamp=${Date.now()}` : '';
|
|
20
|
-
return `/${namespace}/web/${appType}${prefixPath}/query
|
|
29
|
+
return `/${namespace}/web/${appType}${prefixPath}/query/${queryModule}/${apiName}.json${timestamp}`;
|
|
21
30
|
}
|
|
22
31
|
|
|
23
32
|
module.exports = {
|