jscpd-rs 0.1.0

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 (96) hide show
  1. package/CHANGELOG.md +69 -0
  2. package/Cargo.lock +1323 -0
  3. package/Cargo.toml +54 -0
  4. package/LICENSE +21 -0
  5. package/README.md +372 -0
  6. package/docs/api-parity.md +49 -0
  7. package/docs/cloning-plan.md +281 -0
  8. package/docs/compat-baseline.md +535 -0
  9. package/docs/format-porting.md +86 -0
  10. package/docs/junior-task-template.md +62 -0
  11. package/docs/junior-workflow.md +87 -0
  12. package/docs/migrating-from-jscpd.md +193 -0
  13. package/docs/npm-release.md +116 -0
  14. package/docs/public-benchmark-suite.md +81 -0
  15. package/docs/release-checklist.md +200 -0
  16. package/docs/release-decisions.md +103 -0
  17. package/docs/release-readiness.md +51 -0
  18. package/docs/upstream-bugs.md +501 -0
  19. package/docs/upstream-issue-drafts.md +393 -0
  20. package/docs/user-guide.md +309 -0
  21. package/examples/dump_oxc_tokens.rs +112 -0
  22. package/examples/library_api.rs +42 -0
  23. package/npm/bin/jscpd-rs.js +6 -0
  24. package/npm/bin/jscpd-server.js +6 -0
  25. package/npm/lib/run-binary.js +68 -0
  26. package/npm/scripts/postinstall.js +50 -0
  27. package/package.json +53 -0
  28. package/skills/dry-refactoring/SKILL.md +63 -0
  29. package/skills/jscpd/SKILL.md +85 -0
  30. package/src/app.rs +512 -0
  31. package/src/bin/jscpd-server.rs +429 -0
  32. package/src/blame.rs +130 -0
  33. package/src/cli/config.rs +543 -0
  34. package/src/cli/parsing.rs +301 -0
  35. package/src/cli/tests.rs +543 -0
  36. package/src/cli.rs +671 -0
  37. package/src/detector/matching/secondary.rs +387 -0
  38. package/src/detector/matching.rs +274 -0
  39. package/src/detector/model.rs +190 -0
  40. package/src/detector/prepare.rs +71 -0
  41. package/src/detector/skip_local.rs +40 -0
  42. package/src/detector/statistics.rs +138 -0
  43. package/src/detector/store.rs +96 -0
  44. package/src/detector/tests.rs +238 -0
  45. package/src/detector.rs +265 -0
  46. package/src/files/discovery.rs +508 -0
  47. package/src/files/gitignore.rs +203 -0
  48. package/src/files/paths.rs +68 -0
  49. package/src/files/shebang.rs +106 -0
  50. package/src/files/tests.rs +523 -0
  51. package/src/files.rs +25 -0
  52. package/src/formats.rs +570 -0
  53. package/src/lib.rs +433 -0
  54. package/src/main.rs +26 -0
  55. package/src/report/ai.rs +125 -0
  56. package/src/report/badge.rs +238 -0
  57. package/src/report/console.rs +180 -0
  58. package/src/report/console_common.rs +37 -0
  59. package/src/report/console_full.rs +139 -0
  60. package/src/report/csv.rs +65 -0
  61. package/src/report/escape.rs +8 -0
  62. package/src/report/file_output.rs +28 -0
  63. package/src/report/html/assets.rs +47 -0
  64. package/src/report/html.rs +336 -0
  65. package/src/report/json.rs +119 -0
  66. package/src/report/markdown.rs +125 -0
  67. package/src/report/sarif.rs +302 -0
  68. package/src/report/silent.rs +22 -0
  69. package/src/report/source.rs +38 -0
  70. package/src/report/summary.rs +50 -0
  71. package/src/report/test_support.rs +133 -0
  72. package/src/report/threshold.rs +76 -0
  73. package/src/report/xcode.rs +90 -0
  74. package/src/report/xml.rs +119 -0
  75. package/src/report.rs +250 -0
  76. package/src/server/mcp.rs +942 -0
  77. package/src/server.rs +1081 -0
  78. package/src/tokenizer/apex.rs +97 -0
  79. package/src/tokenizer/blocks.rs +532 -0
  80. package/src/tokenizer/embedded.rs +106 -0
  81. package/src/tokenizer/generic.rs +511 -0
  82. package/src/tokenizer/hash.rs +27 -0
  83. package/src/tokenizer/ignore.rs +33 -0
  84. package/src/tokenizer/line_index.rs +33 -0
  85. package/src/tokenizer/markdown.rs +289 -0
  86. package/src/tokenizer/markup_attrs.rs +289 -0
  87. package/src/tokenizer/oxc/fallback.rs +275 -0
  88. package/src/tokenizer/oxc/jsx.rs +168 -0
  89. package/src/tokenizer/oxc/kind.rs +177 -0
  90. package/src/tokenizer/oxc/lexical.rs +67 -0
  91. package/src/tokenizer/oxc.rs +659 -0
  92. package/src/tokenizer/scan.rs +88 -0
  93. package/src/tokenizer/tap.rs +150 -0
  94. package/src/tokenizer/tests.rs +915 -0
  95. package/src/tokenizer.rs +328 -0
  96. package/src/verbose.rs +195 -0
@@ -0,0 +1,62 @@
1
+ # Junior Task Template
2
+
3
+ Use this as the prompt body for `pi --no-session --tools read,edit,bash -p`.
4
+
5
+ ```text
6
+ Junior helper task.
7
+ Goal: <one concrete deliverable>
8
+ Scope: edit only <exact file list>; read <exact reference files>.
9
+ Allowed actions:
10
+ - read <files>
11
+ - edit <files>
12
+ - run exactly `<verification command>` from <worktree path>
13
+ Verification: <exact command and expected high-level result>
14
+ Rules:
15
+ - stay in scope
16
+ - follow the nearest existing pattern
17
+ - do not change production logic unless explicitly requested
18
+ - do not edit generated files unless the task says so
19
+ - do not touch external repositories
20
+ - stop on blockers instead of guessing
21
+ Output: Russian report with Result, Evidence, Verification, Blockers.
22
+ ```
23
+
24
+ ## Example: Add One Test
25
+
26
+ ```text
27
+ Junior helper task.
28
+ Goal: Add one unit test proving weak mode skips TOML hash comments in the generic tokenizer.
29
+ Scope: edit only src/tokenizer.rs, preferably only the #[cfg(test)] module.
30
+ Allowed actions:
31
+ - read src/tokenizer.rs
32
+ - edit src/tokenizer.rs
33
+ - run exactly `cargo test tokenizer::tests::weak_mode_skips_generic_toml_comments` from /tmp/jscpd-rs-junior/toml-comments
34
+ Verification: the exact cargo test above passes.
35
+ Rules:
36
+ - follow the existing weak_mode_skips_generic_comments test style
37
+ - do not change production tokenizer logic
38
+ - use format `toml`
39
+ - assert exact token slices for the non-comment tokens
40
+ - stop on blockers
41
+ Output: Russian report with Result, Evidence, Verification, Blockers.
42
+ ```
43
+
44
+ ## Example: Add One Format Smoke Fixture
45
+
46
+ ```text
47
+ Junior helper task.
48
+ Goal: Add a tiny smoke fixture and test for format <format>.
49
+ Scope: edit only <fixture files> and <one test file>.
50
+ Allowed actions:
51
+ - read docs/format-porting.md
52
+ - read the nearest existing test
53
+ - edit only the files listed in Scope
54
+ - run exactly `scripts/check-format.sh <format> <target>` from /tmp/jscpd-rs-junior/<task>
55
+ Verification: the exact check-format command passes.
56
+ Rules:
57
+ - keep fixtures minimal
58
+ - do not claim upstream parity
59
+ - do not edit src/formats.rs by hand
60
+ - stop on blockers
61
+ Output: Russian report with Result, Evidence, Verification, Blockers.
62
+ ```
@@ -0,0 +1,87 @@
1
+ # Junior Workflow
2
+
3
+ This project can use a local junior agent for bounded implementation tasks. The
4
+ main agent remains responsible for scope, architecture, review, verification,
5
+ commits, and pushes.
6
+
7
+ ## Rules
8
+
9
+ - Run at most one junior agent process at a time.
10
+ - Do not run junior work in the main checkout. Use a dedicated git worktree.
11
+ - Give small implementation tasks with an example, exact files, and exact tests.
12
+ - Do not delegate architecture, compatibility policy, broad reviews, or release
13
+ decisions.
14
+ - Do not let a junior touch external repositories outside the dedicated
15
+ worktree.
16
+ - Treat junior output as a patch proposal. Review every diff before merging.
17
+ - Prefer `pi` for one-shot tasks because `--no-session` and `--tools` make the
18
+ allowed surface explicit. Use `opencode` only as fallback.
19
+
20
+ ## Worktree Loop
21
+
22
+ Create a worktree:
23
+
24
+ ```bash
25
+ scripts/junior-worktree.sh <task-slug>
26
+ ```
27
+
28
+ Run the junior from the printed worktree path:
29
+
30
+ ```bash
31
+ cd "${JUNIOR_WORKTREE_ROOT:-${TMPDIR:-/tmp}/jscpd-rs-junior}/<task-slug>"
32
+ pi --no-session --tools read,edit,bash -p '<prompt from docs/junior-task-template.md>'
33
+ ```
34
+
35
+ Review from the main checkout:
36
+
37
+ ```bash
38
+ git -C "${JUNIOR_WORKTREE_ROOT:-${TMPDIR:-/tmp}/jscpd-rs-junior}/<task-slug>" status --short
39
+ git -C "${JUNIOR_WORKTREE_ROOT:-${TMPDIR:-/tmp}/jscpd-rs-junior}/<task-slug>" diff
40
+ ```
41
+
42
+ If the patch is useful, apply it deliberately from the main checkout using
43
+ `git diff`, `git apply`, cherry-pick, or a manual patch. After review:
44
+
45
+ ```bash
46
+ git worktree remove "${JUNIOR_WORKTREE_ROOT:-${TMPDIR:-/tmp}/jscpd-rs-junior}/<task-slug>"
47
+ git branch -D junior/<branch-name>
48
+ ```
49
+
50
+ ## Good Junior Tasks
51
+
52
+ - Add one unit test following an existing nearby test.
53
+ - Add one small reporter or output-format test after the implementation exists.
54
+ - Add support for one comment style in a generic/native tokenizer.
55
+ - Add or update a small fixture for one format.
56
+ - Port one small function from upstream when the target shape is already clear.
57
+ - Run `scripts/check-format.sh <format> <target>` and report exact output.
58
+
59
+ ## Prompt Notes
60
+
61
+ - Prefer implementation or test tasks over broad read-only scouting. A scout
62
+ prompt that is too procedural may produce a task plan instead of executing the
63
+ checks.
64
+ - For read-only fact gathering, explicitly say: "Execute the checks now; do not
65
+ write instructions or a plan."
66
+ - Keep fact-gathering prompts to exact files and exact commands. If there are
67
+ many commands, give a shell loop instead of asking the junior to design one.
68
+ - Do not use junior output as evidence until the main agent verifies the exact
69
+ commands or diff.
70
+
71
+ ## Bad Junior Tasks
72
+
73
+ - Review a whole commit or subsystem.
74
+ - Decide whether compatibility is acceptable.
75
+ - Refactor detector/tokenizer architecture.
76
+ - Change generated format registry by hand.
77
+ - Touch multiple unrelated files.
78
+ - Run broad benchmarks or modify local third-party repositories.
79
+
80
+ ## Review Checklist
81
+
82
+ - The diff only touches allowed files.
83
+ - The implementation follows an existing local pattern.
84
+ - Tests are exact enough to catch broken structure, not only `contains` checks.
85
+ - Commands in the report match commands actually run.
86
+ - `cargo fmt`, `cargo test`, and relevant scripts pass from the main checkout.
87
+ - Any accepted patch is committed by the main agent, not by the junior.
@@ -0,0 +1,193 @@
1
+ # Migrating From jscpd
2
+
3
+ `jscpd-rs` is a native Rust implementation of the common upstream `jscpd`
4
+ workflow. It scans source trees, reports duplicated fragments, and can fail CI
5
+ when duplication crosses a configured threshold.
6
+
7
+ The migration goal for the first release is practical CLI/reporting
8
+ compatibility for CI and local scans, not exact JavaScript package API parity.
9
+
10
+ ## Quick Command Mapping
11
+
12
+ Cargo install:
13
+
14
+ ```bash
15
+ cargo install jscpd-rs --locked
16
+ jscpd --threshold 5 --exitCode 1 src
17
+ ```
18
+
19
+ npm/npx after npm publication:
20
+
21
+ ```bash
22
+ npx jscpd-rs --threshold 5 --exitCode 1 src
23
+ ```
24
+
25
+ Common upstream command:
26
+
27
+ ```bash
28
+ npx jscpd src --reporters console,json --threshold 5 --exitCode 1
29
+ ```
30
+
31
+ Equivalent `jscpd-rs` command:
32
+
33
+ ```bash
34
+ jscpd src --reporters console,json --threshold 5 --exitCode 1
35
+ ```
36
+
37
+ When installed from npm, the package exposes `jscpd-rs`, `jscpd`, and
38
+ `jscpd-server` bin names. The `jscpd` bin is an installed alias for the native
39
+ CLI so existing scripts can be tested with minimal command changes in a
40
+ controlled environment.
41
+
42
+ ## Config Files
43
+
44
+ `jscpd-rs` reads the same common config locations:
45
+
46
+ - `.jscpd.json`
47
+ - `package.json#jscpd`
48
+
49
+ Example:
50
+
51
+ ```json
52
+ {
53
+ "path": ["src", "packages"],
54
+ "format": ["javascript", "typescript", "rust"],
55
+ "minLines": 5,
56
+ "minTokens": 50,
57
+ "threshold": 5,
58
+ "reporters": ["console", "json", "sarif"],
59
+ "output": "report",
60
+ "ignore": ["target/**", "node_modules/**", "dist/**"],
61
+ "gitignore": true,
62
+ "noTips": true
63
+ }
64
+ ```
65
+
66
+ CLI arguments are applied after config loading, so command-line values can
67
+ override project defaults.
68
+
69
+ ## Commonly Compatible Workflows
70
+
71
+ The first release targets these upstream-style workflows:
72
+
73
+ - local duplicate scan with `jscpd .`;
74
+ - threshold-based CI failure with `--threshold` and `--exitCode`;
75
+ - format filtering with `--format`;
76
+ - `.gitignore` and explicit `--ignore` handling;
77
+ - report generation with built-in reporters;
78
+ - Git blame reports with `--blame`;
79
+ - server startup with `jscpd-server`;
80
+ - snippet checks through the native REST/MCP server.
81
+
82
+ Built-in native reporters:
83
+
84
+ - `ai`
85
+ - `badge`
86
+ - `console`
87
+ - `consoleFull`
88
+ - `csv`
89
+ - `html`
90
+ - `json`
91
+ - `markdown`
92
+ - `sarif`
93
+ - `silent`
94
+ - `threshold`
95
+ - `xcode`
96
+ - `xml`
97
+
98
+ Machine-readable reporters are treated as compatibility-sensitive: JSON, SARIF,
99
+ XML, CSV, and Markdown should keep the shape expected by automation.
100
+
101
+ ## Compatibility Model
102
+
103
+ The release gate is coverage-first. On the same inputs and options, `jscpd-rs`
104
+ must not miss duplicated source lines reported by upstream `jscpd`.
105
+
106
+ Exact clone pair identity, pair ordering, token totals, and some fragment
107
+ boundaries may differ while duplicated upstream source lines remain covered.
108
+ Extra Rust findings are allowed during convergence, but compatibility reports
109
+ keep them visible as `extra` findings.
110
+
111
+ This policy matters for multi-way clones: two implementations can pick
112
+ different clone pairs while still covering the same duplicated source ranges.
113
+
114
+ ## Known First-Release Limits
115
+
116
+ These are intentional first-release limits:
117
+
118
+ - dynamic npm reporters, stores, listeners, and plugins are not loaded;
119
+ - unknown external reporter/store names keep upstream-style warnings where
120
+ upstream continues;
121
+ - HTML reports are self-contained and practically compatible, not
122
+ pixel-perfect;
123
+ - long-tail formats use a synchronized format registry plus generic native
124
+ tokenization unless a fixture or public-repo gate proves a more specific
125
+ tokenizer is needed;
126
+ - the Rust crate exposes a native Rust API, not the upstream JavaScript package
127
+ API;
128
+ - first npm packaging is source-build: a Rust/Cargo toolchain is required during
129
+ install until prebuilt platform packages are added.
130
+
131
+ ## Compare On Your Repository
132
+
133
+ Run upstream and `jscpd-rs` with the same high-level options and compare the
134
+ generated JSON reports:
135
+
136
+ ```bash
137
+ node jscpd/apps/jscpd/bin/jscpd src \
138
+ --reporters json \
139
+ --output /tmp/jscpd-upstream \
140
+ --min-tokens 50 \
141
+ --min-lines 5 \
142
+ --exitCode 0
143
+
144
+ jscpd src \
145
+ --reporters json \
146
+ --output /tmp/jscpd-rs \
147
+ --min-tokens 50 \
148
+ --min-lines 5 \
149
+ --exitCode 0
150
+ ```
151
+
152
+ The repository scripts include the same coverage-first comparator used by the
153
+ release gates:
154
+
155
+ ```bash
156
+ FORMAT=typescript MIN_TOKENS=50 MIN_LINES=5 STRICT=coverage \
157
+ scripts/compat.sh /path/to/project
158
+ ```
159
+
160
+ If `jscpd-rs` misses duplicated lines that upstream reports, that is a
161
+ compatibility bug. If `jscpd-rs` reports additional findings, include them in
162
+ the issue too; they help decide whether a tokenizer or boundary rule needs
163
+ tightening.
164
+
165
+ ## CI Migration Pattern
166
+
167
+ Existing upstream-style CI:
168
+
169
+ ```yaml
170
+ - run: npx jscpd src --reporters console,json --threshold 5 --exitCode 1
171
+ ```
172
+
173
+ Cargo-based `jscpd-rs` CI:
174
+
175
+ ```yaml
176
+ - uses: dtolnay/rust-toolchain@stable
177
+ - run: cargo install jscpd-rs --locked
178
+ - run: jscpd src --reporters console,json --threshold 5 --exitCode 1
179
+ ```
180
+
181
+ npm/npx-based `jscpd-rs` CI after npm publication:
182
+
183
+ ```yaml
184
+ - uses: dtolnay/rust-toolchain@stable
185
+ - uses: actions/setup-node@v5
186
+ with:
187
+ node-version: 22
188
+ - run: npx jscpd-rs src --reporters console,json --threshold 5 --exitCode 1
189
+ ```
190
+
191
+ The npm source-build package still needs Rust available during installation.
192
+ Prebuilt npm platform packages are planned as a publication improvement.
193
+
@@ -0,0 +1,116 @@
1
+ # Npm Release Preparation
2
+
3
+ Current npm readiness snapshot:
4
+
5
+ - Date: 2026-06-01.
6
+ - Baseline commit: `e343350`; rerun `scripts/npm-package-check.sh` on the
7
+ exact checkout before npm publish.
8
+ - GitHub Actions `release-gate`: passed on run `26743839098`.
9
+ - Local checks passed: `cargo test`, `scripts/package-check.sh`,
10
+ `scripts/npm-package-check.sh`, local `npx --no-install` smoke for
11
+ `jscpd-rs`, `jscpd`, and `jscpd-server`.
12
+ - Package name status: `npm view jscpd-rs version` returned `E404`.
13
+ - Packed artifact audit: `jscpd-rs-0.1.0.tgz`, 96 files, about 169 KiB packed,
14
+ about 708 KiB unpacked.
15
+
16
+ The first npm package is `jscpd-rs`. It exposes these bin commands:
17
+
18
+ - `jscpd-rs`: primary `npx jscpd-rs` entrypoint, runs the native `jscpd` CLI.
19
+ - `jscpd`: installed alias for the native `jscpd` CLI.
20
+ - `jscpd-server`: installed alias for the native server binary.
21
+
22
+ The package is intentionally source-build for the first release candidate:
23
+ `postinstall` runs `cargo build --release --locked --bin jscpd --bin
24
+ jscpd-server` inside the installed npm package. This keeps the npm path simple
25
+ and verifiable before publication. Users installing from npm need Node, npm, and
26
+ a Rust/Cargo toolchain. Prebuilt platform packages can be added later without
27
+ changing the CLI behavior.
28
+
29
+ Local verification:
30
+
31
+ ```bash
32
+ scripts/npm-package-check.sh
33
+ ```
34
+
35
+ That script verifies:
36
+
37
+ - `package.json` version matches `Cargo.toml`;
38
+ - `npm pack` includes the expected Rust source and npm shim files;
39
+ - `npm pack` includes the advertised `skills/` files used by the terminal tip;
40
+ - forbidden paths such as `jscpd/`, `target/`, `report/`, `scripts/`, and
41
+ `node_modules/` are not packed;
42
+ - `npm publish --dry-run --json` succeeds without publishing;
43
+ - installing the packed tarball without Cargo fails with the expected Rust
44
+ toolchain hint;
45
+ - a local npm install exposes working `jscpd-rs`, `jscpd`, and `jscpd-server`
46
+ bin commands;
47
+ - `npx --package <local-tarball> jscpd-rs --version` works.
48
+
49
+ Before actual publication, run:
50
+
51
+ ```bash
52
+ git status --short
53
+ npm whoami
54
+ scripts/npm-package-check.sh
55
+ npm view jscpd-rs version
56
+ ```
57
+
58
+ `npm view` should return `E404` for the first publication, or the package must
59
+ already be owned by this project. Do not run `npm publish` until explicit
60
+ release approval.
61
+
62
+ Do not use `scripts/prepublish-check.sh` as the npm-only gate after the Cargo
63
+ crate and GitHub tag have already been published: that script is the full
64
+ Cargo/GitHub first-publication gate and intentionally checks that the crate name
65
+ and release tag are still available.
66
+
67
+ If the package name is still free and the npm account is logged in:
68
+
69
+ ```bash
70
+ scripts/npm-package-check.sh
71
+ npm publish --access public
72
+ ```
73
+
74
+ If the account requires a one-time password for publish:
75
+
76
+ ```bash
77
+ npm publish --access public --otp 123456
78
+ ```
79
+
80
+ ## Trusted Publishing
81
+
82
+ Trusted Publishing is the preferred npm path for CI/CD publishing. npm trusts a
83
+ specific GitHub Actions workflow through OIDC, so the workflow can publish
84
+ without a long-lived npm token. For public packages published from public
85
+ repositories, npm also generates provenance attestations automatically.
86
+
87
+ This repository provides a manual publish workflow:
88
+
89
+ ```text
90
+ .github/workflows/npm-publish.yml
91
+ ```
92
+
93
+ Configure npm:
94
+
95
+ 1. Open the `jscpd-rs` package settings on npmjs.com.
96
+ 2. Go to **Trusted Publisher**.
97
+ 3. Select **GitHub Actions**.
98
+ 4. Use these values:
99
+ - Organization or user: `vv-bogdanov`
100
+ - Repository: `jscpd-rs`
101
+ - Workflow filename: `npm-publish.yml`
102
+ - Environment name: leave empty
103
+ - Allowed actions: `npm publish`
104
+
105
+ Then publish from GitHub:
106
+
107
+ 1. Open GitHub Actions.
108
+ 2. Select the `npm-publish` workflow.
109
+ 3. Click **Run workflow** on `main`.
110
+ 4. Enter `jscpd-rs` for `package_name`.
111
+ 5. Run the workflow.
112
+
113
+ If npm does not allow Trusted Publishing configuration before the first package
114
+ version exists, publish the first version with either interactive 2FA or a
115
+ short-lived granular token with bypass 2FA enabled, then immediately configure
116
+ Trusted Publishing for future releases.
@@ -0,0 +1,81 @@
1
+ # Public Benchmark Suite
2
+
3
+ The release benchmark suite uses popular public repositories cloned under
4
+ `${XDG_CACHE_HOME:-$HOME/.cache}/jscpd-rs/public-bench/repos` by default. These
5
+ clones are generated local state outside this git repo.
6
+
7
+ Keep benchmark repositories outside this project tree unless you intentionally
8
+ disable parent `.gitignore` effects. Upstream `jscpd` respects parent ignore
9
+ files and can silently skip repo-internal benchmark directories that are
10
+ gitignored.
11
+
12
+ Upstream `jscpd` does not currently ship a public-repository performance suite.
13
+ Its monorepo scripts expose `pnpm test`, CI runs build/lint/test plus
14
+ `./apps/jscpd/bin/jscpd ./fixtures`, and the README benchmark note is based on
15
+ the local `fixtures/` directory. This suite is therefore a project-owned release
16
+ gate for product-scale speed checks.
17
+
18
+ Configured cases:
19
+
20
+ | Case | Repository | Branch | Format |
21
+ | --- | --- | --- | --- |
22
+ | `react` | `https://github.com/facebook/react.git` | `main` | `javascript` |
23
+ | `next` | `https://github.com/vercel/next.js.git` | `canary` | `typescript` |
24
+ | `vscode` | `https://github.com/microsoft/vscode.git` | `main` | `typescript` |
25
+ | `prometheus` | `https://github.com/prometheus/prometheus.git` | `main` | `go` |
26
+ | `rust` | `https://github.com/rust-lang/rust.git` | `main` | `rust` |
27
+
28
+ Usage:
29
+
30
+ ```bash
31
+ LIST=1 scripts/public-bench-suite.sh
32
+ CASES=react,next RUNS=3 scripts/public-bench-suite.sh
33
+ CHECK_COMPAT=1 CASES=react scripts/public-bench-suite.sh
34
+ MIN_SPEEDUP=10 CASES=react,next RUNS=3 scripts/public-bench-suite.sh
35
+ UPSTREAM_TIMEOUT=600s CASES=vscode RUNS=1 scripts/public-bench-suite.sh
36
+ PUBLIC=1 PUBLIC_CASES=react,next PUBLIC_RUNS=3 scripts/release-gate.sh
37
+ ```
38
+
39
+ Default behavior clones missing repositories with `--depth=1`, runs Rust and
40
+ upstream `jscpd` through `scripts/bench.sh`, and writes raw benchmark output to
41
+ `$BENCH_ROOT/results`. It also writes a TSV summary to
42
+ `$BENCH_ROOT/results/summary.tsv` with case, commit, format, Rust average,
43
+ upstream average, speedup, and compatibility status. Set `UPDATE=1` to refresh
44
+ existing clones. Set `MIN_SPEEDUP` to make the suite fail when any selected
45
+ case falls below the required upstream/Rust speedup. Each upstream timing run is
46
+ bounded by `UPSTREAM_TIMEOUT` (`600s` by default) so optional stress cases cannot
47
+ hang a release gate indefinitely; set `RUST_TIMEOUT` or `UPSTREAM_TIMEOUT` to an
48
+ empty value to disable that side's timeout.
49
+
50
+ When `CHECK_COMPAT=1` is enabled, the suite runs the same coverage-first report
51
+ comparison used by the fixture gates. `react`, `next`, and `prometheus` include
52
+ narrow allowlists for upstream overextended ranges documented in
53
+ `docs/upstream-bugs.md`; those entries are printed as ignored line-coverage
54
+ exceptions in the comparison output. New public benchmark misses should be fixed
55
+ or documented before they are added to this allowlist.
56
+
57
+ Recorded release-candidate measurements on May 31, 2026:
58
+
59
+ ```bash
60
+ scripts/release-candidate.sh
61
+ ```
62
+
63
+ | Case | Commit | Format | Rust avg | Upstream avg | Speedup | Compat |
64
+ | --- | --- | --- | ---: | ---: | ---: | --- |
65
+ | `react` | `f0dfee3` | `javascript` | 0.199097s | 10.079214s | 50.62x | pass |
66
+ | `next` | `2bbb67b9` | `typescript` | 0.262433s | 14.715736s | 56.07x | pass |
67
+ | `prometheus` | `a0524ee` | `go` | 0.085239s | 4.642435s | 54.46x | pass |
68
+
69
+ `kubernetes` was also checked as a Go stress case, but upstream `jscpd` ran out
70
+ of memory with the default Node heap, so it is intentionally not part of the
71
+ default release suite.
72
+
73
+ `vscode` is configured as an optional TypeScript stress case, but it is not part
74
+ of the default release suite yet. On May 31, 2026, Rust completed the timing run
75
+ in `1.464358s` at commit `e4074382`, while upstream was still running after
76
+ more than nine minutes and the exploratory run was stopped before compatibility
77
+ comparison. Keep it behind an explicit `CASES=vscode` run until we decide on a
78
+ separate slow-suite policy.
79
+
80
+ Before publication, rerun the suite on the selected cases and copy the measured
81
+ averages into `docs/compat-baseline.md` or release notes with commit hashes.