@agentrhq/webcmd 0.1.1 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +317 -24
- package/dist/src/adapter-shadow.d.ts +11 -0
- package/dist/src/adapter-shadow.js +72 -0
- package/dist/src/adapter-shadow.test.d.ts +1 -0
- package/dist/src/adapter-shadow.test.js +49 -0
- package/dist/src/adapter-source.d.ts +11 -0
- package/dist/src/adapter-source.js +24 -0
- package/dist/src/adapter-source.test.d.ts +1 -0
- package/dist/src/adapter-source.test.js +29 -0
- package/dist/src/brand.d.ts +9 -0
- package/dist/src/brand.js +9 -0
- package/dist/src/brand.test.d.ts +1 -0
- package/dist/src/brand.test.js +15 -0
- package/dist/src/browser/analyze.d.ts +118 -0
- package/dist/src/browser/analyze.js +405 -0
- package/dist/src/browser/analyze.test.d.ts +1 -0
- package/dist/src/browser/analyze.test.js +209 -0
- package/dist/src/browser/article-extract.d.ts +57 -0
- package/dist/src/browser/article-extract.e2e.test.d.ts +1 -0
- package/dist/src/browser/article-extract.e2e.test.js +105 -0
- package/dist/src/browser/article-extract.js +169 -0
- package/dist/src/browser/article-extract.test.d.ts +1 -0
- package/dist/src/browser/article-extract.test.js +94 -0
- package/dist/src/browser/ax-snapshot.d.ts +37 -0
- package/dist/src/browser/ax-snapshot.js +217 -0
- package/dist/src/browser/ax-snapshot.test.d.ts +1 -0
- package/dist/src/browser/ax-snapshot.test.js +91 -0
- package/dist/src/browser/base-page.d.ts +151 -0
- package/dist/src/browser/base-page.js +1060 -0
- package/dist/src/browser/base-page.test.d.ts +1 -0
- package/dist/src/browser/base-page.test.js +803 -0
- package/dist/src/browser/bridge-readiness.d.ts +14 -0
- package/dist/src/browser/bridge-readiness.js +24 -0
- package/dist/src/browser/bridge-readiness.test.d.ts +1 -0
- package/dist/src/browser/bridge-readiness.test.js +58 -0
- package/dist/src/browser/bridge.d.ts +26 -0
- package/dist/src/browser/bridge.js +58 -0
- package/dist/src/browser/cdp-click-fixture.test.d.ts +1 -0
- package/dist/src/browser/cdp-click-fixture.test.js +87 -0
- package/dist/src/browser/cdp.d.ts +46 -0
- package/dist/src/browser/cdp.js +536 -0
- package/dist/src/browser/cdp.test.d.ts +1 -0
- package/dist/src/browser/cdp.test.js +79 -0
- package/dist/src/browser/compound.d.ts +59 -0
- package/dist/src/browser/compound.js +112 -0
- package/dist/src/browser/compound.test.d.ts +1 -0
- package/dist/src/browser/compound.test.js +175 -0
- package/dist/src/browser/config.d.ts +2 -0
- package/dist/src/browser/config.js +14 -0
- package/dist/src/browser/daemon-client.d.ts +32 -0
- package/dist/src/browser/daemon-client.js +135 -0
- package/dist/src/browser/daemon-client.test.d.ts +1 -0
- package/dist/src/browser/daemon-client.test.js +326 -0
- package/dist/src/browser/daemon-lifecycle.d.ts +37 -0
- package/dist/src/browser/daemon-lifecycle.js +147 -0
- package/dist/src/browser/daemon-transport.d.ts +54 -0
- package/dist/src/browser/daemon-transport.js +68 -0
- package/dist/src/browser/daemon-version.d.ts +4 -0
- package/dist/src/browser/daemon-version.js +12 -0
- package/dist/src/browser/dom-helpers.d.ts +41 -0
- package/dist/src/browser/dom-helpers.js +239 -0
- package/dist/src/browser/dom-helpers.test.d.ts +1 -0
- package/dist/src/browser/dom-helpers.test.js +106 -0
- package/dist/src/browser/dom-snapshot.d.ts +93 -0
- package/dist/src/browser/dom-snapshot.js +894 -0
- package/dist/src/browser/dom-snapshot.test.d.ts +11 -0
- package/dist/src/browser/dom-snapshot.test.js +313 -0
- package/dist/src/browser/errors.d.ts +38 -0
- package/dist/src/browser/errors.js +79 -0
- package/dist/src/browser/errors.test.d.ts +1 -0
- package/dist/src/browser/errors.test.js +60 -0
- package/dist/src/browser/extract.d.ts +69 -0
- package/dist/src/browser/extract.js +132 -0
- package/dist/src/browser/extract.test.d.ts +1 -0
- package/dist/src/browser/extract.test.js +129 -0
- package/dist/src/browser/find.d.ts +84 -0
- package/dist/src/browser/find.js +398 -0
- package/dist/src/browser/find.test.d.ts +1 -0
- package/dist/src/browser/find.test.js +180 -0
- package/dist/src/browser/html-tree.d.ts +75 -0
- package/dist/src/browser/html-tree.js +112 -0
- package/dist/src/browser/html-tree.test.d.ts +1 -0
- package/dist/src/browser/html-tree.test.js +181 -0
- package/dist/src/browser/index.d.ts +14 -0
- package/dist/src/browser/index.js +12 -0
- package/dist/src/browser/network-cache.d.ts +49 -0
- package/dist/src/browser/network-cache.js +78 -0
- package/dist/src/browser/network-cache.test.d.ts +1 -0
- package/dist/src/browser/network-cache.test.js +75 -0
- package/dist/src/browser/network-interceptor.d.ts +11 -0
- package/dist/src/browser/network-interceptor.js +11 -0
- package/dist/src/browser/network-key.d.ts +22 -0
- package/dist/src/browser/network-key.js +66 -0
- package/dist/src/browser/network-key.test.d.ts +1 -0
- package/dist/src/browser/network-key.test.js +49 -0
- package/dist/src/browser/page.d.ts +83 -0
- package/dist/src/browser/page.js +453 -0
- package/dist/src/browser/page.test.d.ts +1 -0
- package/dist/src/browser/page.test.js +406 -0
- package/dist/src/browser/profile.d.ts +14 -0
- package/dist/src/browser/profile.js +86 -0
- package/dist/src/browser/protocol.d.ts +63 -0
- package/dist/src/browser/protocol.js +1 -0
- package/dist/src/browser/runtime/local-cloak/actions.d.ts +3 -0
- package/dist/src/browser/runtime/local-cloak/actions.js +240 -0
- package/dist/src/browser/runtime/local-cloak/downloads.d.ts +3 -0
- package/dist/src/browser/runtime/local-cloak/downloads.js +28 -0
- package/dist/src/browser/runtime/local-cloak/network.d.ts +28 -0
- package/dist/src/browser/runtime/local-cloak/network.js +120 -0
- package/dist/src/browser/runtime/local-cloak/network.test.d.ts +1 -0
- package/dist/src/browser/runtime/local-cloak/network.test.js +124 -0
- package/dist/src/browser/runtime/local-cloak/profiles.d.ts +6 -0
- package/dist/src/browser/runtime/local-cloak/profiles.js +17 -0
- package/dist/src/browser/runtime/local-cloak/profiles.test.d.ts +1 -0
- package/dist/src/browser/runtime/local-cloak/profiles.test.js +19 -0
- package/dist/src/browser/runtime/local-cloak/provider.d.ts +15 -0
- package/dist/src/browser/runtime/local-cloak/provider.js +27 -0
- package/dist/src/browser/runtime/local-cloak/provider.test.d.ts +1 -0
- package/dist/src/browser/runtime/local-cloak/provider.test.js +244 -0
- package/dist/src/browser/runtime/local-cloak/session-manager.d.ts +77 -0
- package/dist/src/browser/runtime/local-cloak/session-manager.js +280 -0
- package/dist/src/browser/runtime/local-cloak/session-manager.test.d.ts +1 -0
- package/dist/src/browser/runtime/local-cloak/session-manager.test.js +92 -0
- package/dist/src/browser/runtime/provider.d.ts +9 -0
- package/dist/src/browser/runtime/provider.js +1 -0
- package/dist/src/browser/runtime/provider.test.d.ts +2 -0
- package/dist/src/browser/runtime/provider.test.js +49 -0
- package/dist/src/browser/shape-filter.d.ts +52 -0
- package/dist/src/browser/shape-filter.js +101 -0
- package/dist/src/browser/shape-filter.test.d.ts +1 -0
- package/dist/src/browser/shape-filter.test.js +101 -0
- package/dist/src/browser/shape.d.ts +23 -0
- package/dist/src/browser/shape.js +95 -0
- package/dist/src/browser/shape.test.d.ts +1 -0
- package/dist/src/browser/shape.test.js +82 -0
- package/dist/src/browser/stealth.d.ts +11 -0
- package/dist/src/browser/stealth.js +359 -0
- package/dist/src/browser/stealth.test.d.ts +1 -0
- package/dist/src/browser/stealth.test.js +134 -0
- package/dist/src/browser/tabs.d.ts +13 -0
- package/dist/src/browser/tabs.js +70 -0
- package/dist/src/browser/target-errors.d.ts +39 -0
- package/dist/src/browser/target-errors.js +45 -0
- package/dist/src/browser/target-errors.test.d.ts +1 -0
- package/dist/src/browser/target-errors.test.js +94 -0
- package/dist/src/browser/target-resolver.d.ts +158 -0
- package/dist/src/browser/target-resolver.js +570 -0
- package/dist/src/browser/target-resolver.test.d.ts +1 -0
- package/dist/src/browser/target-resolver.test.js +118 -0
- package/dist/src/browser/utils.d.ts +20 -0
- package/dist/src/browser/utils.js +64 -0
- package/dist/src/browser/utils.test.d.ts +1 -0
- package/dist/src/browser/utils.test.js +29 -0
- package/dist/src/browser/verify-fixture.d.ts +65 -0
- package/dist/src/browser/verify-fixture.js +318 -0
- package/dist/src/browser/verify-fixture.test.d.ts +1 -0
- package/dist/src/browser/verify-fixture.test.js +219 -0
- package/dist/src/browser/visual-refs.d.ts +11 -0
- package/dist/src/browser/visual-refs.js +108 -0
- package/dist/src/browser.test.d.ts +1 -0
- package/dist/src/browser.test.js +297 -0
- package/dist/src/build-manifest.d.ts +103 -0
- package/dist/src/build-manifest.js +363 -0
- package/dist/src/build-manifest.test.d.ts +1 -0
- package/dist/src/build-manifest.test.js +341 -0
- package/dist/src/capabilityRouting.d.ts +19 -0
- package/dist/src/capabilityRouting.js +56 -0
- package/dist/src/capabilityRouting.test.d.ts +1 -0
- package/dist/src/capabilityRouting.test.js +63 -0
- package/dist/src/cli-argv-preprocess.d.ts +63 -0
- package/dist/src/cli-argv-preprocess.js +303 -0
- package/dist/src/cli-argv-preprocess.test.d.ts +1 -0
- package/dist/src/cli-argv-preprocess.test.js +304 -0
- package/dist/src/cli.d.ts +79 -0
- package/dist/src/cli.js +3350 -0
- package/dist/src/cli.test.d.ts +1 -0
- package/dist/src/cli.test.js +3041 -0
- package/dist/src/commanderAdapter.d.ts +21 -0
- package/dist/src/commanderAdapter.js +208 -0
- package/dist/src/commanderAdapter.test.d.ts +1 -0
- package/dist/src/commanderAdapter.test.js +352 -0
- package/dist/src/commands/auth.d.ts +40 -0
- package/dist/src/commands/auth.js +434 -0
- package/dist/src/commands/auth.test.d.ts +1 -0
- package/dist/src/commands/auth.test.js +252 -0
- package/dist/src/commands/daemon.d.ts +9 -0
- package/dist/src/commands/daemon.js +91 -0
- package/dist/src/commands/daemon.test.d.ts +1 -0
- package/dist/src/commands/daemon.test.js +293 -0
- package/dist/src/completion-fast.d.ts +22 -0
- package/dist/src/completion-fast.js +88 -0
- package/dist/src/completion-shared.d.ts +13 -0
- package/dist/src/completion-shared.js +61 -0
- package/dist/src/completion.d.ts +21 -0
- package/dist/src/completion.js +68 -0
- package/dist/src/completion.test.d.ts +1 -0
- package/dist/src/completion.test.js +24 -0
- package/dist/src/constants.d.ts +18 -0
- package/dist/src/constants.js +41 -0
- package/dist/src/convention-audit.d.ts +50 -0
- package/dist/src/convention-audit.js +553 -0
- package/dist/src/convention-audit.test.d.ts +1 -0
- package/dist/src/convention-audit.test.js +255 -0
- package/dist/src/daemon/server.d.ts +13 -0
- package/dist/src/daemon/server.js +185 -0
- package/dist/src/daemon/server.test.d.ts +1 -0
- package/dist/src/daemon/server.test.js +132 -0
- package/dist/src/daemon-utils.d.ts +18 -0
- package/dist/src/daemon-utils.js +37 -0
- package/dist/src/daemon.d.ts +1 -0
- package/dist/src/daemon.js +27 -0
- package/dist/src/daemon.test.d.ts +1 -0
- package/dist/src/daemon.test.js +60 -0
- package/dist/src/discovery.d.ts +46 -0
- package/dist/src/discovery.js +341 -0
- package/dist/src/doctor.d.ts +39 -0
- package/dist/src/doctor.js +168 -0
- package/dist/src/doctor.test.d.ts +1 -0
- package/dist/src/doctor.test.js +302 -0
- package/dist/src/download/article-download.d.ts +72 -0
- package/dist/src/download/article-download.js +305 -0
- package/dist/src/download/article-download.test.d.ts +1 -0
- package/dist/src/download/article-download.test.js +235 -0
- package/dist/src/download/index.d.ts +71 -0
- package/dist/src/download/index.js +363 -0
- package/dist/src/download/index.test.d.ts +1 -0
- package/dist/src/download/index.test.js +174 -0
- package/dist/src/download/media-download.d.ts +49 -0
- package/dist/src/download/media-download.js +127 -0
- package/dist/src/download/media-download.test.d.ts +1 -0
- package/dist/src/download/media-download.test.js +112 -0
- package/dist/src/download/progress.d.ts +36 -0
- package/dist/src/download/progress.js +120 -0
- package/dist/src/download/progress.test.d.ts +1 -0
- package/dist/src/download/progress.test.js +36 -0
- package/dist/src/electron-apps.d.ts +31 -0
- package/dist/src/electron-apps.js +91 -0
- package/dist/src/electron-apps.test.d.ts +1 -0
- package/dist/src/electron-apps.test.js +76 -0
- package/dist/src/engine.test.d.ts +1 -0
- package/dist/src/engine.test.js +312 -0
- package/dist/src/errors.d.ts +114 -0
- package/dist/src/errors.js +174 -0
- package/dist/src/errors.test.d.ts +1 -0
- package/dist/src/errors.test.js +109 -0
- package/dist/src/execution.d.ts +24 -0
- package/dist/src/execution.js +509 -0
- package/dist/src/execution.test.d.ts +1 -0
- package/dist/src/execution.test.js +645 -0
- package/dist/src/external.d.ts +49 -0
- package/dist/src/external.js +211 -0
- package/dist/src/external.test.d.ts +1 -0
- package/dist/src/external.test.js +110 -0
- package/dist/src/help.d.ts +88 -0
- package/dist/src/help.js +556 -0
- package/dist/src/help.test.d.ts +1 -0
- package/dist/src/help.test.js +58 -0
- package/dist/src/hooks.d.ts +46 -0
- package/dist/src/hooks.js +58 -0
- package/dist/src/hooks.test.d.ts +4 -0
- package/dist/src/hooks.test.js +92 -0
- package/dist/src/interceptor.d.ts +44 -0
- package/dist/src/interceptor.js +183 -0
- package/dist/src/interceptor.test.d.ts +4 -0
- package/dist/src/interceptor.test.js +81 -0
- package/dist/src/launcher.d.ts +41 -0
- package/dist/src/launcher.js +226 -0
- package/dist/src/launcher.test.d.ts +1 -0
- package/dist/src/launcher.test.js +153 -0
- package/dist/src/logger.d.ts +26 -0
- package/dist/src/logger.js +49 -0
- package/dist/src/main.d.ts +5 -0
- package/dist/src/main.js +177 -0
- package/dist/src/manifest-types.d.ts +43 -0
- package/dist/src/manifest-types.js +9 -0
- package/dist/src/node-network.d.ts +10 -0
- package/dist/src/node-network.js +174 -0
- package/dist/src/node-network.test.d.ts +1 -0
- package/dist/src/node-network.test.js +55 -0
- package/dist/src/observation/artifact.d.ts +16 -0
- package/dist/src/observation/artifact.js +260 -0
- package/dist/src/observation/artifact.test.d.ts +1 -0
- package/dist/src/observation/artifact.test.js +121 -0
- package/dist/src/observation/events.d.ts +89 -0
- package/dist/src/observation/events.js +1 -0
- package/dist/src/observation/index.d.ts +7 -0
- package/dist/src/observation/index.js +7 -0
- package/dist/src/observation/manager.d.ts +9 -0
- package/dist/src/observation/manager.js +27 -0
- package/dist/src/observation/manager.test.d.ts +1 -0
- package/dist/src/observation/manager.test.js +13 -0
- package/dist/src/observation/redaction.d.ts +11 -0
- package/dist/src/observation/redaction.js +81 -0
- package/dist/src/observation/redaction.test.d.ts +1 -0
- package/dist/src/observation/redaction.test.js +32 -0
- package/dist/src/observation/retention.d.ts +32 -0
- package/dist/src/observation/retention.js +160 -0
- package/dist/src/observation/retention.test.d.ts +1 -0
- package/dist/src/observation/retention.test.js +118 -0
- package/dist/src/observation/ring-buffer.d.ts +22 -0
- package/dist/src/observation/ring-buffer.js +45 -0
- package/dist/src/observation/ring-buffer.test.d.ts +1 -0
- package/dist/src/observation/ring-buffer.test.js +22 -0
- package/dist/src/observation/session.d.ts +25 -0
- package/dist/src/observation/session.js +50 -0
- package/dist/src/output.d.ts +14 -0
- package/dist/src/output.js +152 -0
- package/dist/src/output.test.d.ts +1 -0
- package/dist/src/output.test.js +53 -0
- package/dist/src/package-exports.test.d.ts +1 -0
- package/dist/src/package-exports.test.js +112 -0
- package/dist/src/package-paths.d.ts +8 -0
- package/dist/src/package-paths.js +41 -0
- package/dist/src/pipeline/executor.d.ts +11 -0
- package/dist/src/pipeline/executor.js +93 -0
- package/dist/src/pipeline/executor.test.d.ts +4 -0
- package/dist/src/pipeline/executor.test.js +182 -0
- package/dist/src/pipeline/index.d.ts +5 -0
- package/dist/src/pipeline/index.js +5 -0
- package/dist/src/pipeline/registry.d.ts +28 -0
- package/dist/src/pipeline/registry.js +55 -0
- package/dist/src/pipeline/steps/browser.d.ts +13 -0
- package/dist/src/pipeline/steps/browser.js +86 -0
- package/dist/src/pipeline/steps/download.d.ts +18 -0
- package/dist/src/pipeline/steps/download.js +252 -0
- package/dist/src/pipeline/steps/download.test.d.ts +1 -0
- package/dist/src/pipeline/steps/download.test.js +102 -0
- package/dist/src/pipeline/steps/fetch.d.ts +5 -0
- package/dist/src/pipeline/steps/fetch.js +122 -0
- package/dist/src/pipeline/steps/fetch.test.d.ts +1 -0
- package/dist/src/pipeline/steps/fetch.test.js +117 -0
- package/dist/src/pipeline/steps/intercept.d.ts +5 -0
- package/dist/src/pipeline/steps/intercept.js +50 -0
- package/dist/src/pipeline/steps/tap.d.ts +12 -0
- package/dist/src/pipeline/steps/tap.js +93 -0
- package/dist/src/pipeline/steps/transform.d.ts +9 -0
- package/dist/src/pipeline/steps/transform.js +70 -0
- package/dist/src/pipeline/template.d.ts +17 -0
- package/dist/src/pipeline/template.js +344 -0
- package/dist/src/pipeline/template.test.d.ts +4 -0
- package/dist/src/pipeline/template.test.js +197 -0
- package/dist/src/pipeline/transform.test.d.ts +4 -0
- package/dist/src/pipeline/transform.test.js +133 -0
- package/dist/src/plugin-manifest.d.ts +70 -0
- package/dist/src/plugin-manifest.js +160 -0
- package/dist/src/plugin-manifest.test.d.ts +4 -0
- package/dist/src/plugin-manifest.test.js +179 -0
- package/dist/src/plugin-scaffold.d.ts +28 -0
- package/dist/src/plugin-scaffold.js +143 -0
- package/dist/src/plugin-scaffold.test.d.ts +4 -0
- package/dist/src/plugin-scaffold.test.js +83 -0
- package/dist/src/plugin.d.ts +146 -0
- package/dist/src/plugin.js +1303 -0
- package/dist/src/plugin.test.d.ts +4 -0
- package/dist/src/plugin.test.js +1363 -0
- package/dist/src/registry-api.d.ts +13 -0
- package/dist/src/registry-api.js +10 -0
- package/dist/src/registry.d.ts +123 -0
- package/dist/src/registry.js +125 -0
- package/dist/src/registry.test.d.ts +4 -0
- package/dist/src/registry.test.js +209 -0
- package/dist/src/runtime-copy.test.d.ts +1 -0
- package/dist/src/runtime-copy.test.js +29 -0
- package/dist/src/runtime-detect.d.ts +31 -0
- package/dist/src/runtime-detect.js +47 -0
- package/dist/src/runtime-detect.test.d.ts +1 -0
- package/dist/src/runtime-detect.test.js +39 -0
- package/dist/src/runtime-identity.test.d.ts +1 -0
- package/dist/src/runtime-identity.test.js +18 -0
- package/dist/src/runtime.d.ts +47 -0
- package/dist/src/runtime.js +54 -0
- package/dist/src/scripts/framework.d.ts +7 -0
- package/dist/src/scripts/framework.js +25 -0
- package/dist/src/scripts/interact.d.ts +4 -0
- package/dist/src/scripts/interact.js +20 -0
- package/dist/src/scripts/store.d.ts +13 -0
- package/dist/src/scripts/store.js +48 -0
- package/dist/src/serialization.d.ts +42 -0
- package/dist/src/serialization.js +103 -0
- package/dist/src/serialization.test.d.ts +1 -0
- package/dist/src/serialization.test.js +78 -0
- package/dist/src/skills.d.ts +14 -0
- package/dist/src/skills.js +127 -0
- package/dist/src/skills.test.d.ts +1 -0
- package/dist/src/skills.test.js +71 -0
- package/dist/src/snapshotFormatter.d.ts +11 -0
- package/dist/src/snapshotFormatter.js +338 -0
- package/dist/src/snapshotFormatter.test.d.ts +7 -0
- package/dist/src/snapshotFormatter.test.js +521 -0
- package/dist/src/tui.d.ts +28 -0
- package/dist/src/tui.js +179 -0
- package/dist/src/tui.test.d.ts +1 -0
- package/dist/src/tui.test.js +19 -0
- package/dist/src/types.d.ts +236 -0
- package/dist/src/types.js +7 -0
- package/dist/src/update-check.d.ts +66 -0
- package/dist/src/update-check.js +206 -0
- package/dist/src/update-check.test.d.ts +1 -0
- package/dist/src/update-check.test.js +117 -0
- package/dist/src/utils.d.ts +57 -0
- package/dist/src/utils.js +152 -0
- package/dist/src/utils.test.d.ts +1 -0
- package/dist/src/utils.test.js +155 -0
- package/dist/src/validate.d.ts +21 -0
- package/dist/src/validate.js +130 -0
- package/dist/src/validate.test.d.ts +9 -0
- package/dist/src/validate.test.js +90 -0
- package/dist/src/verify.d.ts +26 -0
- package/dist/src/verify.js +67 -0
- package/dist/src/version.d.ts +4 -0
- package/dist/src/version.js +22 -0
- package/dist/src/weixin-download.test.d.ts +1 -0
- package/dist/src/weixin-download.test.js +45 -0
- package/package.json +4 -3
- package/scripts/check-package-bin.mjs +95 -0
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { describe, expect, it, vi } from 'vitest';
|
|
2
|
+
const { mockGetRegistry } = vi.hoisted(() => ({
|
|
3
|
+
mockGetRegistry: vi.fn(() => new Map([
|
|
4
|
+
['github/issues', { site: 'github', name: 'issues' }],
|
|
5
|
+
])),
|
|
6
|
+
}));
|
|
7
|
+
vi.mock('./registry.js', () => ({
|
|
8
|
+
getRegistry: mockGetRegistry,
|
|
9
|
+
}));
|
|
10
|
+
import { getCompletions } from './completion.js';
|
|
11
|
+
describe('getCompletions', () => {
|
|
12
|
+
it('includes top-level built-ins that are registered outside the site registry', () => {
|
|
13
|
+
const completions = getCompletions([], 1);
|
|
14
|
+
expect(completions).toContain('plugin');
|
|
15
|
+
expect(completions).toContain('external');
|
|
16
|
+
expect(completions).not.toContain('install');
|
|
17
|
+
expect(completions).not.toContain('register');
|
|
18
|
+
expect(completions).not.toContain('setup');
|
|
19
|
+
});
|
|
20
|
+
it('still includes discovered site names', () => {
|
|
21
|
+
const completions = getCompletions([], 1);
|
|
22
|
+
expect(completions).toContain('github');
|
|
23
|
+
});
|
|
24
|
+
});
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared constants used across explore, synthesize, and pipeline modules.
|
|
3
|
+
*/
|
|
4
|
+
import { DAEMON_HEADER_NAME } from './brand.js';
|
|
5
|
+
/** Default daemon port for HTTP/WebSocket communication with browser extension */
|
|
6
|
+
export declare const DEFAULT_DAEMON_PORT = 19825;
|
|
7
|
+
export declare function unsupportedDaemonPortEnvMessage(value?: string): string;
|
|
8
|
+
export { DAEMON_HEADER_NAME };
|
|
9
|
+
/** URL query params that are volatile/ephemeral and should be stripped from patterns */
|
|
10
|
+
export declare const VOLATILE_PARAMS: Set<string>;
|
|
11
|
+
/** Search-related query parameter names */
|
|
12
|
+
export declare const SEARCH_PARAMS: Set<string>;
|
|
13
|
+
/** Pagination-related query parameter names */
|
|
14
|
+
export declare const PAGINATION_PARAMS: Set<string>;
|
|
15
|
+
/** Limit/page-size query parameter names */
|
|
16
|
+
export declare const LIMIT_PARAMS: Set<string>;
|
|
17
|
+
/** Field role → common API field names mapping */
|
|
18
|
+
export declare const FIELD_ROLES: Record<string, string[]>;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared constants used across explore, synthesize, and pipeline modules.
|
|
3
|
+
*/
|
|
4
|
+
import { CLI_COMMAND, DAEMON_HEADER_NAME, ENV_PREFIX, PRODUCT_DISPLAY_NAME } from './brand.js';
|
|
5
|
+
/** Default daemon port for HTTP/WebSocket communication with browser extension */
|
|
6
|
+
export const DEFAULT_DAEMON_PORT = 19825;
|
|
7
|
+
export function unsupportedDaemonPortEnvMessage(value) {
|
|
8
|
+
const envName = `${ENV_PREFIX}_DAEMON_PORT`;
|
|
9
|
+
const suffix = value ? ` (received ${value})` : '';
|
|
10
|
+
return `${envName} is no longer supported${suffix}. ` +
|
|
11
|
+
`The ${PRODUCT_DISPLAY_NAME} Chrome extension can only connect to localhost:${DEFAULT_DAEMON_PORT}. ` +
|
|
12
|
+
`Unset ${envName} and rerun ${CLI_COMMAND}.`;
|
|
13
|
+
}
|
|
14
|
+
export { DAEMON_HEADER_NAME };
|
|
15
|
+
/** URL query params that are volatile/ephemeral and should be stripped from patterns */
|
|
16
|
+
export const VOLATILE_PARAMS = new Set([
|
|
17
|
+
'w_rid', 'wts', '_', 'callback', 'timestamp', 't', 'nonce', 'sign',
|
|
18
|
+
]);
|
|
19
|
+
/** Search-related query parameter names */
|
|
20
|
+
export const SEARCH_PARAMS = new Set([
|
|
21
|
+
'q', 'query', 'keyword', 'search', 'wd', 'kw', 'search_query', 'w',
|
|
22
|
+
]);
|
|
23
|
+
/** Pagination-related query parameter names */
|
|
24
|
+
export const PAGINATION_PARAMS = new Set([
|
|
25
|
+
'page', 'pn', 'offset', 'cursor', 'next', 'page_num',
|
|
26
|
+
]);
|
|
27
|
+
/** Limit/page-size query parameter names */
|
|
28
|
+
export const LIMIT_PARAMS = new Set([
|
|
29
|
+
'limit', 'count', 'size', 'per_page', 'page_size', 'ps', 'num',
|
|
30
|
+
]);
|
|
31
|
+
/** Field role → common API field names mapping */
|
|
32
|
+
export const FIELD_ROLES = {
|
|
33
|
+
title: ['title', 'name', 'text', 'content', 'desc', 'description', 'headline', 'subject'],
|
|
34
|
+
url: ['url', 'uri', 'link', 'href', 'permalink', 'jump_url', 'web_url', 'share_url'],
|
|
35
|
+
author: ['author', 'username', 'user_name', 'nickname', 'nick', 'owner', 'creator', 'up_name', 'uname'],
|
|
36
|
+
score: ['score', 'hot', 'heat', 'likes', 'like_count', 'view_count', 'views', 'play', 'favorite_count', 'reply_count'],
|
|
37
|
+
time: ['time', 'created_at', 'publish_time', 'pub_time', 'date', 'ctime', 'mtime', 'pubdate', 'created'],
|
|
38
|
+
id: ['id', 'aid', 'bvid', 'mid', 'uid', 'oid', 'note_id', 'item_id'],
|
|
39
|
+
cover: ['cover', 'pic', 'image', 'thumbnail', 'poster', 'avatar'],
|
|
40
|
+
category: ['category', 'tag', 'type', 'tname', 'channel', 'section'],
|
|
41
|
+
};
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
export type ConventionRuleId = 'silent-column-drop' | 'camelCase-in-columns' | 'missing-access-metadata' | 'silent-clamp' | 'silent-empty-fallback' | 'silent-sentinel' | 'write-without-delete-pair';
|
|
2
|
+
type ManifestArg = {
|
|
3
|
+
name?: string;
|
|
4
|
+
positional?: boolean;
|
|
5
|
+
required?: boolean;
|
|
6
|
+
help?: string;
|
|
7
|
+
};
|
|
8
|
+
export type ManifestCommand = {
|
|
9
|
+
site?: string;
|
|
10
|
+
name?: string;
|
|
11
|
+
access?: string;
|
|
12
|
+
args?: ManifestArg[];
|
|
13
|
+
columns?: string[];
|
|
14
|
+
modulePath?: string;
|
|
15
|
+
sourceFile?: string;
|
|
16
|
+
};
|
|
17
|
+
export type ConventionViolation = {
|
|
18
|
+
rule: ConventionRuleId;
|
|
19
|
+
site: string;
|
|
20
|
+
name: string;
|
|
21
|
+
command: string;
|
|
22
|
+
message: string;
|
|
23
|
+
file?: string;
|
|
24
|
+
line?: number;
|
|
25
|
+
details?: Record<string, unknown>;
|
|
26
|
+
};
|
|
27
|
+
export type ConventionCategory = {
|
|
28
|
+
rule: ConventionRuleId;
|
|
29
|
+
count: number;
|
|
30
|
+
violations: ConventionViolation[];
|
|
31
|
+
};
|
|
32
|
+
export type ConventionAuditReport = {
|
|
33
|
+
ok: boolean;
|
|
34
|
+
summary: {
|
|
35
|
+
commands: number;
|
|
36
|
+
sites: number;
|
|
37
|
+
files_scanned: number;
|
|
38
|
+
violations: number;
|
|
39
|
+
};
|
|
40
|
+
categories: ConventionCategory[];
|
|
41
|
+
};
|
|
42
|
+
export type ConventionAuditOptions = {
|
|
43
|
+
projectRoot: string;
|
|
44
|
+
manifestPath?: string;
|
|
45
|
+
target?: string;
|
|
46
|
+
site?: string;
|
|
47
|
+
};
|
|
48
|
+
export declare function runConventionAudit(opts: ConventionAuditOptions): ConventionAuditReport;
|
|
49
|
+
export declare function renderConventionAuditText(report: ConventionAuditReport): string;
|
|
50
|
+
export {};
|
|
@@ -0,0 +1,553 @@
|
|
|
1
|
+
import * as fs from 'node:fs';
|
|
2
|
+
import * as path from 'node:path';
|
|
3
|
+
const RULES = [
|
|
4
|
+
'silent-column-drop',
|
|
5
|
+
'camelCase-in-columns',
|
|
6
|
+
'missing-access-metadata',
|
|
7
|
+
'silent-clamp',
|
|
8
|
+
'silent-empty-fallback',
|
|
9
|
+
'silent-sentinel',
|
|
10
|
+
'write-without-delete-pair',
|
|
11
|
+
];
|
|
12
|
+
const COLUMN_DROP_IGNORED_KEYS = new Set([
|
|
13
|
+
'ok',
|
|
14
|
+
'error',
|
|
15
|
+
]);
|
|
16
|
+
const WRITE_PAIR_RULES = [
|
|
17
|
+
{
|
|
18
|
+
match: /(^|[-_])like($|[-_])/,
|
|
19
|
+
describe: 'like',
|
|
20
|
+
expected: (name) => [name.replace(/like/g, 'unlike'), 'unlike'],
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
match: /(^|[-_])follow($|[-_])/,
|
|
24
|
+
describe: 'follow',
|
|
25
|
+
expected: (name) => [name.replace(/follow/g, 'unfollow'), 'unfollow'],
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
match: /(^|[-_])subscribe($|[-_])/,
|
|
29
|
+
describe: 'subscribe',
|
|
30
|
+
expected: (name) => [name.replace(/subscribe/g, 'unsubscribe'), 'unsubscribe'],
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
match: /(^|[-_])bookmark($|[-_])/,
|
|
34
|
+
describe: 'bookmark',
|
|
35
|
+
expected: (name) => [name.replace(/bookmark/g, 'unbookmark'), 'unbookmark'],
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
match: /(^|[-_])save($|[-_])/,
|
|
39
|
+
describe: 'save',
|
|
40
|
+
expected: (name) => [name.replace(/save/g, 'unsave'), 'unsave', 'delete', 'remove', 'rm'],
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
match: /(^|[-_])create($|[-_])/,
|
|
44
|
+
describe: 'create',
|
|
45
|
+
expected: (name) => [
|
|
46
|
+
name.replace(/create/g, 'delete'),
|
|
47
|
+
name.replace(/create/g, 'remove'),
|
|
48
|
+
'delete',
|
|
49
|
+
'remove',
|
|
50
|
+
'rm',
|
|
51
|
+
],
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
match: /(^|[-_])post($|[-_])/,
|
|
55
|
+
describe: 'post',
|
|
56
|
+
expected: (name) => [name.replace(/post/g, 'delete'), 'delete', 'remove', 'rm'],
|
|
57
|
+
},
|
|
58
|
+
];
|
|
59
|
+
export function runConventionAudit(opts) {
|
|
60
|
+
const manifestPath = opts.manifestPath ?? path.join(opts.projectRoot, 'cli-manifest.json');
|
|
61
|
+
const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf-8'));
|
|
62
|
+
const filtered = manifest.filter((entry) => matchesTarget(entry, opts));
|
|
63
|
+
const violations = [];
|
|
64
|
+
const sourceCache = new Map();
|
|
65
|
+
const scannedFiles = new Set();
|
|
66
|
+
for (const entry of filtered) {
|
|
67
|
+
const command = normalizeCommand(entry);
|
|
68
|
+
if (!command)
|
|
69
|
+
continue;
|
|
70
|
+
if (entry.access !== 'read' && entry.access !== 'write') {
|
|
71
|
+
violations.push({
|
|
72
|
+
rule: 'missing-access-metadata',
|
|
73
|
+
...command,
|
|
74
|
+
message: `${command.command} must declare access: 'read' | 'write'`,
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
for (const column of entry.columns ?? []) {
|
|
78
|
+
if (/[a-z][A-Z]/.test(column)) {
|
|
79
|
+
violations.push({
|
|
80
|
+
rule: 'camelCase-in-columns',
|
|
81
|
+
...command,
|
|
82
|
+
message: `${command.command} column "${column}" should use snake_case for agent-stable keys`,
|
|
83
|
+
details: { column },
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
const sourcePath = resolveSourcePath(opts.projectRoot, entry);
|
|
88
|
+
if (!sourcePath)
|
|
89
|
+
continue;
|
|
90
|
+
const source = readSource(sourcePath, sourceCache);
|
|
91
|
+
if (source == null)
|
|
92
|
+
continue;
|
|
93
|
+
scannedFiles.add(sourcePath);
|
|
94
|
+
violations.push(...auditColumnDrop(command, entry, source, sourcePath, opts.projectRoot));
|
|
95
|
+
violations.push(...auditTypedErrorPatterns(command, source, sourcePath, opts.projectRoot));
|
|
96
|
+
}
|
|
97
|
+
violations.push(...auditWriteDeletePair(filtered));
|
|
98
|
+
const categories = RULES.map((rule) => {
|
|
99
|
+
const items = violations.filter((violation) => violation.rule === rule);
|
|
100
|
+
return { rule, count: items.length, violations: items };
|
|
101
|
+
});
|
|
102
|
+
const commandCount = filtered.filter((entry) => normalizeCommand(entry) != null).length;
|
|
103
|
+
const sites = new Set(filtered.map((entry) => entry.site).filter((site) => typeof site === 'string'));
|
|
104
|
+
return {
|
|
105
|
+
ok: violations.length === 0,
|
|
106
|
+
summary: {
|
|
107
|
+
commands: commandCount,
|
|
108
|
+
sites: sites.size,
|
|
109
|
+
files_scanned: scannedFiles.size,
|
|
110
|
+
violations: violations.length,
|
|
111
|
+
},
|
|
112
|
+
categories,
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
export function renderConventionAuditText(report) {
|
|
116
|
+
const lines = [];
|
|
117
|
+
lines.push('Convention Audit Report');
|
|
118
|
+
lines.push(`Scanned ${report.summary.commands} command(s) across ${report.summary.sites} site(s), ${report.summary.files_scanned} source file(s).`);
|
|
119
|
+
lines.push(`Violations: ${report.summary.violations}`);
|
|
120
|
+
lines.push('');
|
|
121
|
+
for (const category of report.categories) {
|
|
122
|
+
const marker = category.count === 0 ? 'OK' : String(category.count);
|
|
123
|
+
lines.push(`${category.rule}: ${marker}`);
|
|
124
|
+
for (const violation of category.violations) {
|
|
125
|
+
const location = violation.file ? ` (${violation.file}${violation.line ? `:${violation.line}` : ''})` : '';
|
|
126
|
+
lines.push(` - ${violation.command}${location}`);
|
|
127
|
+
lines.push(` ${violation.message}`);
|
|
128
|
+
const details = formatDetails(violation.details);
|
|
129
|
+
if (details)
|
|
130
|
+
lines.push(` ${details}`);
|
|
131
|
+
}
|
|
132
|
+
lines.push('');
|
|
133
|
+
}
|
|
134
|
+
if (report.ok) {
|
|
135
|
+
lines.push('OK - no convention violations found.');
|
|
136
|
+
}
|
|
137
|
+
else {
|
|
138
|
+
lines.push('Run with -f yaml or -f json for machine-readable output.');
|
|
139
|
+
}
|
|
140
|
+
return lines.join('\n');
|
|
141
|
+
}
|
|
142
|
+
function normalizeCommand(entry) {
|
|
143
|
+
if (typeof entry.site !== 'string' || typeof entry.name !== 'string')
|
|
144
|
+
return null;
|
|
145
|
+
return {
|
|
146
|
+
site: entry.site,
|
|
147
|
+
name: entry.name,
|
|
148
|
+
command: `${entry.site}/${entry.name}`,
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
function matchesTarget(entry, opts) {
|
|
152
|
+
const target = opts.target?.trim();
|
|
153
|
+
const site = opts.site?.trim();
|
|
154
|
+
if (site && entry.site !== site)
|
|
155
|
+
return false;
|
|
156
|
+
if (!target)
|
|
157
|
+
return true;
|
|
158
|
+
if (target.includes('/'))
|
|
159
|
+
return `${entry.site}/${entry.name}` === target;
|
|
160
|
+
return entry.site === target;
|
|
161
|
+
}
|
|
162
|
+
function resolveSourcePath(projectRoot, entry) {
|
|
163
|
+
const relative = entry.sourceFile ?? entry.modulePath;
|
|
164
|
+
if (!relative)
|
|
165
|
+
return null;
|
|
166
|
+
const sourcePath = path.join(projectRoot, 'clis', relative);
|
|
167
|
+
return fs.existsSync(sourcePath) ? sourcePath : null;
|
|
168
|
+
}
|
|
169
|
+
function readSource(sourcePath, cache) {
|
|
170
|
+
if (cache.has(sourcePath))
|
|
171
|
+
return cache.get(sourcePath) ?? null;
|
|
172
|
+
try {
|
|
173
|
+
const source = fs.readFileSync(sourcePath, 'utf-8');
|
|
174
|
+
cache.set(sourcePath, source);
|
|
175
|
+
return source;
|
|
176
|
+
}
|
|
177
|
+
catch {
|
|
178
|
+
cache.set(sourcePath, null);
|
|
179
|
+
return null;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
function auditColumnDrop(command, entry, source, sourcePath, projectRoot) {
|
|
183
|
+
const columns = new Set(entry.columns ?? []);
|
|
184
|
+
if (columns.size === 0)
|
|
185
|
+
return [];
|
|
186
|
+
const transformedIntermediateKeys = findTransformedIntermediateKeys(source, columns);
|
|
187
|
+
const seen = new Set();
|
|
188
|
+
const violations = [];
|
|
189
|
+
for (const object of extractPotentialRowObjects(source)) {
|
|
190
|
+
if (isFailureDiagnosticObject(object.text))
|
|
191
|
+
continue;
|
|
192
|
+
const keys = extractObjectKeys(object.text)
|
|
193
|
+
.filter((key) => !COLUMN_DROP_IGNORED_KEYS.has(key));
|
|
194
|
+
if (keys.length < 2)
|
|
195
|
+
continue;
|
|
196
|
+
if (looksLikeCommandMetadata(keys))
|
|
197
|
+
continue;
|
|
198
|
+
const overlap = keys.filter((key) => columns.has(key));
|
|
199
|
+
if (overlap.length === 0)
|
|
200
|
+
continue;
|
|
201
|
+
const missing = keys.filter((key) => !columns.has(key) && !transformedIntermediateKeys.has(key));
|
|
202
|
+
if (missing.length === 0)
|
|
203
|
+
continue;
|
|
204
|
+
const signature = missing.sort().join(',');
|
|
205
|
+
if (seen.has(signature))
|
|
206
|
+
continue;
|
|
207
|
+
seen.add(signature);
|
|
208
|
+
violations.push({
|
|
209
|
+
rule: 'silent-column-drop',
|
|
210
|
+
...command,
|
|
211
|
+
file: relativeFile(projectRoot, sourcePath),
|
|
212
|
+
line: lineForIndex(source, object.index),
|
|
213
|
+
message: `${command.command} row emits key(s) not present in columns: ${missing.join(', ')}`,
|
|
214
|
+
details: { emitted_keys: keys, columns: [...columns], missing },
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
return violations;
|
|
218
|
+
}
|
|
219
|
+
function auditTypedErrorPatterns(command, source, sourcePath, projectRoot) {
|
|
220
|
+
const violations = [];
|
|
221
|
+
const relative = relativeFile(projectRoot, sourcePath);
|
|
222
|
+
const lines = source.split(/\r?\n/);
|
|
223
|
+
const catchRanges = findCatchBlockRanges(source);
|
|
224
|
+
let offset = 0;
|
|
225
|
+
lines.forEach((line, index) => {
|
|
226
|
+
if (/Math\.min\s*\([^)]*limit[^)]*\)/i.test(line)) {
|
|
227
|
+
violations.push({
|
|
228
|
+
rule: 'silent-clamp',
|
|
229
|
+
...command,
|
|
230
|
+
file: relative,
|
|
231
|
+
line: index + 1,
|
|
232
|
+
message: 'limit is clamped with Math.min; prefer validating and throwing ArgumentError on invalid input',
|
|
233
|
+
details: { text: line.trim() },
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
const emptyReturnIndex = line.search(/\breturn\s+\[\s*\]\s*;?/);
|
|
237
|
+
if (emptyReturnIndex >= 0 && isInsideAnyRange(offset + emptyReturnIndex, catchRanges)) {
|
|
238
|
+
violations.push({
|
|
239
|
+
rule: 'silent-empty-fallback',
|
|
240
|
+
...command,
|
|
241
|
+
file: relative,
|
|
242
|
+
line: index + 1,
|
|
243
|
+
message: 'empty array fallback hides fetch/parse failures from agents; prefer a typed error when data is expected',
|
|
244
|
+
details: { text: line.trim() },
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
const sentinel = /(?:\?\?|\|\|)\s*(['"])(unknown|Unknown|UNKNOWN|N\/A|n\/a|NA|未知|-)\1/.exec(line);
|
|
248
|
+
if (sentinel) {
|
|
249
|
+
if (!isThrowMessageLine(line)) {
|
|
250
|
+
violations.push({
|
|
251
|
+
rule: 'silent-sentinel',
|
|
252
|
+
...command,
|
|
253
|
+
file: relative,
|
|
254
|
+
line: index + 1,
|
|
255
|
+
message: `sentinel fallback ${sentinel[0].trim()} can turn missing data into fake data; prefer dropping the field or throwing a typed error`,
|
|
256
|
+
details: { text: line.trim() },
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
offset += line.length + 1;
|
|
261
|
+
});
|
|
262
|
+
return dedupeViolations(violations);
|
|
263
|
+
}
|
|
264
|
+
function isThrowMessageLine(line) {
|
|
265
|
+
// Only single-line `throw new X(...)` diagnostics are ignored. Multi-line
|
|
266
|
+
// throw expressions with row-like sentinel fallbacks still stay visible.
|
|
267
|
+
return /\bthrow\s+new\b/.test(line);
|
|
268
|
+
}
|
|
269
|
+
function auditWriteDeletePair(entries) {
|
|
270
|
+
const bySite = new Map();
|
|
271
|
+
for (const entry of entries) {
|
|
272
|
+
if (!entry.site || !entry.name)
|
|
273
|
+
continue;
|
|
274
|
+
const list = bySite.get(entry.site) ?? [];
|
|
275
|
+
list.push(entry);
|
|
276
|
+
bySite.set(entry.site, list);
|
|
277
|
+
}
|
|
278
|
+
const violations = [];
|
|
279
|
+
for (const [site, siteEntries] of bySite) {
|
|
280
|
+
const names = new Set(siteEntries.map((entry) => entry.name).filter((name) => typeof name === 'string'));
|
|
281
|
+
for (const entry of siteEntries) {
|
|
282
|
+
if (entry.access !== 'write' || !entry.name)
|
|
283
|
+
continue;
|
|
284
|
+
const pair = WRITE_PAIR_RULES.find((rule) => rule.match.test(entry.name));
|
|
285
|
+
if (!pair)
|
|
286
|
+
continue;
|
|
287
|
+
const expected = [...new Set(pair.expected(entry.name).filter((name) => name !== entry.name))];
|
|
288
|
+
if (expected.some((name) => names.has(name)))
|
|
289
|
+
continue;
|
|
290
|
+
violations.push({
|
|
291
|
+
rule: 'write-without-delete-pair',
|
|
292
|
+
site,
|
|
293
|
+
name: entry.name,
|
|
294
|
+
command: `${site}/${entry.name}`,
|
|
295
|
+
message: `write command "${entry.name}" looks like ${pair.describe} but no matching undo/delete command exists on this site`,
|
|
296
|
+
details: { expected_any_of: expected },
|
|
297
|
+
});
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
return violations;
|
|
301
|
+
}
|
|
302
|
+
function extractPotentialRowObjects(source) {
|
|
303
|
+
const objects = [];
|
|
304
|
+
const triggers = [
|
|
305
|
+
/\.push\s*\(\s*{/g,
|
|
306
|
+
/\breturn\s+(?:\(\s*)?{/g,
|
|
307
|
+
/=>\s*\(\s*{/g,
|
|
308
|
+
/\bmap\s*:\s*{/g,
|
|
309
|
+
];
|
|
310
|
+
for (const trigger of triggers) {
|
|
311
|
+
for (const match of source.matchAll(trigger)) {
|
|
312
|
+
const token = match[0];
|
|
313
|
+
const openOffset = token.lastIndexOf('{');
|
|
314
|
+
if (match.index === undefined || openOffset < 0)
|
|
315
|
+
continue;
|
|
316
|
+
const index = match.index + openOffset;
|
|
317
|
+
const text = readBalancedBlock(source, index);
|
|
318
|
+
if (text)
|
|
319
|
+
objects.push({ text, index });
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
return objects;
|
|
323
|
+
}
|
|
324
|
+
function findCatchBlockRanges(source) {
|
|
325
|
+
const ranges = [];
|
|
326
|
+
for (const match of source.matchAll(/\bcatch\s*(?:\([^)]*\))?\s*{/g)) {
|
|
327
|
+
if (match.index === undefined)
|
|
328
|
+
continue;
|
|
329
|
+
const openIndex = match.index + match[0].lastIndexOf('{');
|
|
330
|
+
const end = findBalancedBlockEnd(source, openIndex);
|
|
331
|
+
if (end >= 0)
|
|
332
|
+
ranges.push({ start: openIndex, end });
|
|
333
|
+
}
|
|
334
|
+
return ranges;
|
|
335
|
+
}
|
|
336
|
+
function findBalancedBlockEnd(source, openIndex) {
|
|
337
|
+
let depth = 0;
|
|
338
|
+
let quote = null;
|
|
339
|
+
let escaped = false;
|
|
340
|
+
for (let i = openIndex; i < source.length; i++) {
|
|
341
|
+
const ch = source[i];
|
|
342
|
+
if (quote) {
|
|
343
|
+
if (escaped) {
|
|
344
|
+
escaped = false;
|
|
345
|
+
}
|
|
346
|
+
else if (ch === '\\') {
|
|
347
|
+
escaped = true;
|
|
348
|
+
}
|
|
349
|
+
else if (ch === quote) {
|
|
350
|
+
quote = null;
|
|
351
|
+
}
|
|
352
|
+
continue;
|
|
353
|
+
}
|
|
354
|
+
if (ch === '"' || ch === "'" || ch === '`') {
|
|
355
|
+
quote = ch;
|
|
356
|
+
continue;
|
|
357
|
+
}
|
|
358
|
+
if (ch === '{')
|
|
359
|
+
depth++;
|
|
360
|
+
if (ch === '}') {
|
|
361
|
+
depth--;
|
|
362
|
+
if (depth === 0)
|
|
363
|
+
return i;
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
return -1;
|
|
367
|
+
}
|
|
368
|
+
function isInsideAnyRange(index, ranges) {
|
|
369
|
+
return ranges.some((range) => index >= range.start && index <= range.end);
|
|
370
|
+
}
|
|
371
|
+
function readBalancedBlock(source, openIndex) {
|
|
372
|
+
let depth = 0;
|
|
373
|
+
let quote = null;
|
|
374
|
+
let escaped = false;
|
|
375
|
+
for (let i = openIndex; i < source.length; i++) {
|
|
376
|
+
const ch = source[i];
|
|
377
|
+
if (quote) {
|
|
378
|
+
if (escaped) {
|
|
379
|
+
escaped = false;
|
|
380
|
+
}
|
|
381
|
+
else if (ch === '\\') {
|
|
382
|
+
escaped = true;
|
|
383
|
+
}
|
|
384
|
+
else if (ch === quote) {
|
|
385
|
+
quote = null;
|
|
386
|
+
}
|
|
387
|
+
continue;
|
|
388
|
+
}
|
|
389
|
+
if (ch === '"' || ch === "'" || ch === '`') {
|
|
390
|
+
quote = ch;
|
|
391
|
+
continue;
|
|
392
|
+
}
|
|
393
|
+
if (ch === '{')
|
|
394
|
+
depth++;
|
|
395
|
+
if (ch === '}') {
|
|
396
|
+
depth--;
|
|
397
|
+
if (depth === 0)
|
|
398
|
+
return source.slice(openIndex, i + 1);
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
return null;
|
|
402
|
+
}
|
|
403
|
+
function extractObjectKeys(objectText) {
|
|
404
|
+
return [...new Set(extractObjectProperties(objectText).map((property) => property.key))];
|
|
405
|
+
}
|
|
406
|
+
function extractObjectProperties(objectText) {
|
|
407
|
+
const body = objectText.trim().replace(/^\{/, '').replace(/\}$/, '');
|
|
408
|
+
const parts = splitTopLevel(body, ',');
|
|
409
|
+
const properties = [];
|
|
410
|
+
for (const part of parts) {
|
|
411
|
+
const property = extractProperty(part);
|
|
412
|
+
if (property)
|
|
413
|
+
properties.push(property);
|
|
414
|
+
}
|
|
415
|
+
return properties;
|
|
416
|
+
}
|
|
417
|
+
function extractProperty(part) {
|
|
418
|
+
const trimmed = part.trim();
|
|
419
|
+
if (!trimmed || trimmed.startsWith('...') || trimmed.startsWith('['))
|
|
420
|
+
return null;
|
|
421
|
+
const colonIndex = findTopLevelChar(trimmed, ':');
|
|
422
|
+
if (colonIndex >= 0) {
|
|
423
|
+
const raw = trimmed.slice(0, colonIndex).trim();
|
|
424
|
+
const value = trimmed.slice(colonIndex + 1).trim();
|
|
425
|
+
if (/^['"][^'"]+['"]$/.test(raw))
|
|
426
|
+
return { key: raw.slice(1, -1), value };
|
|
427
|
+
const identifier = /^([A-Za-z_$][\w$]*)$/.exec(raw);
|
|
428
|
+
return identifier ? { key: identifier[1], value } : null;
|
|
429
|
+
}
|
|
430
|
+
const shorthand = /^([A-Za-z_$][\w$]*)\b/.exec(trimmed);
|
|
431
|
+
return shorthand ? { key: shorthand[1], value: shorthand[1] } : null;
|
|
432
|
+
}
|
|
433
|
+
function isFailureDiagnosticObject(objectText) {
|
|
434
|
+
const ok = extractObjectProperties(objectText).find((property) => property.key === 'ok');
|
|
435
|
+
return ok != null && /^false\b/.test(ok.value);
|
|
436
|
+
}
|
|
437
|
+
function findTransformedIntermediateKeys(source, columns) {
|
|
438
|
+
const transformed = new Set();
|
|
439
|
+
for (const object of extractArrowReturnObjects(source)) {
|
|
440
|
+
for (const property of extractObjectProperties(object.text)) {
|
|
441
|
+
if (!columns.has(property.key))
|
|
442
|
+
continue;
|
|
443
|
+
for (const match of property.value.matchAll(/\b([A-Za-z_$][\w$]*(?:Raw|Class))\b/g)) {
|
|
444
|
+
const rawKey = match[1];
|
|
445
|
+
if (rawKey !== property.key)
|
|
446
|
+
transformed.add(rawKey);
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
return transformed;
|
|
451
|
+
}
|
|
452
|
+
function extractArrowReturnObjects(source) {
|
|
453
|
+
const objects = [];
|
|
454
|
+
for (const match of source.matchAll(/=>\s*\(\s*{/g)) {
|
|
455
|
+
const token = match[0];
|
|
456
|
+
const openOffset = token.lastIndexOf('{');
|
|
457
|
+
if (match.index === undefined || openOffset < 0)
|
|
458
|
+
continue;
|
|
459
|
+
const index = match.index + openOffset;
|
|
460
|
+
const text = readBalancedBlock(source, index);
|
|
461
|
+
if (text)
|
|
462
|
+
objects.push({ text, index });
|
|
463
|
+
}
|
|
464
|
+
return objects;
|
|
465
|
+
}
|
|
466
|
+
function splitTopLevel(input, separator) {
|
|
467
|
+
const parts = [];
|
|
468
|
+
let start = 0;
|
|
469
|
+
let depth = 0;
|
|
470
|
+
let quote = null;
|
|
471
|
+
let escaped = false;
|
|
472
|
+
for (let i = 0; i < input.length; i++) {
|
|
473
|
+
const ch = input[i];
|
|
474
|
+
if (quote) {
|
|
475
|
+
if (escaped)
|
|
476
|
+
escaped = false;
|
|
477
|
+
else if (ch === '\\')
|
|
478
|
+
escaped = true;
|
|
479
|
+
else if (ch === quote)
|
|
480
|
+
quote = null;
|
|
481
|
+
continue;
|
|
482
|
+
}
|
|
483
|
+
if (ch === '"' || ch === "'" || ch === '`') {
|
|
484
|
+
quote = ch;
|
|
485
|
+
continue;
|
|
486
|
+
}
|
|
487
|
+
if (ch === '{' || ch === '[' || ch === '(')
|
|
488
|
+
depth++;
|
|
489
|
+
if (ch === '}' || ch === ']' || ch === ')')
|
|
490
|
+
depth--;
|
|
491
|
+
if (depth === 0 && ch === separator) {
|
|
492
|
+
parts.push(input.slice(start, i));
|
|
493
|
+
start = i + 1;
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
parts.push(input.slice(start));
|
|
497
|
+
return parts;
|
|
498
|
+
}
|
|
499
|
+
function findTopLevelChar(input, target) {
|
|
500
|
+
let depth = 0;
|
|
501
|
+
let quote = null;
|
|
502
|
+
let escaped = false;
|
|
503
|
+
for (let i = 0; i < input.length; i++) {
|
|
504
|
+
const ch = input[i];
|
|
505
|
+
if (quote) {
|
|
506
|
+
if (escaped)
|
|
507
|
+
escaped = false;
|
|
508
|
+
else if (ch === '\\')
|
|
509
|
+
escaped = true;
|
|
510
|
+
else if (ch === quote)
|
|
511
|
+
quote = null;
|
|
512
|
+
continue;
|
|
513
|
+
}
|
|
514
|
+
if (ch === '"' || ch === "'" || ch === '`') {
|
|
515
|
+
quote = ch;
|
|
516
|
+
continue;
|
|
517
|
+
}
|
|
518
|
+
if (ch === '{' || ch === '[' || ch === '(')
|
|
519
|
+
depth++;
|
|
520
|
+
if (ch === '}' || ch === ']' || ch === ')')
|
|
521
|
+
depth--;
|
|
522
|
+
if (depth === 0 && ch === target)
|
|
523
|
+
return i;
|
|
524
|
+
}
|
|
525
|
+
return -1;
|
|
526
|
+
}
|
|
527
|
+
function looksLikeCommandMetadata(keys) {
|
|
528
|
+
const set = new Set(keys);
|
|
529
|
+
return set.has('site') && (set.has('description') || set.has('access') || set.has('strategy'));
|
|
530
|
+
}
|
|
531
|
+
function lineForIndex(source, index) {
|
|
532
|
+
return source.slice(0, index).split(/\r?\n/).length;
|
|
533
|
+
}
|
|
534
|
+
function relativeFile(projectRoot, sourcePath) {
|
|
535
|
+
return path.relative(projectRoot, sourcePath).replaceAll(path.sep, '/');
|
|
536
|
+
}
|
|
537
|
+
function formatDetails(details) {
|
|
538
|
+
if (!details)
|
|
539
|
+
return '';
|
|
540
|
+
return Object.entries(details)
|
|
541
|
+
.map(([key, value]) => `${key}: ${Array.isArray(value) ? value.join(', ') : String(value)}`)
|
|
542
|
+
.join(' | ');
|
|
543
|
+
}
|
|
544
|
+
function dedupeViolations(violations) {
|
|
545
|
+
const seen = new Set();
|
|
546
|
+
return violations.filter((violation) => {
|
|
547
|
+
const key = `${violation.rule}:${violation.command}:${violation.file}:${violation.line}:${violation.message}`;
|
|
548
|
+
if (seen.has(key))
|
|
549
|
+
return false;
|
|
550
|
+
seen.add(key);
|
|
551
|
+
return true;
|
|
552
|
+
});
|
|
553
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|