cuke-dedup 0.5.0 → 0.7.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.
package/CHANGELOG.md CHANGED
@@ -5,6 +5,67 @@ All notable changes to CukeDedup are documented in this file. The project follow
5
5
 
6
6
  ## [Unreleased]
7
7
 
8
+ ## [0.7.0] - 2026-09-21
9
+
10
+ ### Changed
11
+
12
+ - **Breaking (reports and baselines):** semantic fingerprints now use the first 128 bits of SHA-256 instead of FNV-1a-64. JSON and JSONL reports use schema version `3`, SARIF publishes `cukeDedupFingerprint/v3`, and semantic baselines use version `3`. Existing version `1` or `2` baselines are rejected for comparison and can be regenerated in place with `--update-baseline`.
13
+ - Matcher compilation now runs in bounded windows instead of retaining every compiled matcher at once, substantially reducing peak memory on large definition corpora. Complete, untruncated runs retain the same findings; a run that exhausts its matcher-scan budget may become incomplete or retain a different bounded subset than v0.6.0.
14
+
15
+ ## [0.6.0] - 2026-09-18
16
+
17
+ ### Added
18
+
19
+ - `assertionModules` names module specifiers whose `expect` export is a trusted assertion factory.
20
+ Use it for a local module that re-exports `expect`, or for a runner this build does not recognise
21
+ by name. Both `import` and `require` spellings honour the setting, including
22
+ `import * as fixtures from "./fixtures"` and `import fixtures = require("./fixtures")`. An
23
+ undeclared module stays untrusted, and a facade inferred from registration re-exports is not
24
+ trusted for assertions.
25
+
26
+ ### Changed
27
+
28
+ - `vitest`, `chai` and `bun:test` join the recognised assertion factories. Importing `expect` from
29
+ one of them was previously *less* trusted than relying on an injected global, so explicit imports
30
+ were penalised: expected values did not count as behaviour and two steps asserting different
31
+ values could be offered as a `parameterization-candidate`. Chain recognition is shape-based, so
32
+ Chai's `expect(x).to.equal(y)` is read exactly as `expect(x).toBe(y)` is. **Reports can lose those
33
+ spurious findings on upgrade.**
34
+ - A module-scoped constant now reaches the handler fingerprint, so two handlers reading the same
35
+ value through differently named constants are recognised as the same handler. A mutable `let`
36
+ stays unproven, a handler-local binding of the same name shadows the module constant, and a
37
+ constant declared inside another declaration's initializer is out of scope and never substituted.
38
+ A name captured from an enclosing scope is nearer than the module's, so a module constant never
39
+ substitutes over it. **Reports can gain findings on upgrade.**
40
+
41
+ ### Fixed
42
+
43
+ - A function declared in a handler and then called contributes its assertions to that handler, so
44
+ conflicting expected values inside it keep the handlers apart instead of surfacing as a
45
+ `parameterization-candidate`. Expansion is single level and terminates on recursion. It is
46
+ declined when the name is bound by anything else in the handler — another declaration, a variable,
47
+ a parameter, a catch binding, a `class`, an `enum`, or a reassignment — because a name-keyed lookup
48
+ cannot tell which body a shadowed call reaches, and expanding the wrong one would attribute
49
+ assertions the handler never runs. A transparent wrapper such as `(check)()` resolves the same
50
+ declaration a bare call does.
51
+
52
+ ### Known limitations
53
+
54
+ - A module-scoped constant reaches the handler fingerprint only when it is declared **before** the
55
+ registrations that read it. Two handlers reading the same value through constants declared after
56
+ the `Given`/`When`/`Then` calls are not recognised as the same handler, so the duplicate is
57
+ missed. Moving the declarations above the registrations restores the finding.
58
+ - A handler passed as a call expression, such as `Given("...", makeHandler())`, cannot be resolved
59
+ to a body and takes no part in the handler rules; it is reported as `dynamic or unsupported step
60
+ handler cannot be compared statically`. A handler reached through a member expression, such as
61
+ `Given("...", steps.run)`, is not compared either, and that case is silent. Inline functions —
62
+ arrow, `async`, `function` and generator — and a reference to a local function declaration are
63
+ all compared normally.
64
+ - Step definitions registered through a default import or TypeScript's `import x = require(...)`
65
+ and called as a member, such as `cucumber.Given("...")`, are not discovered, so those files
66
+ contribute no definitions to any rule. Named imports, namespace imports (`import * as cucumber`),
67
+ renamed named imports and `require` destructuring are all recognised.
68
+
8
69
  ## [0.5.0] - 2026-09-16
9
70
 
10
71
  ### Added
@@ -223,6 +284,8 @@ Initial public release.
223
284
  - Native Cargo and npm distributions for eight supported targets.
224
285
  - A checksum-verified GitHub Action and an agent-oriented CukeDedup skill.
225
286
 
287
+ [0.7.0]: https://github.com/figueiredoluiz/cuke-dedup/compare/v0.6.0...v0.7.0
288
+ [0.6.0]: https://github.com/figueiredoluiz/cuke-dedup/compare/v0.5.0...v0.6.0
226
289
  [0.5.0]: https://github.com/figueiredoluiz/cuke-dedup/compare/v0.4.0...v0.5.0
227
290
  [0.4.0]: https://github.com/figueiredoluiz/cuke-dedup/compare/v0.3.0...v0.4.0
228
291
  [0.3.0]: https://github.com/figueiredoluiz/cuke-dedup/compare/v0.2.1...v0.3.0
package/README.md CHANGED
@@ -136,7 +136,7 @@ steps:
136
136
  with:
137
137
  fetch-depth: 0
138
138
  persist-credentials: false
139
- - uses: figueiredoluiz/cuke-dedup@v0.5.0
139
+ - uses: figueiredoluiz/cuke-dedup@v0.7.0
140
140
  with:
141
141
  path: .
142
142
  threshold: 5
package/SECURITY.md CHANGED
@@ -5,11 +5,6 @@
5
5
  Security fixes are released for the latest published minor version. Before the first
6
6
  stable release, only the latest `0.x` release is supported.
7
7
 
8
- | Version | Supported |
9
- | --- | --- |
10
- | 0.2.x | Yes |
11
- | Older versions | No |
12
-
13
8
  ## Reporting a vulnerability
14
9
 
15
10
  Please do not open a public issue for a suspected vulnerability. Use
@@ -18,7 +18,7 @@ steps:
18
18
  fetch-depth: 0
19
19
  persist-credentials: false
20
20
 
21
- - uses: figueiredoluiz/cuke-dedup@v0.5.0
21
+ - uses: figueiredoluiz/cuke-dedup@v0.7.0
22
22
  id: cuke-dedup
23
23
  with:
24
24
  path: .
@@ -101,8 +101,7 @@ cuke-dedup . --baseline .cuke-dedup-baseline.json --fail-on-new 3
101
101
 
102
102
  Baselines store sorted semantic fingerprints with multiplicity counts. Renames and unrelated line
103
103
  changes do not make a finding new, while an additional occurrence beyond the recorded count does.
104
- Version `1` baselines from CukeDedup 0.1 can be replaced by running the version `2` tool with
105
- `--update-baseline`; comparison rejects mismatched schemas.
104
+ Version `1` and `2` baselines can be replaced by running the version `3` tool with `--update-baseline`; comparison rejects mismatched schemas and names the regeneration command.
106
105
 
107
106
  Updates reject `--changed-since` and are skipped after incomplete analysis or an operational error,
108
107
  preventing a partial scan from erasing accepted findings.
@@ -134,6 +134,39 @@ Nested, asynchronous, generator, conditional, multi-statement, reordered, rewrit
134
134
  dynamically constructed wrappers cannot be inferred safely. Declare those names when they still
135
135
  take the matcher first and handler second.
136
136
 
137
+ ## Trusted assertion modules
138
+
139
+ `assertionModules` names module specifiers whose `expect` export is a real assertion factory.
140
+
141
+ CukeDedup recognizes `@playwright/test`, `playwright/test`, `@jest/globals`, `expect`, `vitest`,
142
+ `chai`, and `bun:test` without configuration, and it also trusts the ambient `expect` that Jest,
143
+ Vitest, and Playwright inject. Chain recognition is shape-based, so Chai's `expect(x).to.equal(y)`
144
+ is understood exactly as Jest's `expect(x).toBe(y)` is; only the origin of the factory is listed.
145
+
146
+ Declare a module when the factory reaches your steps another way:
147
+
148
+ ```json
149
+ {
150
+ "assertionModules": ["./support/fixtures"]
151
+ }
152
+ ```
153
+
154
+ A local module that re-exports `expect` is the common case:
155
+
156
+ ```ts
157
+ // support/fixtures.ts
158
+ export { expect } from "@playwright/test";
159
+ ```
160
+
161
+ Without the declaration, `expect` is untrusted, because the analyzer does not follow a local
162
+ re-export back to its origin. An untrusted factory is not an error: its expected values simply stop
163
+ counting as behaviour, so two steps asserting genuinely different values can be offered as a
164
+ `parameterization-candidate`. Declaring the module restores the distinction. Both `import` and
165
+ `require` spellings honour the setting.
166
+
167
+ Trust only modules that really do expose an assertion factory. Declaring an unrelated module makes
168
+ its call arguments semantically load-bearing and can manufacture similarity evidence.
169
+
137
170
  ## Custom parameter types
138
171
 
139
172
  `parameterTypes` maps a project-defined Cucumber Expression parameter type to the regular
package/docs/reports.md CHANGED
@@ -23,7 +23,7 @@ Rule headings are bold cyan, paths are blue, error/warning labels are red/yellow
23
23
 
24
24
  ## JSON and HTML
25
25
 
26
- JSON schema version `2` contains:
26
+ JSON schema version `3` contains:
27
27
 
28
28
  - Relative Unicode-aware source spans and severity.
29
29
  - Suppressions and semantic fingerprints.
@@ -43,7 +43,7 @@ byte-reproducible artifacts matter. The deterministic corpus census remains pres
43
43
 
44
44
  ## JSON Lines
45
45
 
46
- JSONL schema version `2` emits one compact record per finding and a final summary:
46
+ JSONL schema version `3` emits one compact record per finding and a final summary:
47
47
 
48
48
  ```sh
49
49
  cuke-dedup . --reporters jsonl \
@@ -25,6 +25,16 @@ resource limits keep failures visible and bound the work performed.
25
25
  overlap witnesses.
26
26
  - Bounded fuzzy indexes preserve a deterministic sample in highly repetitive vocabularies but do
27
27
  not promise every possible pair after a work limit is reached.
28
+ - A module-scoped constant declared *after* the registrations that read it is not resolved, even
29
+ though the handler body runs later and the value is initialized by then. Declaration order is the
30
+ conservative rule shared with handler-local constants, where a reference before the declaration is
31
+ a temporal-dead-zone error. The effect is a missed duplicate, never an invented one.
32
+ - A handler produced by calling a generator function, such as
33
+ `Given('a step', (function* () { … })())`, is not compared. Calling a generator returns a
34
+ suspended object and never runs the body, so the registered handler is that object rather than a
35
+ function and its assertions never execute. Treating the suspended body as the handler's behaviour
36
+ would invent meaning the code does not have, so each such registration warns that the handler
37
+ cannot be compared statically and is excluded from handler rules.
28
38
 
29
39
  When analysis is incomplete, existing findings remain valid but the absence of a finding proves
30
40
  nothing. Machine reports expose the incomplete or truncated status. Pass `--fail-on-incomplete`
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cuke-dedup",
3
- "version": "0.5.0",
3
+ "version": "0.7.0",
4
4
  "description": "Static analysis for duplicate and reusable Cucumber step definitions",
5
5
  "type": "module",
6
6
  "bin": {
@@ -24,6 +24,8 @@
24
24
  "bump:version": "node scripts/release/bump-version.mjs",
25
25
  "pack:check": "npm pack --dry-run",
26
26
  "corpus:check": "node scripts/check/check-corpus.mjs",
27
+ "memory:check": "node scripts/check/check-memory.mjs target/release/cuke-dedup",
28
+ "duplication:check": "node scripts/check/check-duplication.mjs",
27
29
  "benchmark": "node scripts/benchmark/benchmark-analysis.mjs"
28
30
  },
29
31
  "engines": {
@@ -47,13 +49,13 @@
47
49
  "npm/platforms/*"
48
50
  ],
49
51
  "optionalDependencies": {
50
- "cuke-dedup-darwin-arm64": "0.5.0",
51
- "cuke-dedup-darwin-x64": "0.5.0",
52
- "cuke-dedup-linux-arm64-gnu": "0.5.0",
53
- "cuke-dedup-linux-arm64-musl": "0.5.0",
54
- "cuke-dedup-linux-x64-gnu": "0.5.0",
55
- "cuke-dedup-linux-x64-musl": "0.5.0",
56
- "cuke-dedup-windows-arm64-msvc": "0.5.0",
57
- "cuke-dedup-windows-x64-msvc": "0.5.0"
52
+ "cuke-dedup-darwin-arm64": "0.7.0",
53
+ "cuke-dedup-darwin-x64": "0.7.0",
54
+ "cuke-dedup-linux-arm64-gnu": "0.7.0",
55
+ "cuke-dedup-linux-arm64-musl": "0.7.0",
56
+ "cuke-dedup-linux-x64-gnu": "0.7.0",
57
+ "cuke-dedup-linux-x64-musl": "0.7.0",
58
+ "cuke-dedup-windows-arm64-msvc": "0.7.0",
59
+ "cuke-dedup-windows-x64-msvc": "0.7.0"
58
60
  }
59
61
  }