@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.
Files changed (149) hide show
  1. package/node_modules/@hmos-library-adapter/executer/README.md +2 -2
  2. package/node_modules/@hmos-library-adapter/executer/shared/skill-map.json +5 -1
  3. package/node_modules/@hmos-library-adapter/executer/shared/skills/arkts-rules/SKILL.md +400 -0
  4. package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/CHANGELOG.md +1391 -0
  5. package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/COMPATIBILITY.md +53 -0
  6. package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/README.md +123 -0
  7. package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/SKILL.md +48 -0
  8. package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/agents/upgrade-assess-change.md +37 -0
  9. package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/agents/upgrade-fix-file.md +23 -0
  10. package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/package.json +19 -0
  11. 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
  12. 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
  13. 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
  14. 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
  15. 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
  16. 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
  17. 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
  18. 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
  19. 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
  20. 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
  21. 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
  22. 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
  23. 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
  24. 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
  25. 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
  26. 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
  27. 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
  28. 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
  29. 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
  30. 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
  31. 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
  32. 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
  33. 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
  34. 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
  35. package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/references/changelogs-index.json +883 -0
  36. package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/references/common/arkts-coding-standards.md +53 -0
  37. package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/references/common/compatibility.md +207 -0
  38. package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/references/common/native-adaptation.md +21 -0
  39. package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/references/common/runtime-functional-tests.md +62 -0
  40. package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/references/common/sdk-versions.md +77 -0
  41. package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/references/common/troubleshooting.md +54 -0
  42. package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/references/common/verification.md +79 -0
  43. package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/references/common/workflow-scan.md +41 -0
  44. package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/references/deprecated/context-migration.md +274 -0
  45. package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/references/deprecated/deprecated-api-adaptation.md +157 -0
  46. package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/references/deprecated/deprecated-api-examples.md +219 -0
  47. package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/references/toolchain/hvigor.md +50 -0
  48. package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/references/upgrade/har-upgrade.md +60 -0
  49. package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/references/upgrade/scanner-missed-cases.md +171 -0
  50. package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/references/upgrade/upgrade-compatibility.md +112 -0
  51. package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/references/upgrade/upgrade-examples.md +289 -0
  52. package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/references/upgrade/upgrade-workflow.md +122 -0
  53. package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/scripts/maintenance/references/generate-behavior-policy.mjs +96 -0
  54. package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/scripts/maintenance/references/references.mjs +9 -0
  55. package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/scripts/maintenance/regression/compare-deprecated.mjs +57 -0
  56. package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/scripts/maintenance/regression/compare-golden.mjs +130 -0
  57. package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/scripts/maintenance/regression/regress-deprecated.mjs +120 -0
  58. package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/scripts/maintenance/test/behavior-catalog-generation.test.mjs +59 -0
  59. package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/scripts/maintenance/test/behavior-local-sources.test.mjs +44 -0
  60. package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/scripts/maintenance/test/changelog-index.test.mjs +66 -0
  61. package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/scripts/maintenance/test/compare-golden.test.mjs +103 -0
  62. package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/scripts/maintenance/test/fixtures/behavior/local-sources.csv +5 -0
  63. package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/scripts/maintenance/test/references.test.mjs +16 -0
  64. package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/scripts/maintenance/test/skill-content.test.mjs +139 -0
  65. package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/scripts/maintenance/test/skill-frontmatter.test.mjs +11 -0
  66. package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/scripts/maintenance/test/test-suite.test.mjs +65 -0
  67. package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/scripts/maintenance/validation/skill-content.mjs +125 -0
  68. package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/scripts/maintenance/validation/skill-frontmatter.mjs +6 -0
  69. package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/scripts/maintenance/validation/validate-skill.mjs +57 -0
  70. package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/scripts/scanner/CHANGELOG.md +506 -0
  71. package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/scripts/scanner/GOLDEN_VALIDATION.md +42 -0
  72. package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/scripts/scanner/README.md +137 -0
  73. package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/scripts/scanner/behavior/behavior-policy.mjs +117 -0
  74. package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/scripts/scanner/behavior/upgrade-coverage.mjs +29 -0
  75. package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/scripts/scanner/bin/deveco-worker-compat.cjs +21 -0
  76. package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/scripts/scanner/bin/harmony-api-scan.mjs +17 -0
  77. package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/scripts/scanner/core/cli.mjs +416 -0
  78. package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/scripts/scanner/core/deveco.mjs +348 -0
  79. package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/scripts/scanner/core/project.mjs +81 -0
  80. package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/scripts/scanner/core/rules.mjs +425 -0
  81. package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/scripts/scanner/core/runtime-identity.mjs +23 -0
  82. package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/scripts/scanner/core/scan-runner.mjs +521 -0
  83. package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/scripts/scanner/core/sdk-deprecations.mjs +204 -0
  84. package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/scripts/scanner/data/behavior-change-policy.json +4378 -0
  85. package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/scripts/scanner/deprecated/arkts-deprecations.mjs +363 -0
  86. package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/scripts/scanner/deprecated/build-log.mjs +72 -0
  87. package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/scripts/scanner/native/native.mjs +95 -0
  88. package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/scripts/scanner/package.json +16 -0
  89. package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/scripts/scanner/test/batch-layout.test.mjs +94 -0
  90. package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/scripts/scanner/test/behavior-policy.test.mjs +75 -0
  91. package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/scripts/scanner/test/build-log.test.mjs +69 -0
  92. package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/scripts/scanner/test/cli.test.mjs +102 -0
  93. package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/scripts/scanner/test/csv.test.mjs +35 -0
  94. package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/scripts/scanner/test/deprecations.test.mjs +283 -0
  95. package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/scripts/scanner/test/deveco.test.mjs +262 -0
  96. package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/scripts/scanner/test/helpers/scan-batch.mjs +10 -0
  97. package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/scripts/scanner/test/native.test.mjs +108 -0
  98. package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/scripts/scanner/test/project.test.mjs +42 -0
  99. package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/scripts/scanner/test/rules.test.mjs +190 -0
  100. package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/scripts/scanner/test/runtime-identity.test.mjs +53 -0
  101. package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/scripts/scanner/test/scan-mode.test.mjs +38 -0
  102. package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/scripts/scanner/test/scan-runner.test.mjs +481 -0
  103. package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/scripts/scanner/test/scan-scope.test.mjs +110 -0
  104. package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/scripts/scanner/test/source-inputs.test.mjs +64 -0
  105. package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/scripts/scanner/test/upgrade.test.mjs +164 -0
  106. package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/scripts/shared/format/args.mjs +66 -0
  107. package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/scripts/shared/format/csv.mjs +85 -0
  108. package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/scripts/shared/format/json5.mjs +92 -0
  109. package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/scripts/shared/format/upgrade-results.mjs +45 -0
  110. package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/scripts/shared/format/zip.mjs +87 -0
  111. package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/scripts/shared/io/fs-utils.mjs +60 -0
  112. package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/scripts/shared/platform/artifact-layout.mjs +74 -0
  113. package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/scripts/shared/platform/changelog-index.mjs +29 -0
  114. package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/scripts/shared/platform/deveco-version.mjs +40 -0
  115. package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/scripts/shared/platform/document-links.mjs +30 -0
  116. package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/scripts/shared/platform/errors.mjs +41 -0
  117. package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/scripts/shared/platform/fs.mjs +72 -0
  118. package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/scripts/shared/platform/paths.mjs +34 -0
  119. package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/scripts/shared/platform/process.mjs +154 -0
  120. package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/scripts/shared/platform/scan-mode.mjs +7 -0
  121. package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/scripts/shared/platform/scan-statistics.mjs +7 -0
  122. package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/scripts/shared/platform/scan-version-catalog.json +43 -0
  123. package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/scripts/shared/platform/scan-versions.mjs +51 -0
  124. package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/scripts/shared/platform/sdk-layout.mjs +27 -0
  125. package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/scripts/shared/platform/source-scope.mjs +108 -0
  126. package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/scripts/shared/platform/source-types.mjs +7 -0
  127. package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/scripts/shared/platform/version.mjs +76 -0
  128. package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/scripts/shared/test/deveco-version.test.mjs +50 -0
  129. package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/scripts/shared/test/document-links.test.mjs +13 -0
  130. package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/scripts/shared/test/errors.test.mjs +40 -0
  131. package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/scripts/shared/test/fs-utils.test.mjs +31 -0
  132. package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/scripts/shared/test/platform-paths.test.mjs +19 -0
  133. package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/scripts/shared/test/process-utils.test.mjs +68 -0
  134. package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/scripts/shared/test/scan-versions.test.mjs +26 -0
  135. package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/scripts/shared/test/sdk-version.test.mjs +17 -0
  136. package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/scripts/shared/test/upgrade-results.test.mjs +140 -0
  137. package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/scripts/test-suite.mjs +29 -0
  138. package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/templates/deprecated-report.md +86 -0
  139. package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/templates/progress.md +150 -0
  140. package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/templates/results.md +110 -0
  141. package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/templates/upgrade-report.md +105 -0
  142. package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/templates/upgrade-spec.md +58 -0
  143. package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-api-upgrade-adapter/templates/upgrade-test-report.md +47 -0
  144. package/node_modules/@hmos-library-adapter/executer/shared/skills/harmonyos-knowledge/SKILL.md +70 -0
  145. package/node_modules/@hmos-library-adapter/executer/src/config.js +3 -4
  146. package/node_modules/@hmos-library-adapter/executer/src/doctor.js +1 -1
  147. package/node_modules/@hmos-library-adapter/executer/src/install.js +1 -1
  148. package/node_modules/@hmos-library-adapter/executer/src/paths.js +4 -4
  149. package/package.json +1 -1
@@ -0,0 +1,110 @@
1
+ import { runScanInBatch } from './helpers/scan-batch.mjs';
2
+ import test from 'node:test';
3
+ import assert from 'node:assert/strict';
4
+ import { mkdtemp, mkdir, readFile, writeFile } from 'node:fs/promises';
5
+ import path from 'node:path';
6
+ import os from 'node:os';
7
+ import { loadProject } from '../core/project.mjs';
8
+ import { cleanupScanDirectory } from '../core/scan-runner.mjs';
9
+ import { serializeError } from '../../shared/platform/errors.mjs';
10
+
11
+ async function setup(backend, directories) {
12
+ const root=await mkdtemp(path.join(os.tmpdir(),'backend-scope-'));
13
+ await writeFile(path.join(root,'build-profile.json5'),JSON.stringify({app:{products:[{name:'default'}]},
14
+ modules:['entry','library'].map(name=>({name,srcPath:name}))}));
15
+ for(const name of ['entry','library']) {
16
+ await mkdir(path.join(root,name,'src/main/ets/keep'),{recursive:true});
17
+ await writeFile(path.join(root,name,'src/main/module.json5'),JSON.stringify({module:{type:name==='library'?'har':'entry'}}));
18
+ await writeFile(path.join(root,name,'src/main/ets/keep/A.ts'),'api(1);');
19
+ await writeFile(path.join(root,name,'src/main/ets/Outside.ts'),'api(1);');
20
+ }
21
+ const project=await loadProject(root,'default',directories?['library/src/main/ets/keep']:[],['library'],'har');
22
+ const fake=path.join(root,'scanner.mjs'), captured=path.join(root,'captured.jsonl');
23
+ await writeFile(fake,`
24
+ import {mkdir,writeFile,readFile,appendFile} from 'node:fs/promises';
25
+ import path from 'node:path';
26
+ const args=process.argv.slice(2),get=flag=>args[args.indexOf(flag)+1];
27
+ await appendFile(${JSON.stringify(captured)},JSON.stringify(args)+'\\n');
28
+ const backend=${JSON.stringify(backend)},files=args.includes('--scanFilePaths')?get('--scanFilePaths'):
29
+ args.includes('--arkTsFiles')?get('--arkTsFiles'):null;
30
+ if(files && Buffer.byteLength(files)>16000) throw new Error('file arguments exceed safe command-line budget');
31
+ if(${directories} && !files) throw new Error('directory selection requires a file-level backend call');
32
+ if(files && args.includes('--modulePaths')) throw new Error('module paths override precise file selection');
33
+ if(files && files.split(',').some(file=>path.relative(${JSON.stringify(root)},file).split(path.sep)[0]==='entry')) throw new Error('entry entered the scan');
34
+ if(args.includes('--modulePaths') && get('--modulePaths').split(',').some(file=>path.basename(file)!=='library')) throw new Error('unexpected module');
35
+ const out=get(backend==='index'?'--outPath':'--outputPath');
36
+ const changes=JSON.parse(await readFile(get(backend==='index'?'--apiModifiedPath':'--apiModifiedPath'),'utf8'));
37
+ if(backend==='index') {
38
+ const config=JSON.parse(await readFile(get('--configPath'),'utf8'));
39
+ if(!Array.isArray(config.projectPath)||config.projectPath.some(file=>path.basename(file)!=='library'))throw new Error('wrong index module context');
40
+ }
41
+ await mkdir(out,{recursive:true});
42
+ const location='library/src/main/ets/keep/A.ts:1';
43
+ if(backend==='index') await writeFile(path.join(out,'report.csv'),'Api Name,Api Type,Api File Path,Api Prefix,Api Params,API Args,Invoke Method,Invoke Location,Api Key,Invoke Instance,ChangeId,MethodDefinition\\n'+
44
+ 'api,method,a.d.ts,A,,,api,'+location+',key,,"'+changes[0].interfaceChanges.id+'","api(): void"\\n');
45
+ else await writeFile(path.join(out,'report.csv'),'Api Definition,Language,Changed in SDK,Affected Versions,Title,Code Location,Fixed,Change Type,ChangeId\\n'+
46
+ 'api(): void,ArkTS,6.1.0(23),ALL,scope,'+location+',Unfixed,API Behavior Change,'+changes[0].interfaceChanges.id+'\\n');
47
+ `);
48
+ const api={implementLanguage:'typeScript',completePath:'/sdk/a.d.ts',className:'A',interfaceType:'method',interfaceName:'api',methodDefinition:'api(): void'};
49
+ const rule={provider:'DEVECO',ruleId:'scope-rule',changedInSdk:'6.1.0(23)',affectedVersions:'ALL',changeType:'API Behavior Change',
50
+ changeOperation:'API_BEHAVIOR',sourceReference:'a.md:1',title:'scope',url:'',rawEntry:{id:'scope-rule',affectedApis:[api],interfaceChanges:{id:'scope-id'}}};
51
+ return {root,project,captured,rule,toolchain:{apiChangeScan:fake,apiChangeBackend:backend,analyzerRoot:root,sdkRoot:'/sdk',
52
+ sdkFamilies:{openharmony:'/sdk/ohos',hms:'/sdk/hms'},fileScanSupported:true}};
53
+ }
54
+
55
+ const range={start:{platformVersion:'6.0.2',apiLevel:22,channel:'release',channelNumber:0},
56
+ end:{platformVersion:'26.0.0',apiLevel:26,channel:'release',channelNumber:0}};
57
+
58
+ test('an index without file filtering scans only selected whole modules and rejects narrow directories',async()=>{
59
+ const f=await setup('index',false);f.toolchain.fileScanSupported=false;
60
+ await runScanInBatch('upgrade', {...f,rules:[f.rule],range,mode:'upgrade',outputDirectory:f.root});
61
+ const commands=(await readFile(f.captured,'utf8')).trim().split('\n').map(JSON.parse);
62
+ assert.equal(commands.length,1);assert.ok(!commands[0].includes('--scanFilePaths'));
63
+ const narrow=await setup('index',true);narrow.toolchain.fileScanSupported=false;
64
+ await assert.rejects(runScanInBatch('upgrade', {...narrow,rules:[narrow.rule],range,outputDirectory:narrow.root}),{code:'SCAN_SCOPE_UNSUPPORTED'});
65
+ f.project.sourceScope.scanDirs=[f.project.selectedModules[0].root];
66
+ await runScanInBatch('upgrade', {...f,rules:[f.rule],range,mode:'upgrade',outputDirectory:f.root});
67
+ });
68
+
69
+ test('module and directory selections reach both DevEco backends without scanning entry',async()=>{
70
+ for(const backend of ['integrated','index']) for(const directories of [false,true]) {
71
+ const f=await setup(backend,directories);
72
+ const result=await runScanInBatch('upgrade', {...f,rules:[f.rule],range,mode:'upgrade',outputDirectory:f.root});
73
+ const commands=(await readFile(f.captured,'utf8')).trim().split('\n').map(JSON.parse);
74
+ assert.equal(commands.length,1);assert.equal(result.findings.length,1);
75
+ const expected=backend==='index'?'--scanFilePaths':directories?'--arkTsFiles':'--modulePaths';
76
+ assert.ok(commands[0].includes(expected));
77
+ assert.equal(result.findings[0]['Code Location'],'library/src/main/ets/keep/A.ts:1');
78
+ }
79
+ });
80
+
81
+ test('precise file scopes are batched and unsupported file backends never widen the scope',async()=>{
82
+ const f=await setup('index',true), module=f.project.selectedModules[0];
83
+ f.project.sourceFiles.push(...Array.from({length:500},(_,i)=>path.join(module.root,'src/main/ets/keep',`VeryLongFileName${'x'.repeat(60)}${i}.ts`)));
84
+ await runScanInBatch('upgrade', {...f,rules:[f.rule],range,mode:'upgrade',outputDirectory:f.root});
85
+ const commands=(await readFile(f.captured,'utf8')).trim().split('\n').map(JSON.parse);
86
+ assert.ok(commands.length>1);
87
+ for(const args of commands)assert.ok(Buffer.byteLength(args[args.indexOf('--scanFilePaths')+1])<=16000);
88
+ const unsupported=await setup('integrated',true);unsupported.toolchain.fileScanSupported=false;
89
+ await assert.rejects(runScanInBatch('upgrade', {...unsupported,rules:[unsupported.rule],range,outputDirectory:unsupported.root}),{code:'SCAN_SCOPE_UNSUPPORTED'});
90
+ });
91
+
92
+ test('temporary cleanup retries locks and never replaces a scan result or its original error',async()=>{
93
+ let calls=0;
94
+ assert.equal(await cleanupScanDirectory('temporary',async()=>{
95
+ if(++calls<3)throw Object.assign(new Error('busy'),{code:'EBUSY'});
96
+ }),null);
97
+ assert.equal(calls,3);
98
+ const f=await setup('integrated',false);
99
+ const removeTemporary=async()=>{throw Object.assign(new Error('locked'),{code:'EPERM'});};
100
+ const result=await runScanInBatch('upgrade', {...f,rules:[f.rule],range,mode:'upgrade',outputDirectory:f.root,removeTemporary});
101
+ assert.equal(result.findings.length,1);
102
+ assert.equal(result.diagnostics.cleanupWarning.code,'SCAN_TEMP_CLEANUP_FAILED');
103
+ assert.ok(result.diagnostics.warnings.some(value=>value.includes('locked')));
104
+ await assert.rejects(runScanInBatch('upgrade', {...f,rules:[f.rule],range,outputDirectory:f.root,removeTemporary,
105
+ verifyInputs:async()=>{throw Object.assign(new Error('original scan error'),{code:'SOURCE_CHANGED'});}}),error=>{
106
+ assert.equal(error.code,'SOURCE_CHANGED');
107
+ assert.equal(serializeError(error).cleanupWarning.errorCode,'EPERM');
108
+ return true;
109
+ });
110
+ });
@@ -0,0 +1,64 @@
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, realpath, writeFile, symlink } from 'node:fs/promises';
6
+ import { gzipSync } from 'node:zlib';
7
+ import ts from 'typescript';
8
+ import { createArkTsProgram } from '../deprecated/arkts-deprecations.mjs';
9
+
10
+ async function fixture() {
11
+ const root = await realpath(await mkdtemp(path.join(os.tmpdir(), 'source-inputs-')));
12
+ const entry = path.join(root, 'application source'), file = path.join(entry, 'Main.ts');
13
+ await mkdir(entry);
14
+ const project = { root, sourceFiles: [file], modules: [] };
15
+ const catalog = { ts, sourceFiles: new Map(), toolchain: { sdkRoot: root }, etsOptions: {} };
16
+ return { root, entry, file, project, catalog };
17
+ }
18
+
19
+ test('binary file dependencies never become TS sources or binary text inputs', async () => {
20
+ const f = await fixture(), extensions = ['.har', '.hap', '.hsp', '.app', '.abc', '.so', '.a', '.o', '.dll', '.exe', '.bin', ''];
21
+ const dependencies = {}, imports = [];
22
+ for (const [index, extension] of extensions.entries()) {
23
+ const name = `binary${index}`, file = path.join(f.root, `artifact${extension}`);
24
+ await writeFile(file, gzipSync(Buffer.from([0, 255, 254, index])));
25
+ dependencies[name] = `file:${path.relative(f.entry, file).split(path.sep).join('/')}`;
26
+ imports.push(`import { value as v${index} } from '${name}';`);
27
+ }
28
+ await writeFile(path.join(f.entry, 'oh-package.json5'), JSON.stringify({ dependencies }));
29
+ await writeFile(f.file, imports.join('\n'));
30
+ const context = createArkTsProgram(f.project, f.catalog, { compilerOptions: { noLib: true } });
31
+ assert.deepEqual(context.program.getSourceFiles().map(source => path.normalize(source.fileName)), [path.normalize(f.file)]);
32
+ assert.equal(context.unresolvedImports.length, extensions.length);
33
+
34
+ });
35
+
36
+ test('package main binaries are excluded while existing text declarations remain usable', async () => {
37
+ const f = await fixture();
38
+ for (const [name, types] of [['opaque', false], ['typed', true]]) {
39
+ const root = path.join(f.entry, 'oh_modules', name);
40
+ await mkdir(root, { recursive: true });
41
+ await writeFile(path.join(root, 'oh-package.json5'), JSON.stringify({ main: 'payload.hap', ...(types ? { types: 'Index.d.ts' } : {}) }));
42
+ await writeFile(path.join(root, 'payload.hap'), gzipSync(Buffer.from('not source')));
43
+ if (types) await writeFile(path.join(root, 'Index.d.ts'), 'export declare const value: number;');
44
+ }
45
+ await writeFile(f.file, "import { value } from 'typed'; import { missing } from 'opaque'; const n: number = value;");
46
+ const context = createArkTsProgram(f.project, f.catalog, { compilerOptions: { noLib: true } });
47
+ assert.equal(context.program.getSourceFiles().length, 2);
48
+ assert.ok(context.program.getSourceFiles().every(source => source.fileName.endsWith('.ts')));
49
+ assert.deepEqual(context.unresolvedImports.map(item => item.module), ['opaque']);
50
+ });
51
+
52
+ test('binary roots, overlays and directory aliases cannot bypass the source whitelist', async () => {
53
+ const f = await fixture(), binary = path.join(f.root, 'payload.har'), alias = path.join(f.root, 'source alias');
54
+ await writeFile(binary, gzipSync(Buffer.from('archive')));
55
+ await writeFile(path.join(f.entry, 'payload.har'), gzipSync(Buffer.from('archive')));
56
+ await writeFile(f.file, 'export const value = 1;');
57
+ await symlink(f.entry, alias, process.platform === 'win32' ? 'junction' : 'dir');
58
+ f.project.sourceFiles.push(binary);
59
+ const context = createArkTsProgram(f.project, f.catalog, { overlays: new Map([[binary, 'pretend source']]), compilerOptions: { noLib: true } });
60
+ assert.equal(context.program.getSourceFile(binary), undefined);
61
+ assert.equal(context.resolveModule(path.join(alias, 'payload.har'), f.file), null);
62
+ assert.equal(context.resolveModule(path.join(alias, 'Main.ts'), f.file), f.file);
63
+ assert.equal(context.program.getSourceFiles().length, 1);
64
+ });
@@ -0,0 +1,164 @@
1
+ import test from 'node:test';
2
+ import assert from 'node:assert/strict';
3
+ import { mkdir, mkdtemp, writeFile } from 'node:fs/promises';
4
+ import path from 'node:path';
5
+ import os from 'node:os';
6
+ import { rm } from 'node:fs/promises';
7
+ import { compileRules } from '../core/rules.mjs';
8
+ import { selectScanRules, createRunOutputDirectory } from '../core/cli.mjs';
9
+ import { assertUpgradeRange } from '../../shared/platform/version.mjs';
10
+ import { upgradeRuleCoverage } from '../behavior/upgrade-coverage.mjs';
11
+ import { normalizeFinding } from '../core/scan-runner.mjs';
12
+
13
+ test('upgrade only uses plugin rules, keeps no-adaptation hits and never parses additional API diff or deprecated guidance', async () => {
14
+ const root=await mkdtemp(path.join(os.tmpdir(),'scanner-upgrade-')),resource=path.join(root,'resources'),refs=path.join(root,'references');
15
+ await mkdir(resource);await mkdir(refs);await mkdir(path.join(refs,'API变更清单'));
16
+ await writeFile(path.join(refs,'guide.md'),'---\nsource: https://example.test/change\n---\n\n## 测试行为\n\n**适配指导**\n\n无需适配。\n');
17
+ await writeFile(path.join(refs,'API变更清单','bad.md'),'| 不支持的操作 | 无法解析 | 无法解析 | missing.d.ts |\n');
18
+ const entry={affectedApis:[{implementLanguage:'typescript',methodDefinition:'api(): void',className:'global',completePath:'api',interfaceType:'method',interfaceName:'api'}],
19
+ interfaceChanges:{id:'1',changeTitle:'测试行为',changeType:'接口行为变更',apiEffectiveVersion:'NOT_ISOLATED',sdkVersion:'5.0.2(14) Release',apiVersion:'14'}};
20
+ await writeFile(path.join(resource,'HarmonyOS_5.0.2(14)_Release.json'),JSON.stringify([entry,{...entry,interfaceChanges:{...entry.interfaceChanges,id:'2',changeTitle:'没有文档的废弃项',changeType:'接口废弃变更'}}]));
21
+ const result=await compileRules({toolchain:{apiChangeResourceRoot:resource},referenceRoot:refs,range:assertUpgradeRange(13,14),mode:'upgrade'});
22
+ assert.equal(result.rules.length,1);assert.equal(result.rules[0].provider,'DEVECO');
23
+ assert.equal(result.rules[0].reportDisposition,'REQUIRES_REVIEW');assert.equal(result.rules[0].filterReason,null);
24
+ assert.deepEqual(selectScanRules([...result.rules,{provider:'UNSUPPORTED',changeType:'API Definition Change'},{provider:'DEVECO',changeType:'Deprecated API'}],'upgrade'),result.rules);
25
+ });
26
+
27
+ test('official rule gaps stay incomplete; available, excluded, baseline and empty versions remain distinct', () => {
28
+ const policies=[{apiLevel:14,changedInSdk:'5.0.2(14)',title:'缺规则',sourceReference:'missing.md:1'},
29
+ {apiLevel:15,changedInSdk:'5.0.3(15)',title:'有规则',sourceReference:'hit.md:1'},
30
+ {apiLevel:15,changedInSdk:'5.0.3(15)',title:'无 API',sourceReference:'global.md:1'}];
31
+ const coverage=upgradeRuleCoverage({policies,rules:[{provider:'DEVECO',sourceReference:'hit.md:1',ruleId:'r'}],
32
+ excluded:[{sourceReference:'global.md:1',reason:'NO_AFFECTED_APIS'}],range:assertUpgradeRange(13,26)});
33
+ assert.equal(coverage.status,'INCOMPLETE');assert.equal(coverage.missingRuleCount,1);
34
+ assert.equal(coverage.entries[1].status,'RULE_AVAILABLE');assert.equal(coverage.entries[2].status,'NOT_SCANNABLE_NO_AFFECTED_APIS');
35
+ assert.equal(coverage.versions[0].status,'START_BASELINE_EXCLUDED');
36
+ assert.equal(coverage.versions.find(v=>v.apiLevel===16).status,'NO_OFFICIAL_CHANGES');
37
+ assert.ok(!coverage.versions.some(v=>v.apiLevel===25));
38
+ });
39
+
40
+ test('API12 baselines need no deleted source and from=12 still includes API13 changes', async t => {
41
+ const root = await mkdtemp(path.join(os.tmpdir(), 'scanner-api13-中文 '));
42
+ t.after(() => rm(root, { recursive: true, force: true }));
43
+ const resource = path.join(root, 'resources'), refs = path.join(root, 'changelogs');
44
+ await mkdir(resource); await mkdir(path.join(refs, '5.0.1(13)'), { recursive: true });
45
+ await writeFile(path.join(refs, '5.0.1(13)', 'release-os-api-behavior-changes.md'),
46
+ '## API13变更\n**适配指导**\n检查真实调用。\n');
47
+ const entry = (id, version, title) => ({
48
+ affectedApis: [{ implementLanguage: 'typescript', interfaceType: 'method', interfaceName: 'api' }],
49
+ interfaceChanges: { id, changeTitle: title, changeType: '接口行为变更',
50
+ apiEffectiveVersion: 'NOT_ISOLATED', sdkVersion: version },
51
+ });
52
+ await writeFile(path.join(resource, 'HarmonyOS_5.0.0(12)_Release.json'),
53
+ JSON.stringify([entry('baseline', '5.0.0(12) Release', '已删除的API12变更')]));
54
+ await writeFile(path.join(resource, 'HarmonyOS_5.0.1(13)_Release.json'),
55
+ JSON.stringify([entry('api13', '5.0.1(13) Release', 'API13变更')]));
56
+ for (const from of [12, '5.0.0(12) Beta3']) {
57
+ const range = assertUpgradeRange(from, 13);
58
+ const result = await compileRules({ toolchain: { apiChangeResourceRoot: resource }, referenceRoot: refs, range, mode: 'upgrade' });
59
+ assert.equal(result.coverage.status, 'COMPLETE');
60
+ assert.deepEqual(result.rules.map(rule => rule.ruleId), ['api13']);
61
+ const baselineCount = typeof from === 'number' ? 0 : 1;
62
+ assert.equal(result.coverage.behaviorBaseline.excludedRuleCount, baselineCount);
63
+ const coverage = upgradeRuleCoverage({ policies: [{ apiLevel: 13, changedInSdk: '5.0.1(13) Release',
64
+ title: 'API13变更', sourceReference: result.rules[0].sourceReference }], rules: result.rules, range });
65
+ assert.equal(coverage.entries[0].status, 'RULE_AVAILABLE');
66
+ }
67
+ });
68
+
69
+ test('concurrent timestamp output allocation never shares a directory',async()=>{
70
+ const root=await mkdtemp(path.join(os.tmpdir(),'upgrade-output-'));
71
+ const results=await Promise.all([createRunOutputDirectory(root),createRunOutputDirectory(root)]);
72
+ assert.notEqual(results[0].directory,results[1].directory);
73
+ });
74
+
75
+ test('upgrade preserves plugin columns to distinguish same-line occurrences',()=>{
76
+ const rule={ruleId:'r',title:'调用变更',changedInSdk:'5.0.2(14)',url:'https://example.test/change',changeType:'API Behavior Change',sourceReference:'doc.md:1'};
77
+ const findings=[1,12].map(Column=>normalizeFinding({'Api Definition':'api()','Code Location':'entry/A.ets:1',Language:'ArkTS',Column},rule,null,'upgrade'));
78
+ assert.deepEqual(findings.map(finding=>finding.Column),[1,12]);
79
+ });
80
+
81
+ test('official entry links resolve by identity and unique source; supplied links survive normalization', async () => {
82
+ const root=await mkdtemp(path.join(os.tmpdir(),'scanner-links-')),resource=path.join(root,'resources'),refs=path.join(root,'references');
83
+ await mkdir(resource);await mkdir(path.join(refs,'5.0.2(14)'),{recursive:true});
84
+ const guide=path.join(refs,'5.0.2(14)/guide.md'), overview=path.join(refs,'5.0.2(14)/overview.md');
85
+ await writeFile(guide,'---\nsource: https://developer.huawei.com/consumer/cn/doc/harmonyos-releases/test\n---\n## 文档标题\n**适配指导**\n需核对调用。\n');
86
+ const entry={affectedApis:[{implementLanguage:'typescript',methodDefinition:'api(): void',interfaceType:'method',interfaceName:'api'}],
87
+ interfaceChanges:{id:'CH123',changeTitle:'文档标题',changeType:'接口行为变更',apiEffectiveVersion:'NOT_ISOLATED',sdkVersion:'5.0.2(14) Release',apiVersion:'14'}};
88
+ await writeFile(path.join(resource,'HarmonyOS_5.0.2(14)_Release.json'),JSON.stringify([entry]));
89
+ const load=async()=> (await compileRules({toolchain:{apiChangeResourceRoot:resource},referenceRoot:refs,range:assertUpgradeRange(13,14),mode:'upgrade'})).rules[0];
90
+ await writeFile(overview,'| [另一个标题](https://developer.huawei.com/consumer/cn/doc/harmonyos-releases/test#ch123) |\n');
91
+ let rule=await load();assert.equal(rule.url,'https://developer.huawei.com/consumer/cn/doc/doccenter-release-notes/test#ch123');
92
+ assert.equal(rule.documentLink.source,'OFFICIAL_OVERVIEW');
93
+ const original='https://developer.huawei.com/consumer/cn/doc/harmonyos-releases/test/#scan-fake';
94
+ const finding=normalizeFinding({Title:`=HYPERLINK("${original}","原始标题")`,'Code Location':'entry/A.ts:1'},rule,null,'upgrade');
95
+ assert.equal(finding.Title,`=HYPERLINK("${rule.url}","文档标题")`);assert.equal(finding.documentLink.rejectedReason,'SYNTHETIC_SCAN_ANCHOR');
96
+ entry.interfaceChanges.url='test#ch123';await writeFile(path.join(resource,'HarmonyOS_5.0.2(14)_Release.json'),JSON.stringify([entry]));
97
+ assert.equal((await load()).documentLink.source,'DEVECO_RULE');delete entry.interfaceChanges.url;
98
+ await writeFile(path.join(resource,'HarmonyOS_5.0.2(14)_Release.json'),JSON.stringify([entry]));
99
+ await writeFile(overview,'| [文档标题](https://developer.huawei.com/consumer/cn/doc/harmonyos-releases/test#one) | [文档标题](https://developer.huawei.com/consumer/cn/doc/harmonyos-releases/test#two) |\n');
100
+ rule=await load();assert.equal(rule.url,'');assert.equal(rule.documentLink.reason,'AMBIGUOUS_DOCUMENT_LINK');
101
+ });
102
+
103
+ test('flattened documents resolve plugin aliases and official anchors from the sidecar without overview files', async () => {
104
+ const root=await mkdtemp(path.join(os.tmpdir(),'scanner-flat-中文 ')),resource=path.join(root,'resources'),refs=path.join(root,'changelogs');
105
+ await mkdir(resource);await mkdir(path.join(refs,'5.0.2(14)'),{recursive:true});
106
+ const file=path.posix.join('5.0.2(14)','Beta1-正文.md');
107
+ const page='https://developer.huawei.com/consumer/cn/doc/harmonyos-releases/test';
108
+ await writeFile(path.join(refs,file),`---\nsource: ${page}\n---\n## 文档标题\n**适配指导**\n需核对调用。\n`);
109
+ await writeFile(path.join(root,'changelogs-index.json'),JSON.stringify({schemaVersion:1,
110
+ documents:[{path:file}],
111
+ overview:[{version:'5.0.2(14)',row:`| [文档标题](${page}#ch123) | phone | 全部生效 |`},
112
+ {version:'26.0.0',row:`| [文档标题](${page}#other-version) |`}]}));
113
+ const entry={affectedApis:[{implementLanguage:'typescript',methodDefinition:'api(): void',interfaceType:'method',interfaceName:'api'}],
114
+ interfaceChanges:{id:'CH123',changeTitle:'插件中的别名',changeType:'接口行为变更',apiEffectiveVersion:'NOT_ISOLATED',sdkVersion:'5.0.2(14) Release',apiVersion:'14'}};
115
+ await writeFile(path.join(resource,'HarmonyOS_5.0.2(14)_Release.json'),JSON.stringify([entry]));
116
+ const result=await compileRules({toolchain:{apiChangeResourceRoot:resource},referenceRoot:refs,range:assertUpgradeRange(13,14),mode:'upgrade'});
117
+ assert.equal(result.rules.length,1);
118
+ assert.equal(result.rules[0].sourceReference,`${file}:4`);
119
+ assert.equal(result.rules[0].url,'https://developer.huawei.com/consumer/cn/doc/doccenter-release-notes/test#ch123');
120
+ assert.equal(result.rules[0].documentLink.source,'OFFICIAL_OVERVIEW');
121
+ });
122
+
123
+ test('fallback plugin rule identities use the current source path when the plugin supplies no change id', async () => {
124
+ const root=await mkdtemp(path.join(os.tmpdir(),'scanner-flat-identity-')),resource=path.join(root,'resources');
125
+ const before=path.join(root,'before'),after=path.join(root,'after');
126
+ await mkdir(resource);
127
+ const oldPath=path.posix.join('5.0.2(14)','OS平台能力','正文.md'),newPath=path.posix.join('5.0.2(14)','正文.md');
128
+ const text='---\nsource: https://example.test/change\n---\n## 测试行为\n**适配指导**\n需核对调用。\n';
129
+ for(const [refs,file]of [[before,oldPath],[after,newPath]]) {
130
+ await mkdir(path.dirname(path.join(refs,file)),{recursive:true});await writeFile(path.join(refs,file),text);
131
+ }
132
+ await writeFile(path.join(root,'after-index.json'),JSON.stringify({schemaVersion:1,
133
+ documents:[{path:newPath}],overview:[]}));
134
+ const entry={affectedApis:[{implementLanguage:'typescript',methodDefinition:'api(): void',interfaceType:'method',interfaceName:'api'}],
135
+ interfaceChanges:{changeTitle:'测试行为',changeType:'接口行为变更',apiEffectiveVersion:'NOT_ISOLATED',sdkVersion:'5.0.2(14) Release',apiVersion:'14'}};
136
+ await writeFile(path.join(resource,'HarmonyOS_5.0.2(14)_Release.json'),JSON.stringify([entry]));
137
+ const load=referenceRoot=>compileRules({toolchain:{apiChangeResourceRoot:resource},referenceRoot,range:assertUpgradeRange(13,14),mode:'upgrade'});
138
+ const original=(await load(before)).rules[0],moved=(await load(after)).rules[0];
139
+ assert.notEqual(moved.ruleId,original.ruleId);assert.equal(moved.sourceReference,`${newPath}:4`);
140
+ assert.equal((await load(after)).rules[0].ruleId,moved.ruleId);
141
+ });
142
+
143
+ test('each mode loads only its rule types before resolving unrelated document headings', async t => {
144
+ const root=await mkdtemp(path.join(os.tmpdir(),'scanner-rule-modes-'));
145
+ t.after(()=>rm(root,{recursive:true,force:true}));
146
+ const resource=path.join(root,'resources');await mkdir(resource);
147
+ const types=['接口行为变更','视觉布局变更','UX交互行为变更','接口定义变更','接口废弃变更'];
148
+ const entries=types.map((changeType,i)=>({affectedApis:[{implementLanguage:i===4?'c':'typescript',
149
+ methodDefinition:'api(): void',interfaceType:'method',interfaceName:'api'}],
150
+ interfaceChanges:{id:String(i),changeTitle:`标题${i}`,changeType,apiEffectiveVersion:'NOT_ISOLATED',
151
+ sdkVersion:'26.0.0(26) Release',apiVersion:'26'}}));
152
+ await writeFile(path.join(resource,'HarmonyOS_26.0.0(26)_Release.json'),JSON.stringify(entries));
153
+ for(const mode of ['upgrade','deprecated']) {
154
+ const referenceRoot=path.join(root,mode);await mkdir(referenceRoot);
155
+ await writeFile(path.join(referenceRoot,'guide.md'),types.map((_,i)=>i===4?mode==='deprecated':mode==='upgrade')
156
+ .flatMap((selected,i)=>selected?[`## 标题${i}`,'**适配指导**','需核对调用。']:[]).join('\n'));
157
+ const result=await compileRules({toolchain:{apiChangeResourceRoot:resource},referenceRoot,range:assertUpgradeRange(24,26),mode});
158
+ assert.equal(result.rules.length,mode==='upgrade'?4:1);
159
+ assert.ok(result.rules.every(rule=>mode==='upgrade'?rule.changeType!=='Deprecated API':rule.changeType==='Deprecated API'));
160
+ assert.equal(result.coverage.errorCount,0);
161
+ assert.ok(!Object.hasOwn(result.coverage,'apiDiffRuleCount'));assert.ok(!Object.hasOwn(result.coverage,'operations'));
162
+ if(mode==='deprecated')assert.equal(selectScanRules(result.rules,mode).length,1);
163
+ }
164
+ });
@@ -0,0 +1,66 @@
1
+ export function parseArgs(argv) {
2
+ const result = { _: [] };
3
+ for (let index = 0; index < argv.length; index += 1) {
4
+ const token = argv[index];
5
+ if (!token.startsWith('--')) {
6
+ result._.push(token);
7
+ continue;
8
+ }
9
+ const separator = token.indexOf('=');
10
+ const key = separator === -1 ? token.slice(2) : token.slice(2, separator);
11
+ const value = separator === -1 ? argv[index + 1] : token.slice(separator + 1);
12
+ if (separator === -1 && (value === undefined || value.startsWith('--'))) {
13
+ addArg(result, key, true);
14
+ continue;
15
+ }
16
+ addArg(result, key, value);
17
+ if (separator === -1) index += 1;
18
+ }
19
+ return result;
20
+ }
21
+
22
+ function addArg(result, key, value) {
23
+ if (result[key] === undefined) {
24
+ result[key] = value;
25
+ } else if (Array.isArray(result[key])) {
26
+ result[key].push(value);
27
+ } else {
28
+ result[key] = [result[key], value];
29
+ }
30
+ }
31
+
32
+ export function requireString(args, name) {
33
+ const value = args[name];
34
+ if (typeof value !== 'string' || value.length === 0) {
35
+ throw new Error(`Missing required argument --${name}`);
36
+ }
37
+ return value;
38
+ }
39
+
40
+ export function stringList(value) {
41
+ if (value === undefined) return [];
42
+ return (Array.isArray(value) ? value : [value]).flatMap((item) => String(item).split(','))
43
+ .map((item) => item.trim()).filter(Boolean);
44
+ }
45
+
46
+ export function integerArg(value, fallback, name) {
47
+ if (value === undefined) return fallback;
48
+ const parsed = Number(value);
49
+ if (!Number.isSafeInteger(parsed) || parsed <= 0) {
50
+ throw new Error(`--${name} must be a positive integer`);
51
+ }
52
+ return parsed;
53
+ }
54
+
55
+ export function booleanArg(value, fallback = false) {
56
+ if (value === undefined) {
57
+ return fallback;
58
+ }
59
+ if (value === true || value === 'true' || value === '1') {
60
+ return true;
61
+ }
62
+ if (value === false || value === 'false' || value === '0') {
63
+ return false;
64
+ }
65
+ throw new Error(`Expected boolean value, received ${String(value)}`);
66
+ }
@@ -0,0 +1,85 @@
1
+ export const BASE_HEADERS = [
2
+ 'Api Definition', 'Language', 'Changed in SDK', 'Affected Versions',
3
+ 'Title', 'Code Location', 'Fixed', 'Change Type',
4
+ ];
5
+
6
+ // Full internal identity fields; independent of the exported CSV projection.
7
+ export const SCANNER_HEADERS = [
8
+ ...BASE_HEADERS, 'Rule ID', 'Change Operation', 'Source Reference',
9
+ 'Replacement API', 'Replacement Source',
10
+ 'API Symbol ID', 'Column', 'Deprecated Since', 'Detection Source', 'Source Scope',
11
+ ];
12
+
13
+ export const UPGRADE_HEADERS = [...BASE_HEADERS, 'Rule ID'];
14
+ export const DEPRECATED_HEADERS = [
15
+ ...BASE_HEADERS.filter(header => header !== 'Affected Versions'), 'Replacement API',
16
+ ];
17
+
18
+ export function parseCsv(text) {
19
+ const source = text.replace(/^\ufeff/, '');
20
+ const records = [];
21
+ let record = [];
22
+ let field = '';
23
+ let quoted = false;
24
+ for (let index = 0; index < source.length; index += 1) {
25
+ const character = source[index];
26
+ if (quoted) {
27
+ if (character === '"') {
28
+ if (source[index + 1] === '"') {
29
+ field += '"';
30
+ index += 1;
31
+ } else quoted = false;
32
+ } else field += character;
33
+ } else if (character === '"' && field.length === 0) quoted = true;
34
+ else if (character === ',') {
35
+ record.push(field);
36
+ field = '';
37
+ } else if (character === '\r' || character === '\n') {
38
+ if (character === '\r' && source[index + 1] === '\n') index += 1;
39
+ record.push(field);
40
+ if (record.some((value) => value !== '')) records.push(record);
41
+ record = [];
42
+ field = '';
43
+ } else field += character;
44
+ }
45
+ if (quoted) throw new Error('Malformed CSV: unterminated quoted field');
46
+ if (field.length > 0 || record.length > 0) {
47
+ record.push(field);
48
+ records.push(record);
49
+ }
50
+ if (records.length === 0) return { headers: [], rows: [] };
51
+ const headers = records[0];
52
+ const rows = records.slice(1).map((values, index) => {
53
+ if (values.length !== headers.length) {
54
+ throw new Error(`Malformed CSV row ${index + 2}: expected ${headers.length} fields, received ${values.length}`);
55
+ }
56
+ return Object.fromEntries(headers.map((header, column) => [header, values[column]]));
57
+ });
58
+ return { headers, rows };
59
+ }
60
+
61
+ function encode(value) {
62
+ const text = value === undefined || value === null ? '' : String(value);
63
+ return /[",\r\n]/.test(text) ? `"${text.replaceAll('"', '""')}"` : text;
64
+ }
65
+
66
+ export function stringifyCsv(headers, rows, { bom = true, newline = '\r\n' } = {}) {
67
+ const lines = [headers.map(encode).join(',')];
68
+ for (const row of rows) lines.push(headers.map((header) => encode(row[header])).join(','));
69
+ return `${bom ? '\ufeff' : ''}${lines.join(newline)}${newline}`;
70
+ }
71
+
72
+ export function hyperlink(url, title) {
73
+ if (!url) return title;
74
+ const escape = (value) => String(value).replaceAll('"', '""');
75
+ return `=HYPERLINK("${escape(url)}","${escape(title)}")`;
76
+ }
77
+
78
+ export function hyperlinkUrl(value) {
79
+ const match = String(value ?? '').match(/^=HYPERLINK\("((?:[^"]|"")*)","(?:[^"]|"")*"\)$/i);
80
+ if (!match) return null;
81
+ try {
82
+ const url = new URL(match[1].replaceAll('""', '"'));
83
+ return ['http:', 'https:'].includes(url.protocol) ? url.href : null;
84
+ } catch { return null; }
85
+ }
@@ -0,0 +1,92 @@
1
+ const IDENTIFIER_START = /[A-Za-z_$]/;
2
+ const IDENTIFIER_PART = /[A-Za-z0-9_$-]/;
3
+
4
+ export function parseJson5(text) {
5
+ const tokens = [];
6
+ let index = 0;
7
+ while (index < text.length) {
8
+ const character = text[index];
9
+ if (/\s|\ufeff/.test(character)) {
10
+ index += 1;
11
+ } else if (character === '/' && text[index + 1] === '/') {
12
+ index += 2;
13
+ while (index < text.length && text[index] !== '\n') index += 1;
14
+ } else if (character === '/' && text[index + 1] === '*') {
15
+ const end = text.indexOf('*/', index + 2);
16
+ if (end === -1) throw new Error(`Unterminated JSON5 comment at offset ${index}`);
17
+ index = end + 2;
18
+ } else if (character === '"' || character === "'") {
19
+ const quote = character;
20
+ let value = '';
21
+ index += 1;
22
+ while (index < text.length && text[index] !== quote) {
23
+ if (text[index] === '\\') {
24
+ index += 1;
25
+ const escaped = text[index];
26
+ value += ({ n: '\n', r: '\r', t: '\t', b: '\b', f: '\f' })[escaped] ?? escaped;
27
+ } else value += text[index];
28
+ index += 1;
29
+ }
30
+ if (text[index] !== quote) throw new Error('Unterminated JSON5 string');
31
+ tokens.push({ type: 'value', value });
32
+ index += 1;
33
+ } else if ('{}[]:,'.includes(character)) {
34
+ tokens.push({ type: 'punctuation', value: character });
35
+ index += 1;
36
+ } else if (IDENTIFIER_START.test(character)) {
37
+ const start = index++;
38
+ while (index < text.length && IDENTIFIER_PART.test(text[index])) index += 1;
39
+ tokens.push({ type: 'identifier', value: text.slice(start, index) });
40
+ } else if (/[+\-.0-9]/.test(character)) {
41
+ const start = index++;
42
+ while (index < text.length && /[A-Fa-fxX0-9eE+\-.]/.test(text[index])) index += 1;
43
+ tokens.push({ type: 'number', value: text.slice(start, index) });
44
+ } else throw new Error(`Unexpected JSON5 character ${JSON.stringify(character)} at offset ${index}`);
45
+ }
46
+ let cursor = 0;
47
+ const current = () => tokens[cursor];
48
+ function consume(value) {
49
+ const token = current();
50
+ if (!token || (value !== undefined && token.value !== value)) throw new Error(`Expected ${value ?? 'token'} at token ${cursor}`);
51
+ cursor += 1;
52
+ return token;
53
+ }
54
+ function value() {
55
+ const token = current();
56
+ if (!token) throw new Error('Unexpected end of JSON5 input');
57
+ if (token.value === '{') return object();
58
+ if (token.value === '[') return array();
59
+ cursor += 1;
60
+ if (token.type === 'value') return token.value;
61
+ if (token.type === 'number') return Number(token.value);
62
+ if (token.value === 'true') return true;
63
+ if (token.value === 'false') return false;
64
+ if (token.value === 'null') return null;
65
+ throw new Error(`Unexpected JSON5 value: ${token.value}`);
66
+ }
67
+ function object() {
68
+ const result = {};
69
+ consume('{');
70
+ while (current()?.value !== '}') {
71
+ const key = consume().value;
72
+ consume(':');
73
+ result[key] = value();
74
+ if (current()?.value === ',') consume(',');
75
+ }
76
+ consume('}');
77
+ return result;
78
+ }
79
+ function array() {
80
+ const result = [];
81
+ consume('[');
82
+ while (current()?.value !== ']') {
83
+ result.push(value());
84
+ if (current()?.value === ',') consume(',');
85
+ }
86
+ consume(']');
87
+ return result;
88
+ }
89
+ const result = value();
90
+ if (cursor !== tokens.length) throw new Error(`Unexpected trailing JSON5 token ${cursor}`);
91
+ return result;
92
+ }
@@ -0,0 +1,45 @@
1
+ import { TextDecoder } from 'node:util';
2
+ import { parseCsv, stringifyCsv } from './csv.mjs';
3
+ import { sha256 } from '../io/fs-utils.mjs';
4
+
5
+ const resultHeaders = ['处理结论', '处理说明', '后续事项'];
6
+ const conclusions = new Set(['无需适配', '已适配', '待决策']);
7
+
8
+ /**
9
+ * Format model-assessed results without changing source cells or deciding business outcomes.
10
+ * @param {Buffer} scanBytes Original UTF-8 scan bytes, including any BOM.
11
+ * @param {Array<{rowId: string, 处理结论: string, 处理说明: string, 后续事项: string}>} results
12
+ * One result per SHA-256:logical-row identity; input order is immaterial.
13
+ * @returns {string} CSV with exactly three appended columns, UTF-8 BOM and CRLF records.
14
+ */
15
+ export function formatUpgradeResults(scanBytes, results) {
16
+ if (!Buffer.isBuffer(scanBytes)) throw new TypeError('scanBytes 必须是原始扫描字节 Buffer');
17
+ if (!Array.isArray(results)) throw new TypeError('results 必须是逐行结果数组');
18
+ let text;
19
+ try { text = new TextDecoder('utf-8', { fatal: true }).decode(scanBytes); }
20
+ catch (cause) { throw new Error('原始扫描表必须使用 UTF-8 编码', { cause }); }
21
+ const { headers, rows } = parseCsv(text);
22
+ if (!headers.length || headers.some(header => !header.trim())) throw new Error('原始扫描表缺少有效表头');
23
+ if (new Set(headers).size !== headers.length) throw new Error('原始扫描表包含重复表头');
24
+ if (resultHeaders.some(header => headers.includes(header)))
25
+ throw new Error('输入已包含处理列,请使用原始扫描表,不要传入 adapted-results');
26
+
27
+ const hash = sha256(scanBytes);
28
+ const rowIds = rows.map((_, index) => `${hash}:${index + 1}`);
29
+ const expected = new Set(rowIds);
30
+ const byId = new Map();
31
+ for (const result of results) {
32
+ if (!result || !expected.has(result.rowId)) throw new Error(`未知结果 rowId: ${result?.rowId}`);
33
+ if (byId.has(result.rowId)) throw new Error(`重复结果 rowId: ${result.rowId}`);
34
+ if (!conclusions.has(result['处理结论'])) throw new Error(`处理结论必须为无需适配、已适配或待决策: ${result.rowId}`);
35
+ if (typeof result['处理说明'] !== 'string' || !result['处理说明'].trim())
36
+ throw new Error(`处理说明必须包含具体理由: ${result.rowId}`);
37
+ if (typeof result['后续事项'] !== 'string') throw new Error(`后续事项必须为字符串,无待办时留空: ${result.rowId}`);
38
+ byId.set(result.rowId, result);
39
+ }
40
+ if (byId.size !== rows.length) throw new Error('缺少扫描行对应的结果 rowId');
41
+ return stringifyCsv([...headers, ...resultHeaders], rows.map((row, index) => {
42
+ const result = byId.get(rowIds[index]);
43
+ return { ...row, ...Object.fromEntries(resultHeaders.map(header => [header, result[header]])) };
44
+ }));
45
+ }