@hmos-library-adapter/fast 0.0.17-beta.0 → 0.0.17-beta.1
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/node_modules/@hmos-library-adapter/executer/README.md +2 -2
- package/node_modules/@hmos-library-adapter/executer/shared/skill-map.json +5 -1
- package/node_modules/@hmos-library-adapter/executer/shared/skills/arkts-rules/SKILL.md +400 -0
- package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/CHANGELOG.md +1391 -0
- package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/COMPATIBILITY.md +53 -0
- package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/README.md +123 -0
- package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/SKILL.md +48 -0
- package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/agents/upgrade-assess-change.md +37 -0
- package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/agents/upgrade-fix-file.md +23 -0
- package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/package.json +19 -0
- package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/references/changelogs/26.0.0/beta1-changes-for-all-apps.md +1793 -0
- package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/references/changelogs/26.0.0/beta1-ux-style-and-effect-changes.md +288 -0
- package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/references/changelogs/26.0.0/beta2-changes-for-all-apps.md +148 -0
- package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/references/changelogs/26.0.0/release-changes-for-all-apps.md +662 -0
- package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/references/changelogs/5.0.1(13)/beta3-os-api-behavior-changes.md +652 -0
- package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/references/changelogs/5.0.1(13)/beta3-ux-style-and-effect-changes.md +222 -0
- package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/references/changelogs/5.0.1(13)/release-os-api-behavior-changes.md +444 -0
- package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/references/changelogs/5.0.1(13)/release-ux-style-and-effect-changes.md +221 -0
- package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/references/changelogs/5.0.2(14)/os-api-behavior-changes.md +1767 -0
- package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/references/changelogs/5.0.2(14)/ux-style-and-effect-changes.md +134 -0
- package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/references/changelogs/5.0.3(15)/beta1-os-api-behavior-changes.md +430 -0
- package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/references/changelogs/5.0.3(15)/beta1-ux-style-and-effect-changes.md +49 -0
- package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/references/changelogs/5.0.3(15)/beta2-os-api-behavior-changes.md +240 -0
- package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/references/changelogs/5.0.3(15)/beta2-ux-style-and-effect-changes.md +90 -0
- package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/references/changelogs/5.0.3(15)/release-os-api-behavior-changes.md +69 -0
- package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/references/changelogs/5.1.0(18)/os-api-behavior-changes.md +1294 -0
- package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/references/changelogs/5.1.0(18)/ux-style-and-effect-changes.md +147 -0
- package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/references/changelogs/6.0.0(20)/beta1-os-api-behavior-changes.md +1019 -0
- package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/references/changelogs/6.0.0(20)/beta1-ux-style-and-effect-changes.md +153 -0
- package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/references/changelogs/6.0.0(20)/beta2-os-api-behavior-changes.md +662 -0
- package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/references/changelogs/6.0.0(20)/beta2-ux-style-and-effect-changes.md +78 -0
- package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/references/changelogs/6.0.0(20)/beta3-os-api-behavior-changes.md +167 -0
- package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/references/changelogs/6.0.0(20)/beta5-os-api-behavior-changes.md +52 -0
- package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/references/changelogs/6.1.0(23)/changes-for-all-apps.md +397 -0
- package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/references/changelogs-index.json +883 -0
- package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/references/common/arkts-coding-standards.md +53 -0
- package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/references/common/compatibility.md +207 -0
- package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/references/common/native-adaptation.md +21 -0
- package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/references/common/runtime-functional-tests.md +62 -0
- package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/references/common/sdk-versions.md +77 -0
- package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/references/common/troubleshooting.md +54 -0
- package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/references/common/verification.md +79 -0
- package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/references/common/workflow-scan.md +41 -0
- package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/references/deprecated/context-migration.md +274 -0
- package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/references/deprecated/deprecated-api-adaptation.md +157 -0
- package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/references/deprecated/deprecated-api-examples.md +219 -0
- package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/references/toolchain/hvigor.md +50 -0
- package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/references/upgrade/har-upgrade.md +60 -0
- package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/references/upgrade/scanner-missed-cases.md +171 -0
- package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/references/upgrade/upgrade-compatibility.md +112 -0
- package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/references/upgrade/upgrade-examples.md +289 -0
- package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/references/upgrade/upgrade-workflow.md +122 -0
- package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/scripts/maintenance/references/generate-behavior-policy.mjs +96 -0
- package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/scripts/maintenance/references/references.mjs +9 -0
- package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/scripts/maintenance/regression/compare-deprecated.mjs +57 -0
- package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/scripts/maintenance/regression/compare-golden.mjs +130 -0
- package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/scripts/maintenance/regression/regress-deprecated.mjs +120 -0
- package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/scripts/maintenance/test/behavior-catalog-generation.test.mjs +59 -0
- package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/scripts/maintenance/test/behavior-local-sources.test.mjs +44 -0
- package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/scripts/maintenance/test/changelog-index.test.mjs +66 -0
- package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/scripts/maintenance/test/compare-golden.test.mjs +103 -0
- package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/scripts/maintenance/test/fixtures/behavior/local-sources.csv +5 -0
- package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/scripts/maintenance/test/references.test.mjs +16 -0
- package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/scripts/maintenance/test/skill-content.test.mjs +139 -0
- package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/scripts/maintenance/test/skill-frontmatter.test.mjs +11 -0
- package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/scripts/maintenance/test/test-suite.test.mjs +65 -0
- package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/scripts/maintenance/validation/skill-content.mjs +125 -0
- package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/scripts/maintenance/validation/skill-frontmatter.mjs +6 -0
- package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/scripts/maintenance/validation/validate-skill.mjs +57 -0
- package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/scripts/scanner/CHANGELOG.md +506 -0
- package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/scripts/scanner/GOLDEN_VALIDATION.md +42 -0
- package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/scripts/scanner/README.md +137 -0
- package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/scripts/scanner/behavior/behavior-policy.mjs +117 -0
- package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/scripts/scanner/behavior/upgrade-coverage.mjs +29 -0
- package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/scripts/scanner/bin/deveco-worker-compat.cjs +21 -0
- package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/scripts/scanner/bin/harmony-api-scan.mjs +17 -0
- package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/scripts/scanner/core/cli.mjs +416 -0
- package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/scripts/scanner/core/deveco.mjs +348 -0
- package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/scripts/scanner/core/project.mjs +81 -0
- package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/scripts/scanner/core/rules.mjs +425 -0
- package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/scripts/scanner/core/runtime-identity.mjs +23 -0
- package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/scripts/scanner/core/scan-runner.mjs +521 -0
- package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/scripts/scanner/core/sdk-deprecations.mjs +204 -0
- package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/scripts/scanner/data/behavior-change-policy.json +4378 -0
- package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/scripts/scanner/deprecated/arkts-deprecations.mjs +363 -0
- package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/scripts/scanner/deprecated/build-log.mjs +72 -0
- package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/scripts/scanner/native/native.mjs +95 -0
- package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/scripts/scanner/package.json +16 -0
- package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/scripts/scanner/test/batch-layout.test.mjs +94 -0
- package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/scripts/scanner/test/behavior-policy.test.mjs +75 -0
- package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/scripts/scanner/test/build-log.test.mjs +69 -0
- package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/scripts/scanner/test/cli.test.mjs +102 -0
- package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/scripts/scanner/test/csv.test.mjs +35 -0
- package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/scripts/scanner/test/deprecations.test.mjs +283 -0
- package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/scripts/scanner/test/deveco.test.mjs +262 -0
- package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/scripts/scanner/test/helpers/scan-batch.mjs +10 -0
- package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/scripts/scanner/test/native.test.mjs +108 -0
- package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/scripts/scanner/test/project.test.mjs +42 -0
- package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/scripts/scanner/test/rules.test.mjs +190 -0
- package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/scripts/scanner/test/runtime-identity.test.mjs +53 -0
- package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/scripts/scanner/test/scan-mode.test.mjs +38 -0
- package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/scripts/scanner/test/scan-runner.test.mjs +481 -0
- package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/scripts/scanner/test/scan-scope.test.mjs +110 -0
- package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/scripts/scanner/test/source-inputs.test.mjs +64 -0
- package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/scripts/scanner/test/upgrade.test.mjs +164 -0
- package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/scripts/shared/format/args.mjs +66 -0
- package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/scripts/shared/format/csv.mjs +85 -0
- package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/scripts/shared/format/json5.mjs +92 -0
- package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/scripts/shared/format/upgrade-results.mjs +45 -0
- package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/scripts/shared/format/zip.mjs +87 -0
- package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/scripts/shared/io/fs-utils.mjs +60 -0
- package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/scripts/shared/platform/artifact-layout.mjs +74 -0
- package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/scripts/shared/platform/changelog-index.mjs +29 -0
- package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/scripts/shared/platform/deveco-version.mjs +40 -0
- package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/scripts/shared/platform/document-links.mjs +30 -0
- package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/scripts/shared/platform/errors.mjs +41 -0
- package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/scripts/shared/platform/fs.mjs +72 -0
- package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/scripts/shared/platform/paths.mjs +34 -0
- package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/scripts/shared/platform/process.mjs +154 -0
- package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/scripts/shared/platform/scan-mode.mjs +7 -0
- package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/scripts/shared/platform/scan-statistics.mjs +7 -0
- package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/scripts/shared/platform/scan-version-catalog.json +43 -0
- package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/scripts/shared/platform/scan-versions.mjs +51 -0
- package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/scripts/shared/platform/sdk-layout.mjs +27 -0
- package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/scripts/shared/platform/source-scope.mjs +108 -0
- package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/scripts/shared/platform/source-types.mjs +7 -0
- package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/scripts/shared/platform/version.mjs +76 -0
- package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/scripts/shared/test/deveco-version.test.mjs +50 -0
- package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/scripts/shared/test/document-links.test.mjs +13 -0
- package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/scripts/shared/test/errors.test.mjs +40 -0
- package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/scripts/shared/test/fs-utils.test.mjs +31 -0
- package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/scripts/shared/test/platform-paths.test.mjs +19 -0
- package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/scripts/shared/test/process-utils.test.mjs +68 -0
- package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/scripts/shared/test/scan-versions.test.mjs +26 -0
- package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/scripts/shared/test/sdk-version.test.mjs +17 -0
- package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/scripts/shared/test/upgrade-results.test.mjs +140 -0
- package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/scripts/test-suite.mjs +29 -0
- package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/templates/deprecated-report.md +86 -0
- package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/templates/progress.md +150 -0
- package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/templates/results.md +110 -0
- package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/templates/upgrade-report.md +105 -0
- package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/templates/upgrade-spec.md +58 -0
- package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/templates/upgrade-test-report.md +47 -0
- package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-knowledge/SKILL.md +70 -0
- package/node_modules/@hmos-library-adapter/executer/src/config.js +3 -4
- package/node_modules/@hmos-library-adapter/executer/src/doctor.js +1 -1
- package/node_modules/@hmos-library-adapter/executer/src/install.js +1 -1
- package/node_modules/@hmos-library-adapter/executer/src/paths.js +4 -4
- package/package.json +1 -1
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import os from 'node:os';
|
|
5
|
+
import { mkdtemp, mkdir, cp, readFile, realpath, rm } from 'node:fs/promises';
|
|
6
|
+
import { spawnSync } from 'node:child_process';
|
|
7
|
+
import { pathToFileURL, fileURLToPath } from 'node:url';
|
|
8
|
+
import { runtimeIdentity } from '../core/runtime-identity.mjs';
|
|
9
|
+
|
|
10
|
+
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../../..');
|
|
11
|
+
|
|
12
|
+
test('a complete skill copy runs CLI and validation from another cwd with local resources and a stable fingerprint', async t => {
|
|
13
|
+
const workspace = await mkdtemp(path.join(os.tmpdir(), '复制 skill 中文 space-'));
|
|
14
|
+
t.after(() => rm(workspace, { recursive: true, force: true }));
|
|
15
|
+
const target = path.join(workspace, 'harmonyos-api-upgrade-adapter');
|
|
16
|
+
const cwd = path.join(workspace, 'business');
|
|
17
|
+
await mkdir(cwd);
|
|
18
|
+
const excluded = new Set(['.git', 'node_modules', '.cache', '.superpowers', '.api-change-output', 'evals']);
|
|
19
|
+
await cp(root, target, { recursive: true, filter: file => !excluded.has(path.basename(file)) });
|
|
20
|
+
const { runtimeIdentity: identify } = await import(pathToFileURL(path.join(target, 'scripts', 'scanner', 'core', 'runtime-identity.mjs')));
|
|
21
|
+
const identity = await identify(target, { mode: 'deprecated' });
|
|
22
|
+
assert.equal(identity.skillRoot, target);
|
|
23
|
+
assert.equal(identity.implementationFingerprint, (await runtimeIdentity(root, { mode: 'deprecated' })).implementationFingerprint);
|
|
24
|
+
await assert.rejects(identify(target, { mode: 'full' }), { code: 'SCAN_MODE_INVALID' });
|
|
25
|
+
const run = args => {
|
|
26
|
+
const env = { ...process.env };
|
|
27
|
+
delete env.NODE_TEST_CONTEXT;
|
|
28
|
+
const result = spawnSync(process.execPath, args, { cwd, env, encoding: 'utf8', timeout: 30_000 });
|
|
29
|
+
assert.ifError(result.error);
|
|
30
|
+
return result;
|
|
31
|
+
};
|
|
32
|
+
const entry = path.join(target, 'scripts', 'scanner', 'bin', 'harmony-api-scan.mjs');
|
|
33
|
+
const help = run([entry, 'help']);
|
|
34
|
+
assert.equal(help.status, 0, help.stderr);
|
|
35
|
+
assert.equal(JSON.parse(help.stdout).ok, true);
|
|
36
|
+
const scan = run([entry, 'scan', '--mode', 'upgrade', '--project', cwd, '--sdk', path.join(workspace, 'missing-sdk'),
|
|
37
|
+
'--from', '13', '--to', '26', '--output-dir', path.join(workspace, 'output')]);
|
|
38
|
+
assert.equal(scan.status, 1);
|
|
39
|
+
const error = JSON.parse(scan.stderr).error;
|
|
40
|
+
assert.equal(error.stage, 'DISCOVER_TOOLCHAIN');
|
|
41
|
+
const diagnostics = JSON.parse(await readFile(error.artifactPaths[0], 'utf8'));
|
|
42
|
+
assert.equal(await realpath(diagnostics.runtime.skillRoot), await realpath(target));
|
|
43
|
+
assert.equal(await realpath(diagnostics.options.referenceRoot), await realpath(path.join(target, 'references', 'changelogs')));
|
|
44
|
+
const validation = run([path.join(target, 'scripts', 'maintenance', 'validation', 'validate-skill.mjs')]);
|
|
45
|
+
assert.equal(validation.status, 0, validation.stderr);
|
|
46
|
+
assert.equal(JSON.parse(validation.stdout).valid, true);
|
|
47
|
+
assert.equal(JSON.parse(validation.stdout).evaluations.status, 'SKIPPED');
|
|
48
|
+
const adapterTests = run([path.join(target, 'scripts', 'test-suite.mjs'), 'adapter']);
|
|
49
|
+
assert.equal(adapterTests.status, 0, adapterTests.stderr || adapterTests.stdout);
|
|
50
|
+
const backendTests = run(['--test', '--test-name-pattern=runs the index backend|batch output keeps an empty scan table',
|
|
51
|
+
path.join(target, 'scripts', 'scanner', 'test', 'scan-runner.test.mjs')]);
|
|
52
|
+
assert.equal(backendTests.status, 0, backendTests.stderr || backendTests.stdout);
|
|
53
|
+
});
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import { mkdtemp, readdir, readFile, writeFile, rm } from 'node:fs/promises';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import os from 'node:os';
|
|
6
|
+
import { execute, selectScanRules } from '../core/cli.mjs';
|
|
7
|
+
import { compileRules, loadDevEcoRules } from '../core/rules.mjs';
|
|
8
|
+
import { runDevEcoScan } from '../core/scan-runner.mjs';
|
|
9
|
+
|
|
10
|
+
for (const command of ['scan', 'inspect-rules']) test(`${command} rejects missing and retired modes before any input lookup or output`, async t => {
|
|
11
|
+
const root = await mkdtemp(path.join(os.tmpdir(), 'scan-mode-中文 空格-'));
|
|
12
|
+
t.after(() => rm(root, { recursive: true, force: true }));
|
|
13
|
+
for (const mode of [undefined, 'full', 'deprecated-only', 'unknown', '', true]) {
|
|
14
|
+
const modeArgs = mode === undefined ? [] : mode === true ? ['--mode'] : ['--mode', mode];
|
|
15
|
+
await assert.rejects(execute([command, ...modeArgs, '--project', path.join(root, 'missing-project'),
|
|
16
|
+
'--sdk', path.join(root, 'missing-sdk'), '--output-dir', path.join(root, 'output')]), { code: 'SCAN_MODE_INVALID' });
|
|
17
|
+
assert.deepEqual(await readdir(root), []);
|
|
18
|
+
}
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
test('internal scan entrypoints require an explicit supported mode', async () => {
|
|
22
|
+
for (const mode of [undefined, 'full', 'deprecated-only', 'unknown']) {
|
|
23
|
+
const options = { mode };
|
|
24
|
+
await assert.rejects(compileRules(options), { code: 'SCAN_MODE_INVALID' });
|
|
25
|
+
await assert.rejects(loadDevEcoRules(null, null, null, mode), { code: 'SCAN_MODE_INVALID' });
|
|
26
|
+
await assert.rejects(runDevEcoScan(options), { code: 'SCAN_MODE_INVALID' });
|
|
27
|
+
assert.throws(() => selectScanRules([], mode), { code: 'SCAN_MODE_INVALID' });
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
test('scan execution requires a batch identity and never falls back to root report files', async t => {
|
|
32
|
+
const root = await mkdtemp(path.join(os.tmpdir(), 'scan-batch-required-'));
|
|
33
|
+
t.after(() => rm(root, { recursive: true, force: true }));
|
|
34
|
+
for (const mode of ['upgrade', 'deprecated']) for (const batchId of [undefined, 'invalid']) {
|
|
35
|
+
await assert.rejects(runDevEcoScan({ mode, batchId, outputDirectory: root }), { code: 'SCAN_BATCH_INVALID' });
|
|
36
|
+
assert.deepEqual(await readdir(root), []);
|
|
37
|
+
}
|
|
38
|
+
});
|
|
@@ -0,0 +1,481 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import { mkdtemp, mkdir, readFile, readdir, writeFile, rm, symlink } from 'node:fs/promises';
|
|
4
|
+
import os from 'node:os';
|
|
5
|
+
import path from 'node:path';
|
|
6
|
+
import { DEPRECATED_HEADERS, parseCsv, SCANNER_HEADERS, UPGRADE_HEADERS } from '../../shared/format/csv.mjs';
|
|
7
|
+
import { sha256 } from '../../shared/io/fs-utils.mjs';
|
|
8
|
+
import { createScanBatch } from '../../shared/platform/artifact-layout.mjs';
|
|
9
|
+
import { runScanInBatch } from './helpers/scan-batch.mjs';
|
|
10
|
+
import {
|
|
11
|
+
compareFindings,
|
|
12
|
+
interpretDevEcoOutput,
|
|
13
|
+
normalizeCodeLocation,
|
|
14
|
+
normalizeFinding,
|
|
15
|
+
rulesForProject,
|
|
16
|
+
runDevEcoScan,
|
|
17
|
+
} from '../core/scan-runner.mjs';
|
|
18
|
+
|
|
19
|
+
test('independent deprecated references retain multiple columns and do not inherit compatibility filters', async (t) => {
|
|
20
|
+
const root = await mkdtemp(path.join(os.tmpdir(), 'sdk-report-'));
|
|
21
|
+
t.after(() => rm(root, { recursive: true, force: true }));
|
|
22
|
+
const file = path.join(root, 'entry/A.ets');
|
|
23
|
+
await mkdir(path.dirname(file));
|
|
24
|
+
await writeFile(file, 'old(); old();');
|
|
25
|
+
const project = { root, modules: [{ name: 'entry', root: path.dirname(file), relativeRoot: 'entry' }], sourceFiles: [file], sourceSnapshot: 'snapshot', scanDirs: [{ absolute: root }] };
|
|
26
|
+
const findings = [1, 8].map(Column => ({
|
|
27
|
+
'Api Definition': 'old(): void', Language: 'ArkTS', 'Changed in SDK': '', 'Deprecated Since': 9,
|
|
28
|
+
'Code Location': 'entry/A.ets:1', Column, 'Rule ID': 'sdk-old', 'Change Type': 'Deprecated API',
|
|
29
|
+
'Change Operation': 'API_DEPRECATED', 'API Symbol ID': 'api-old',
|
|
30
|
+
occurrenceId: `old-${Column}`, includedInCsv: true, disposition: 'REQUIRES_REVIEW', filterReason: null,
|
|
31
|
+
}));
|
|
32
|
+
const directory=path.join(root,'20261023123456');await mkdir(directory);
|
|
33
|
+
const layout=await createScanBatch(root,directory,{mode:'deprecated'});
|
|
34
|
+
const options={project,toolchain:{},rules:[],range:{},outputDirectory:directory,
|
|
35
|
+
mode:'deprecated',batchId:layout.batchId,deprecations:{findings,unresolved:[],coverage:{status:'COMPLETE'}}};
|
|
36
|
+
const deprecated=await runDevEcoScan(options);
|
|
37
|
+
assert.equal(deprecated.findings.length,2);
|
|
38
|
+
assert.ok((await readFile(deprecated.validationCsv,'utf8')).includes('Title'));
|
|
39
|
+
const scan=JSON.parse(await readFile(deprecated.scanJson,'utf8'));
|
|
40
|
+
assert.equal(scan.schemaVersion,4);assert.deepEqual(scan.findings.map(item=>item.Column),[1,8]);
|
|
41
|
+
assert.equal(scan.mode,'deprecated');
|
|
42
|
+
assert.equal(deprecated.outputCsv,layout.rawCsv);assert.equal(deprecated.scanJson,layout.scanJson);
|
|
43
|
+
assert.equal(deprecated.validationCsv,layout.validationCsv);
|
|
44
|
+
const csv = parseCsv(await readFile(deprecated.outputCsv,'utf8'));
|
|
45
|
+
assert.deepEqual(csv.headers,DEPRECATED_HEADERS);
|
|
46
|
+
assert.equal(csv.rows.length,2);
|
|
47
|
+
assert.deepEqual(csv.rows[0],csv.rows[1]);
|
|
48
|
+
for (const finding of scan.findings) {
|
|
49
|
+
assert.equal(finding['API Symbol ID'],'api-old');
|
|
50
|
+
assert.equal(finding['Deprecated Since'],9);
|
|
51
|
+
assert.equal(finding.findingId,`finding-${sha256(SCANNER_HEADERS.map(header=>finding[header]).join('\u0000')).slice(0,20)}`);
|
|
52
|
+
}
|
|
53
|
+
assert.equal(JSON.parse(await readFile(layout.scanJson,'utf8')).batchId,layout.batchId);
|
|
54
|
+
assert.deepEqual((await readdir(directory)).sort(),['.internal','deprecated']);
|
|
55
|
+
await writeFile(deprecated.outputCsv, 'previous report');
|
|
56
|
+
await assert.rejects(runDevEcoScan({...options, verifyInputs:async()=>{throw new Error('input changed');}}), /input changed/);
|
|
57
|
+
assert.equal(await readFile(deprecated.outputCsv,'utf8'),'previous report');
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
test('an unresponsive C++ backend is bounded and still publishes the ArkTS report', async (t) => {
|
|
61
|
+
const root = await mkdtemp(path.join(os.tmpdir(), 'cpp-watchdog-'));
|
|
62
|
+
t.after(() => rm(root, { recursive: true, force: true }));
|
|
63
|
+
const moduleRoot = path.join(root, 'entry');
|
|
64
|
+
await mkdir(moduleRoot);
|
|
65
|
+
const cpp = path.join(moduleRoot, 'main.cpp');
|
|
66
|
+
await writeFile(cpp, 'int main() {}');
|
|
67
|
+
const fake = path.join(root, 'hang.mjs');
|
|
68
|
+
await writeFile(fake, 'setInterval(() => {}, 1000);');
|
|
69
|
+
const result = await runScanInBatch('upgrade', {
|
|
70
|
+
toolchain: { apiChangeScan: fake, analyzerRoot: root, sdkRoot: '/sdk', java: process.execPath,
|
|
71
|
+
cApiScanJar: '/fake/c-apiscan.jar' },
|
|
72
|
+
project: { root, sourceFiles: [cpp], modules: [{ name: 'entry', root: moduleRoot, relativeRoot: 'entry' }], scanDirs: [{ absolute: root }], sourceSnapshot: 'snapshot' },
|
|
73
|
+
rules: [{ ruleId: 'cpp', rawEntry: { affectedApis: [{ implementLanguage: 'c', methodDefinition: 'void OH_Run()' }], interfaceChanges: {} } }],
|
|
74
|
+
range: { start: { platformVersion: '5.0.0', apiLevel: 12, channel: 'release' }, end: { platformVersion: '26.0.0', apiLevel: 26, channel: 'release' } },
|
|
75
|
+
outputDirectory: root, cppPassTimeoutMs: 50,
|
|
76
|
+
});
|
|
77
|
+
assert.equal(result.diagnostics.cppStatus, 'TIMED_OUT');
|
|
78
|
+
assert.ok(result.diagnostics.warnings.some(warning => warning.includes('C++扫描超时')));
|
|
79
|
+
assert.equal(result.findings.length, 0);
|
|
80
|
+
assert.ok(await readFile(result.outputCsv, 'utf8'));
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
test('native modules use independent semantic passes so one module cannot consume another module budget', async t => {
|
|
84
|
+
const root = await mkdtemp(path.join(os.tmpdir(), 'cpp-modules-中文 '));
|
|
85
|
+
t.after(() => rm(root, { recursive: true, force: true }));
|
|
86
|
+
const modules = ['first', 'second'].map(name => ({ name, root: path.join(root, name), relativeRoot: name }));
|
|
87
|
+
for (const module of modules) { await mkdir(module.root); await writeFile(path.join(module.root, 'main.cpp'), 'void run() {}'); }
|
|
88
|
+
const calls = path.join(root, 'calls.jsonl'), fake = path.join(root, 'backend.mjs');
|
|
89
|
+
await writeFile(fake, `import {mkdir,writeFile,appendFile} from 'node:fs/promises';
|
|
90
|
+
import path from 'node:path';
|
|
91
|
+
const value = name => process.argv[process.argv.indexOf(name)+1];
|
|
92
|
+
const modules = value('--modulePaths').split(',');
|
|
93
|
+
await appendFile(${JSON.stringify(calls)}, JSON.stringify(modules)+'\\n');
|
|
94
|
+
if (modules.length !== 1) process.exit(3);
|
|
95
|
+
const out=value('--outputPath');await mkdir(out,{recursive:true});
|
|
96
|
+
await writeFile(path.join(out,'result.csv'),'Api Definition,Language,ChangeId,Code Location\\n');`);
|
|
97
|
+
const result = await runScanInBatch('upgrade', { project: {root,modules,sourceFiles:modules.map(m=>path.join(m.root,'main.cpp')),
|
|
98
|
+
scanDirs:[{absolute:root}],sourceSnapshot:'snapshot'},
|
|
99
|
+
toolchain:{apiChangeScan:fake,analyzerRoot:root,sdkRoot:root,cApiScanJar:fake,java:process.execPath},
|
|
100
|
+
rules:[{provider:'DEVECO',ruleId:'native',rawEntry:{affectedApis:[{implementLanguage:'c',methodDefinition:'void run()'}],interfaceChanges:{}}}],
|
|
101
|
+
range:{start:{platformVersion:'6.0.0',apiLevel:20,channel:'release'},end:{platformVersion:'26.0.0',apiLevel:26,channel:'release'}},outputDirectory:root });
|
|
102
|
+
assert.deepEqual((await readFile(calls,'utf8')).trim().split('\n').map(line=>JSON.parse(line)),modules.map(m=>[m.root]));
|
|
103
|
+
assert.deepEqual(result.diagnostics.nativePasses.map(p=>p.modules),[['first'],['second']]);
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
test('normalizes DevEco rows into the scanner CSV contract', () => {
|
|
107
|
+
const finding = normalizeFinding({
|
|
108
|
+
'Api Definition': 'run(): void', Language: 'ArkTS', 'Changed in SDK': '6.0.0(20) Beta1',
|
|
109
|
+
'Affected Versions': 'ALL', Title: 'title', 'Code Location': 'entry\\A.ets:2',
|
|
110
|
+
}, {
|
|
111
|
+
ruleId: 'r1', changedInSdk: '6.0.0(20) Beta1', affectedVersions: 'N/A (compile-time)',
|
|
112
|
+
changeType: 'Deprecated API', changeOperation: 'API_DEPRECATED', sourceReference: 'a.md:2',
|
|
113
|
+
title: 'API废弃', url: 'https://example.test/deprecated',
|
|
114
|
+
replacementApi: 'ohos.arkui.UIContext.UIContext#getHostContext',
|
|
115
|
+
replacementSource: '/sdk/component/common.d.ts:10',
|
|
116
|
+
}, null, 'deprecated');
|
|
117
|
+
assert.equal(finding.Fixed, 'Unfixed');
|
|
118
|
+
assert.equal(finding['Code Location'], 'entry/A.ets:2');
|
|
119
|
+
assert.equal(finding['Rule ID'], 'r1');
|
|
120
|
+
assert.equal(finding['Replacement API'], 'ohos.arkui.UIContext.UIContext#getHostContext');
|
|
121
|
+
assert.equal(finding['Replacement Source'], '/sdk/component/common.d.ts:10');
|
|
122
|
+
assert.equal(finding.includedInCsv, true);
|
|
123
|
+
assert.equal(finding.disposition, 'REQUIRES_REVIEW');
|
|
124
|
+
assert.equal(finding.filterReason, null);
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
test('marks compatible findings for exclusion from the primary CSV', () => {
|
|
128
|
+
const finding = normalizeFinding({
|
|
129
|
+
'Api Definition': 'run(): void', Language: 'ArkTS', 'Code Location': 'entry/A.ets:2',
|
|
130
|
+
}, {
|
|
131
|
+
ruleId: 'compatible', changedInSdk: '6.0.0(20) Beta1', affectedVersions: 'N/A (compile-time)',
|
|
132
|
+
changeType: 'Deprecated API', changeOperation: 'API_DEPRECATED', sourceReference: 'a.md:2',
|
|
133
|
+
title: '兼容变更', url: '', reportDisposition: 'FILTERED_COMPATIBLE', filterReason: 'DOCUMENTED_NO_ADAPTATION',
|
|
134
|
+
}, null, 'deprecated');
|
|
135
|
+
assert.equal(finding.includedInCsv, false);
|
|
136
|
+
assert.equal(finding.disposition, 'FILTERED_COMPATIBLE');
|
|
137
|
+
assert.equal(finding.filterReason, 'DOCUMENTED_NO_ADAPTATION');
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
test('uses a deterministic finding order', () => {
|
|
141
|
+
const base = { 'Changed in SDK': '6.0.0(20)', 'Rule ID': 'r', Language: 'ArkTS', 'Api Definition': 'a' };
|
|
142
|
+
const first = { ...base, 'Change Type': 'API Behavior Change', 'Code Location': 'entry/A.ets:2' };
|
|
143
|
+
const second = { ...base, 'Change Type': 'Deprecated API', 'Code Location': 'entry/A.ets:1' };
|
|
144
|
+
assert.ok(compareFindings(first, second) < 0);
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
test('keeps C++ timeout non-blocking but rejects grep fallback', () => {
|
|
148
|
+
const timeout = interpretDevEcoOutput({
|
|
149
|
+
code: 0,
|
|
150
|
+
stdout: 'CSV saved',
|
|
151
|
+
stderr: 'error[20004] C++ JAR scan timed out, marking as failed\nJava process exited with code 143',
|
|
152
|
+
});
|
|
153
|
+
assert.equal(timeout.cppStatus, 'TIMED_OUT');
|
|
154
|
+
assert.match(timeout.warnings[0], /结果可能不完整/);
|
|
155
|
+
assert.throws(() => interpretDevEcoOutput({
|
|
156
|
+
code: 0,
|
|
157
|
+
stdout: 'Falling back to grep scan',
|
|
158
|
+
stderr: '',
|
|
159
|
+
}), /inaccurate fallback/);
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
test('converts absolute C++ locations inside the project to portable relative paths', () => {
|
|
163
|
+
assert.equal(
|
|
164
|
+
normalizeCodeLocation({ root: '/work/app', modules: [] }, '/work/app/entry/src/main/cpp/main.cpp:42'),
|
|
165
|
+
'entry/src/main/cpp/main.cpp:42',
|
|
166
|
+
);
|
|
167
|
+
assert.equal(
|
|
168
|
+
normalizeCodeLocation({
|
|
169
|
+
root: '/work/app',
|
|
170
|
+
modules: [{ name: 'native', root: '/work/app/components/native' }],
|
|
171
|
+
}, '/work/app/components/native/src/main/cpp/main.cpp:7'),
|
|
172
|
+
'native/src/main/cpp/main.cpp:7',
|
|
173
|
+
);
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
test('does not send C rules to DevEco for a pure ArkTS scan scope', () => {
|
|
177
|
+
const rules = [{ rawEntry: { affectedApis: [
|
|
178
|
+
{ implementLanguage: 'typeScript', interfaceName: 'run' },
|
|
179
|
+
{ implementLanguage: 'c', interfaceName: 'OH_Run' },
|
|
180
|
+
] } }];
|
|
181
|
+
const selected = rulesForProject(rules, { sourceFiles: ['/work/A.ets'] });
|
|
182
|
+
assert.equal(selected.hasCpp, false);
|
|
183
|
+
assert.deepEqual(selected.entries[0].affectedApis.map((item) => item.interfaceName), ['run']);
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
test('groups duplicate affected APIs under one scan id and preserves all owning rules', () => {
|
|
187
|
+
const api = {
|
|
188
|
+
implementLanguage: 'typeScript', completePath: '/sdk/a.d.ts', className: 'A',
|
|
189
|
+
interfaceType: 'method', interfaceName: 'run', methodDefinition: 'run(): void',
|
|
190
|
+
};
|
|
191
|
+
const rules = ['behavior', 'deprecated'].map((id, index) => ({
|
|
192
|
+
ruleId: id,
|
|
193
|
+
rawEntry: {
|
|
194
|
+
affectedApis: [{ ...api, methodDefinition: `${api.methodDefinition}${index ? ';' : ''}` }],
|
|
195
|
+
interfaceChanges: { id },
|
|
196
|
+
},
|
|
197
|
+
}));
|
|
198
|
+
const selected = rulesForProject(rules, { sourceFiles: ['/work/A.ets'] });
|
|
199
|
+
assert.equal(selected.arkTsEntries.length, 1);
|
|
200
|
+
const scanId = selected.arkTsEntries[0].interfaceChanges.id;
|
|
201
|
+
assert.deepEqual(selected.scanRuleMap.get(scanId).rules.map((rule) => rule.ruleId), ['behavior', 'deprecated']);
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
for (const prefix of ['scanner-timeout-project-', "scanner-timeout-中文 空格'路径-"]) {
|
|
205
|
+
test(`generates a report when the DevEco C++ backend times out${prefix.includes('中文') ? ' with a Chinese, spaced and quoted path' : ''}`, async (t) => {
|
|
206
|
+
const root = await mkdtemp(path.join(os.tmpdir(), prefix));
|
|
207
|
+
t.after(() => rm(root, { recursive: true, force: true }));
|
|
208
|
+
const moduleRoot = path.join(root, 'entry');
|
|
209
|
+
const output = path.join(root, 'output');
|
|
210
|
+
await mkdir(moduleRoot, { recursive: true });
|
|
211
|
+
await mkdir(output, { recursive: true });
|
|
212
|
+
await writeFile(path.join(moduleRoot, 'A.ets'), 'run();\n');
|
|
213
|
+
await writeFile(path.join(moduleRoot, 'main.cpp'), 'int main() { return 0; }\n');
|
|
214
|
+
const fake = path.join(root, 'fake-scan.mjs');
|
|
215
|
+
await writeFile(fake, `
|
|
216
|
+
import { mkdir, readFile, writeFile } from 'node:fs/promises';
|
|
217
|
+
import path from 'node:path';
|
|
218
|
+
const value = (name) => process.argv[process.argv.indexOf(name) + 1];
|
|
219
|
+
if (value('--startVersion') !== 'start-from-resource' || value('--endVersion') !== 'end-from-resource') process.exit(2);
|
|
220
|
+
const out = value('--outputPath');
|
|
221
|
+
const changes = JSON.parse(await readFile(value('--apiModifiedPath'), 'utf8'));
|
|
222
|
+
const change = changes[0];
|
|
223
|
+
const api = change.affectedApis[0];
|
|
224
|
+
const language = api.implementLanguage === 'c' ? 'C' : 'ArkTS';
|
|
225
|
+
const location = language === 'C' ? ${JSON.stringify(`${path.join(moduleRoot, 'main.cpp')}:1`)} : 'entry/A.ets:1';
|
|
226
|
+
await mkdir(out, { recursive: true });
|
|
227
|
+
await writeFile(path.join(out, 'result.csv'), '\\ufeffApi Definition,Language,ChangeId,Changed in SDK,Affected Versions,Title,Code Location,Change Type\\r\\n' + api.methodDefinition + ',' + language + ',' + change.interfaceChanges.id + ',5.0.1(13) Release,ALL,title,' + location + ',接口行为变更\\r\\n');
|
|
228
|
+
console.error('error[20004] C++ JAR scan timed out, marking as failed');
|
|
229
|
+
`);
|
|
230
|
+
const rule = {
|
|
231
|
+
ruleId: 'r1', changedInSdk: '5.0.1(13) Release', affectedVersions: 'ALL',
|
|
232
|
+
changeType: 'API Behavior Change', changeOperation: 'API_BEHAVIOR', sourceReference: 'a.md:1',
|
|
233
|
+
title: '行为变更', url: 'https://example.test/behavior',
|
|
234
|
+
rawEntry: { affectedApis: [
|
|
235
|
+
{ implementLanguage: 'typeScript', methodDefinition: 'run(): void' },
|
|
236
|
+
{ implementLanguage: 'c', methodDefinition: 'int OH_Run(void)' },
|
|
237
|
+
] },
|
|
238
|
+
};
|
|
239
|
+
const result = await runScanInBatch('upgrade', {
|
|
240
|
+
toolchain: {
|
|
241
|
+
apiChangeScan: fake, analyzerRoot: root, cApiScanJar: path.join(root, 'fake', 'c.jar'), sdkRoot: path.join(root, 'fake', 'sdk'),
|
|
242
|
+
contentsRoot: root, java: path.join(root, 'jbr', 'bin', 'java'),
|
|
243
|
+
},
|
|
244
|
+
project: {
|
|
245
|
+
root,
|
|
246
|
+
modules: [{ name: 'entry', root: moduleRoot, relativeRoot: 'entry' }],
|
|
247
|
+
selectedModules: [{ name: 'entry', root: moduleRoot, relativeRoot: 'entry' }],
|
|
248
|
+
scanDirs: [{ absolute: root, relative: '.' }],
|
|
249
|
+
sourceFiles: [path.join(moduleRoot, 'A.ets'), path.join(moduleRoot, 'main.cpp')],
|
|
250
|
+
sourceSnapshot: 'snapshot',
|
|
251
|
+
},
|
|
252
|
+
range: {
|
|
253
|
+
start: { platformVersion: '5.0.0', apiLevel: 12, channel: 'release', channelNumber: 0 },
|
|
254
|
+
end: { platformVersion: '5.0.1', apiLevel: 13, channel: 'release', channelNumber: 0 },
|
|
255
|
+
},
|
|
256
|
+
rules: [rule],
|
|
257
|
+
devecoVersionNames: {
|
|
258
|
+
'5.0.0(12) Release': 'start-from-resource',
|
|
259
|
+
'5.0.1(13) Release': 'end-from-resource',
|
|
260
|
+
},
|
|
261
|
+
outputDirectory: output,
|
|
262
|
+
});
|
|
263
|
+
assert.equal(result.findings.length, 2);
|
|
264
|
+
const locations = findings => findings.map(finding => [finding.Language, finding['Code Location']]).sort();
|
|
265
|
+
const expected = [['ArkTS', 'entry/A.ets:1'], ['C', 'entry/main.cpp:1']];
|
|
266
|
+
assert.deepEqual(locations(result.findings), expected);
|
|
267
|
+
assert.deepEqual(locations(parseCsv(await readFile(result.outputCsv, 'utf8')).rows), expected);
|
|
268
|
+
assert.deepEqual(locations(JSON.parse(await readFile(result.scanJson, 'utf8')).findings), expected);
|
|
269
|
+
assert.equal(result.diagnostics.cppStatus, 'TIMED_OUT');
|
|
270
|
+
assert.match(result.diagnostics.warnings[0], /需人工复核/);
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
test('upgrade retains every plugin occurrence without compatibility filtering', async () => {
|
|
275
|
+
const root = await mkdtemp(path.join(os.tmpdir(), 'scanner-filter-project-'));
|
|
276
|
+
const moduleRoot = path.join(root, 'entry');
|
|
277
|
+
const output = path.join(root, 'output');
|
|
278
|
+
await mkdir(moduleRoot, { recursive: true });
|
|
279
|
+
await mkdir(output, { recursive: true });
|
|
280
|
+
await writeFile(path.join(moduleRoot, 'A.ets'), 'run();\n');
|
|
281
|
+
const fake = path.join(root, 'fake-scan.mjs');
|
|
282
|
+
await writeFile(fake, `
|
|
283
|
+
import { mkdir, readFile, writeFile } from 'node:fs/promises';
|
|
284
|
+
import path from 'node:path';
|
|
285
|
+
const value = (name) => process.argv[process.argv.indexOf(name) + 1];
|
|
286
|
+
const out = value('--outputPath');
|
|
287
|
+
const changes = JSON.parse(await readFile(value('--apiModifiedPath'), 'utf8'));
|
|
288
|
+
await mkdir(out, { recursive: true });
|
|
289
|
+
const rows = changes.map((change) => {
|
|
290
|
+
const api = change.affectedApis[0];
|
|
291
|
+
return [api.methodDefinition, 'ArkTS', change.interfaceChanges.id, '6.0.0(20) Beta1', 'ALL', 'title', 'entry/A.ets:1', '接口定义变更'].join(',');
|
|
292
|
+
});
|
|
293
|
+
await writeFile(path.join(out, 'result.csv'), '\\ufeffApi Definition,Language,ChangeId,Changed in SDK,Affected Versions,Title,Code Location,Change Type\\r\\n' + rows.join('\\r\\n') + '\\r\\n');
|
|
294
|
+
`);
|
|
295
|
+
const api = {
|
|
296
|
+
implementLanguage: 'typeScript', completePath: '/sdk/a.d.ts', className: 'A',
|
|
297
|
+
interfaceType: 'method', interfaceName: 'run', methodDefinition: 'run(): void',
|
|
298
|
+
};
|
|
299
|
+
const makeRule = (ruleId, reportDisposition, filterReason, applicabilityProof = null) => ({
|
|
300
|
+
provider: 'DEVECO', ruleId, changedInSdk: '6.0.0(20) Beta1', affectedVersions: 'ALL',
|
|
301
|
+
changeType: 'API Behavior Change', changeOperation: 'API_BEHAVIOR', sourceReference: `${ruleId}.md:1`,
|
|
302
|
+
title: ruleId, url: '', reportDisposition, filterReason, applicabilityProof,
|
|
303
|
+
rawEntry: { affectedApis: [api], interfaceChanges: { id: ruleId } },
|
|
304
|
+
});
|
|
305
|
+
const options = {
|
|
306
|
+
toolchain: {
|
|
307
|
+
apiChangeScan: fake, analyzerRoot: root, cApiScanJar: '/fake/c.jar', sdkRoot: '/fake/sdk',
|
|
308
|
+
contentsRoot: root, java: path.join(root, 'jbr', 'bin', 'java'),
|
|
309
|
+
},
|
|
310
|
+
project: {
|
|
311
|
+
root,
|
|
312
|
+
modules: [{ name: 'entry', root: moduleRoot, relativeRoot: 'entry' }],
|
|
313
|
+
selectedModules: [{ name: 'entry', root: moduleRoot, relativeRoot: 'entry' }],
|
|
314
|
+
scanDirs: [{ absolute: root, relative: '.' }],
|
|
315
|
+
sourceFiles: [path.join(moduleRoot, 'A.ets')],
|
|
316
|
+
sourceSnapshot: 'snapshot',
|
|
317
|
+
},
|
|
318
|
+
range: {
|
|
319
|
+
start: { platformVersion: '5.0.0', apiLevel: 12, channel: 'release', channelNumber: 0 },
|
|
320
|
+
end: { platformVersion: '6.0.0', apiLevel: 20, channel: 'beta', channelNumber: 1 },
|
|
321
|
+
},
|
|
322
|
+
rules: [
|
|
323
|
+
makeRule('review', 'REQUIRES_REVIEW', null),
|
|
324
|
+
makeRule('compatible', 'FILTERED_COMPATIBLE', 'DOCUMENTED_NO_ADAPTATION', {
|
|
325
|
+
kind: 'DOCUMENTED_NO_ADAPTATION', sourceReference: 'compatible.md:1',
|
|
326
|
+
sectionField: '适配指导', statement: '默认行为变更,无需适配。',
|
|
327
|
+
}),
|
|
328
|
+
],
|
|
329
|
+
outputDirectory: output,
|
|
330
|
+
};
|
|
331
|
+
const result = await runScanInBatch('upgrade', options);
|
|
332
|
+
assert.equal(result.validationCsv,null);
|
|
333
|
+
const csv = parseCsv(await readFile(result.outputCsv,'utf8'));
|
|
334
|
+
assert.deepEqual(csv.headers,UPGRADE_HEADERS);
|
|
335
|
+
assert.equal(csv.rows.length,2);
|
|
336
|
+
const scan=JSON.parse(await readFile(result.scanJson,'utf8'));
|
|
337
|
+
assert.equal(scan.findings.length,2);assert.equal(scan.filteredFindingCount,0);
|
|
338
|
+
assert.ok(scan.findings.every(finding=>finding.includedInCsv && finding.disposition==='REQUIRES_REVIEW'));
|
|
339
|
+
assert.equal(path.basename(path.dirname(result.scanJson)),'scanner');
|
|
340
|
+
});
|
|
341
|
+
|
|
342
|
+
test('runs the index backend without placing the project file list on the command line', async () => {
|
|
343
|
+
const root = await mkdtemp(path.join(os.tmpdir(), 'index-backend-project-'));
|
|
344
|
+
const moduleRoot = path.join(root, 'entry');
|
|
345
|
+
const output = path.join(root, 'output');
|
|
346
|
+
const source = path.join(moduleRoot, 'A.ets');
|
|
347
|
+
await mkdir(moduleRoot, { recursive: true });
|
|
348
|
+
await mkdir(output);
|
|
349
|
+
await writeFile(source, 'run();\n');
|
|
350
|
+
const fake = path.join(root, 'renamed-index.mjs');
|
|
351
|
+
await writeFile(path.join(root, 'worker.js'), `
|
|
352
|
+
const { parentPort } = require('node:worker_threads');
|
|
353
|
+
parentPort.postMessage('ready');
|
|
354
|
+
`);
|
|
355
|
+
await writeFile(fake, `
|
|
356
|
+
import { mkdir, readFile, writeFile } from 'node:fs/promises';
|
|
357
|
+
import { Worker } from 'node:worker_threads';
|
|
358
|
+
import { fileURLToPath } from 'node:url';
|
|
359
|
+
import path from 'node:path';
|
|
360
|
+
const value = (name) => process.argv[process.argv.indexOf(name) + 1];
|
|
361
|
+
if (process.argv.includes('--scanFilePaths')) throw new Error('source paths must stay out of the command line');
|
|
362
|
+
const worker = new Worker(path.join(path.dirname(fileURLToPath(import.meta.url)), 'ApiScan.worker.ts'));
|
|
363
|
+
await new Promise((resolve, reject) => {
|
|
364
|
+
worker.once('message', resolve);
|
|
365
|
+
worker.once('error', reject);
|
|
366
|
+
});
|
|
367
|
+
const out = value('--outPath');
|
|
368
|
+
const config = JSON.parse(await readFile(value('--configPath'), 'utf8'));
|
|
369
|
+
if (!config.projectPath || !Array.isArray(config.sdks) || config.sdks.length !== 2) throw new Error('invalid scanner config');
|
|
370
|
+
const changes = JSON.parse(await readFile(value('--apiModifiedPath'), 'utf8'));
|
|
371
|
+
await mkdir(out, { recursive: true });
|
|
372
|
+
const header = 'Api Name,Api Type,Api File Path,Api Prefix,Api Params,API Args,Invoke Method,Invoke Location,Api Key,Invoke Instance,ChangeId,MethodDefinition\\r\\n';
|
|
373
|
+
const row = (location) => 'run,method,a.d.ts,A,,,run,' + location + ',key,,"' + changes[0].interfaceChanges.id + '","run(): void"\\r\\n';
|
|
374
|
+
await writeFile(path.join(out, 'raw.csv'), header + row('entry/A.ets:1:1') + row('entry/Outside.ets:1:1'));
|
|
375
|
+
`);
|
|
376
|
+
const api = {
|
|
377
|
+
implementLanguage: 'typeScript', completePath: '/sdk/a.d.ts', className: 'A',
|
|
378
|
+
interfaceType: 'method', interfaceName: 'run', methodDefinition: 'run(): void',
|
|
379
|
+
};
|
|
380
|
+
const rule = {
|
|
381
|
+
provider: 'DEVECO', ruleId: 'index-rule', changedInSdk: '6.0.0(20) Beta1', affectedVersions: 'ALL',
|
|
382
|
+
changeType: 'API Behavior Change', changeOperation: 'API_BEHAVIOR', sourceReference: 'a.md:1',
|
|
383
|
+
title: 'index backend rule', url: '', rawEntry: { affectedApis: [api], interfaceChanges: { id: 'source-id' } },
|
|
384
|
+
};
|
|
385
|
+
const result = await runScanInBatch('upgrade', {
|
|
386
|
+
toolchain: {
|
|
387
|
+
apiChangeBackend: 'index', apiChangeScan: fake, analyzerRoot: root, sdkRoot: '/fake/sdk',
|
|
388
|
+
sdkFamilies: { openharmony: '/fake/sdk/default/openharmony/ets', hms: '/fake/sdk/default/hms/ets' },
|
|
389
|
+
},
|
|
390
|
+
project: {
|
|
391
|
+
root, modules: [{ name: 'entry', root: moduleRoot, relativeRoot: 'entry' }],
|
|
392
|
+
selectedModules: [{ name: 'entry', root: moduleRoot, relativeRoot: 'entry' }],
|
|
393
|
+
scanDirs: [{ absolute: root, relative: '.' }],
|
|
394
|
+
sourceFiles: [source, ...Array.from({ length: 10_000 }, (_, index) => path.join(
|
|
395
|
+
moduleRoot, 'generated', 'long-source-directory', `File${index}.ets`,
|
|
396
|
+
))],
|
|
397
|
+
sourceSnapshot: 'snapshot',
|
|
398
|
+
},
|
|
399
|
+
range: {
|
|
400
|
+
start: { platformVersion: '5.0.0', apiLevel: 12, channel: 'release', channelNumber: 0 },
|
|
401
|
+
end: { platformVersion: '6.0.0', apiLevel: 20, channel: 'beta', channelNumber: 1 },
|
|
402
|
+
},
|
|
403
|
+
rules: [rule], outputDirectory: output,
|
|
404
|
+
});
|
|
405
|
+
assert.equal(result.findings.length, 1);
|
|
406
|
+
assert.equal(result.findings[0]['Rule ID'], 'index-rule');
|
|
407
|
+
assert.equal(result.findings[0]['Code Location'], 'entry/A.ets:1');
|
|
408
|
+
assert.equal(result.diagnostics.backend, 'index');
|
|
409
|
+
});
|
|
410
|
+
|
|
411
|
+
test('reports an explicit capability error when the index backend is asked to scan C++', async () => {
|
|
412
|
+
const root = await mkdtemp(path.join(os.tmpdir(), 'index-cpp-backend-'));
|
|
413
|
+
const moduleRoot = path.join(root, 'entry');
|
|
414
|
+
const source = path.join(moduleRoot, 'main.cpp');
|
|
415
|
+
await mkdir(moduleRoot);
|
|
416
|
+
await writeFile(source, 'int main() { return 0; }\n');
|
|
417
|
+
await assert.rejects(runScanInBatch('upgrade', {
|
|
418
|
+
toolchain: { apiChangeBackend: 'index', apiChangeScan: '/fake/index.js', layoutDiagnostics: { scannerCandidates: [] } },
|
|
419
|
+
project: {
|
|
420
|
+
root, modules: [{ name: 'entry', root: moduleRoot, relativeRoot: 'entry' }],
|
|
421
|
+
selectedModules: [{ name: 'entry', root: moduleRoot, relativeRoot: 'entry' }],
|
|
422
|
+
scanDirs: [{ absolute: root, relative: '.' }], sourceFiles: [source], sourceSnapshot: 'snapshot',
|
|
423
|
+
},
|
|
424
|
+
range: {}, outputDirectory: root,
|
|
425
|
+
rules: [{ provider: 'DEVECO', ruleId: 'cpp', rawEntry: { affectedApis: [
|
|
426
|
+
{ implementLanguage: 'c', methodDefinition: 'int OH_Run(void)' },
|
|
427
|
+
], interfaceChanges: {} } }],
|
|
428
|
+
}), { code: 'C_CPP_BACKEND_UNAVAILABLE' });
|
|
429
|
+
});
|
|
430
|
+
|
|
431
|
+
test('batch output keeps an empty scan table and its JSON bound to the same ID',async t=>{
|
|
432
|
+
const root=await mkdtemp(path.join(os.tmpdir(),'batch-scan-'));
|
|
433
|
+
t.after(()=>rm(root,{recursive:true,force:true}));
|
|
434
|
+
const batchId='abcdef012345';
|
|
435
|
+
const result=await runScanInBatch('upgrade', {project:{root,modules:[],sourceFiles:[],sourceSnapshot:'empty',scanDirs:[]},
|
|
436
|
+
toolchain:{},rules:[],range:{},outputDirectory:root,batchId,mode:'upgrade'});
|
|
437
|
+
assert.equal(path.basename(result.outputCsv),`scan-results-${batchId}.csv`);
|
|
438
|
+
assert.equal(result.outputCsv,path.join(root,'upgrade',`scan-results-${batchId}.csv`));
|
|
439
|
+
assert.equal(result.validationCsv,null);
|
|
440
|
+
assert.equal(result.scanJson,path.join(root,'.internal','scanner',`scan-results-${batchId}.scan.json`));
|
|
441
|
+
await assert.rejects(readFile(path.join(root,`scan-results-${batchId}.validation.csv`)),{code:'ENOENT'});
|
|
442
|
+
assert.equal(JSON.parse(await readFile(result.scanJson,'utf8')).batchId,batchId);
|
|
443
|
+
assert.ok((await readFile(result.outputCsv,'utf8')).includes('Api Definition'));
|
|
444
|
+
assert.deepEqual(parseCsv(await readFile(result.outputCsv,'utf8')), {headers:UPGRADE_HEADERS,rows:[]});
|
|
445
|
+
assert.deepEqual((await readdir(root)).sort(),['.internal','upgrade']);
|
|
446
|
+
});
|
|
447
|
+
|
|
448
|
+
test('empty deprecated scans still publish the eight-column table and complete JSON', async t => {
|
|
449
|
+
const root = await mkdtemp(path.join(os.tmpdir(), 'empty-deprecated-'));
|
|
450
|
+
t.after(() => rm(root, { recursive: true, force: true }));
|
|
451
|
+
const result = await runScanInBatch('deprecated', {
|
|
452
|
+
project: { root, modules: [], sourceFiles: [], sourceSnapshot: 'empty', scanDirs: [] },
|
|
453
|
+
toolchain: {}, rules: [], range: {}, outputDirectory: root,
|
|
454
|
+
deprecations: { findings: [], unresolved: [], coverage: { status: 'COMPLETE' } },
|
|
455
|
+
});
|
|
456
|
+
assert.deepEqual(parseCsv(await readFile(result.outputCsv, 'utf8')), { headers: DEPRECATED_HEADERS, rows: [] });
|
|
457
|
+
const scan = JSON.parse(await readFile(result.scanJson, 'utf8'));
|
|
458
|
+
assert.equal(scan.schemaVersion, 4);
|
|
459
|
+
assert.deepEqual(scan.findings, []);
|
|
460
|
+
assert.deepEqual(scan.actionGroups, []);
|
|
461
|
+
});
|
|
462
|
+
|
|
463
|
+
for (const mode of ['upgrade', 'deprecated']) test(`${mode} rechecks the feature directory before publishing scan bytes`, async t => {
|
|
464
|
+
const root = await mkdtemp(path.join(os.tmpdir(), 'scanner-delivery-'));
|
|
465
|
+
const outside = await mkdtemp(path.join(os.tmpdir(), 'scanner-outside-'));
|
|
466
|
+
t.after(() => rm(root, { recursive: true, force: true }));
|
|
467
|
+
t.after(() => rm(outside, { recursive: true, force: true }));
|
|
468
|
+
const directory = path.join(root, '20261027172821');
|
|
469
|
+
await mkdir(directory);
|
|
470
|
+
const layout = await createScanBatch(root, directory, { mode });
|
|
471
|
+
await assert.rejects(runScanInBatch(mode, {
|
|
472
|
+
project: { root, modules: [], sourceFiles: [], sourceSnapshot: 'empty', scanDirs: [] },
|
|
473
|
+
toolchain: {}, rules: [], range: {}, outputDirectory: directory, batchId: layout.batchId,
|
|
474
|
+
verifyInputs: async () => {
|
|
475
|
+
await rm(layout.deliveryDirectory, { recursive: true });
|
|
476
|
+
await symlink(outside, layout.deliveryDirectory, process.platform === 'win32' ? 'junction' : 'dir');
|
|
477
|
+
},
|
|
478
|
+
}), { code: 'SCAN_BATCH_INVALID' });
|
|
479
|
+
assert.deepEqual(await readdir(outside), []);
|
|
480
|
+
await assert.rejects(readFile(layout.scanJson), { code: 'ENOENT' });
|
|
481
|
+
});
|