miniread 1.115.3 → 1.115.4

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 (36) hide show
  1. package/dist/transforms/preset-stats.json +2 -2
  2. package/dist/transforms-by-id/rename-command-result-variables/manifest.json +4 -4
  3. package/dist/transforms-by-id/rename-command-result-variables/rename-command-result-variables-transform.js +15 -1
  4. package/dist/transforms-by-id/rename-error-first-callback-parameters/manifest.json +6 -6
  5. package/dist/transforms-by-id/rename-error-first-callback-parameters/rename-error-first-callback-parameters-transform.js +3 -1
  6. package/dist/transforms-by-id/rename-http-method-parameters/manifest.json +7 -7
  7. package/dist/transforms-by-id/rename-http-method-parameters/rename-http-method-parameters-transform.js +3 -3
  8. package/dist/transforms-by-id/rename-object-keys-iterator-variables/manifest.json +4 -4
  9. package/dist/transforms-by-id/rename-object-keys-iterator-variables/rename-object-keys-iterator-variables-transform.js +3 -1
  10. package/dist/transforms-by-id/rename-return-object-bindings/collect-return-object-binding-candidates.js +16 -3
  11. package/dist/transforms-by-id/rename-uint8array-concat-variables/ast-helpers.d.ts +1 -1
  12. package/dist/transforms-by-id/rename-uint8array-concat-variables/ast-helpers.js +1 -5
  13. package/dist/transforms-by-id/rename-uint8array-concat-variables/find-loop-copy-match.d.ts +13 -0
  14. package/dist/transforms-by-id/rename-uint8array-concat-variables/find-loop-copy-match.js +68 -0
  15. package/dist/transforms-by-id/rename-uint8array-concat-variables/find-uint8array-concat-match.js +9 -3
  16. package/dist/transforms-by-id/rename-uint8array-concat-variables/find-uint8array-output-rename-matches.d.ts +14 -0
  17. package/dist/transforms-by-id/rename-uint8array-concat-variables/find-uint8array-output-rename-matches.js +60 -0
  18. package/dist/transforms-by-id/rename-uint8array-concat-variables/get-loop-set-and-offset-expressions.d.ts +3 -0
  19. package/dist/transforms-by-id/rename-uint8array-concat-variables/get-loop-set-and-offset-expressions.js +28 -0
  20. package/dist/transforms-by-id/rename-uint8array-concat-variables/has-local-dynamic-name-lookup.d.ts +3 -0
  21. package/dist/transforms-by-id/rename-uint8array-concat-variables/has-local-dynamic-name-lookup.js +38 -0
  22. package/dist/transforms-by-id/rename-uint8array-concat-variables/is-plus-equals-length-or-bytelength.d.ts +2 -0
  23. package/dist/transforms-by-id/rename-uint8array-concat-variables/is-plus-equals-length-or-bytelength.js +25 -0
  24. package/dist/transforms-by-id/rename-uint8array-concat-variables/is-set-member-expression.d.ts +2 -0
  25. package/dist/transforms-by-id/rename-uint8array-concat-variables/is-set-member-expression.js +10 -0
  26. package/dist/transforms-by-id/rename-uint8array-concat-variables/loop-matches.d.ts +3 -3
  27. package/dist/transforms-by-id/rename-uint8array-concat-variables/loop-matches.js +36 -19
  28. package/dist/transforms-by-id/rename-uint8array-concat-variables/manifest.json +7 -7
  29. package/dist/transforms-by-id/rename-uint8array-concat-variables/rename-uint8array-concat-variables-transform.js +28 -2
  30. package/dist/transforms-by-id/stabilize-deferred-stable-rhs/manifest.json +6 -6
  31. package/dist/transforms-by-id/stabilize-deferred-stable-rhs/stabilize-deferred-stable-rhs-transform.js +33 -15
  32. package/dist/transforms-by-id/stabilize-nested-bindings/is-factory-callback.d.ts +7 -2
  33. package/dist/transforms-by-id/stabilize-nested-bindings/is-factory-callback.js +31 -5
  34. package/dist/transforms-by-id/stabilize-nested-bindings/manifest.json +5 -5
  35. package/dist/transforms-by-id/stabilize-nested-bindings/stabilize-nested-bindings-transform.js +3 -3
  36. package/package.json +1 -1
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "recommended": {
3
- "diffSizePercent": 25.304267301659294,
4
- "notes": "Measured with baseline none: 25.30% of original diff."
3
+ "diffSizePercent": 25.62554333446725,
4
+ "notes": "Measured with baseline none: 25.63% of original diff."
5
5
  }
6
6
  }
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "evaluations": {
3
3
  "claude-code-2.1.10:claude-code-2.1.11": {
4
- "diffSizePercent": 100,
5
- "evaluatedAt": "2026-02-14T12:53:18.669Z",
6
- "changedLines": 338,
7
- "durationSeconds": 38.783343792,
4
+ "diffSizePercent": 99.99055070491741,
5
+ "evaluatedAt": "2026-02-21T07:41:21.454Z",
6
+ "changedLines": 494,
7
+ "durationSeconds": 38.767131458,
8
8
  "stableNames": 1359
9
9
  }
10
10
  },
@@ -5,6 +5,16 @@ const require = createRequire(import.meta.url);
5
5
  // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
6
6
  const traverse = require("@babel/traverse").default;
7
7
  const BASE_NAME = "commandResult";
8
+ const isAwaitedCallResultBinding = (binding) => {
9
+ if (!binding.path.isVariableDeclarator())
10
+ return false;
11
+ const init = binding.path.node.init;
12
+ if (!init)
13
+ return false;
14
+ if (init.type !== "AwaitExpression")
15
+ return false;
16
+ return init.argument.type === "CallExpression";
17
+ };
8
18
  const isCommandResultProperty = (path) => {
9
19
  const memberExpressionPath = path.parentPath;
10
20
  if (!memberExpressionPath.isMemberExpression())
@@ -38,7 +48,11 @@ const hasCommandResultPattern = (binding) => {
38
48
  hasOutputProperty = true;
39
49
  }
40
50
  }
41
- return hasCodeProperty && hasOutputProperty;
51
+ if (!hasOutputProperty)
52
+ return false;
53
+ if (hasCodeProperty)
54
+ return true;
55
+ return isAwaitedCallResultBinding(binding);
42
56
  };
43
57
  export const renameCommandResultVariablesTransform = {
44
58
  id: "rename-command-result-variables",
@@ -1,14 +1,14 @@
1
1
  {
2
- "recommended": true,
3
- "recommendedOrder": 100,
4
2
  "notes": "Measured with baseline none: diff size remains 100.00% of baseline for this evaluation set. This transform remains recommended because it targets no-else error-first guards and is complementary to rename-error-first-callback-parameters-v2.",
5
3
  "evaluations": {
6
4
  "claude-code-2.1.10:claude-code-2.1.11": {
7
5
  "diffSizePercent": 100,
8
- "evaluatedAt": "2026-02-07T21:24:52.681Z",
9
- "changedLines": 416,
10
- "durationSeconds": 202.892315483,
6
+ "evaluatedAt": "2026-02-21T06:37:23.328Z",
7
+ "changedLines": 428,
8
+ "durationSeconds": 142.79656364099998,
11
9
  "stableNames": 1360
12
10
  }
13
- }
11
+ },
12
+ "recommended": true,
13
+ "recommendedOrder": 100
14
14
  }
@@ -46,7 +46,9 @@ const matchesErrorFirstGuard = (path) => {
46
46
  return undefined;
47
47
  if (firstBinding.kind !== "param")
48
48
  return undefined;
49
- if (firstBinding.referencePaths.length !== 2)
49
+ if (firstBinding.constantViolations.length > 0)
50
+ return undefined;
51
+ if (firstBinding.referencePaths.length < 2)
50
52
  return undefined;
51
53
  const ifStatement = getErrorFirstIfStatement(path);
52
54
  if (!ifStatement)
@@ -1,13 +1,13 @@
1
1
  {
2
- "recommended": true,
3
- "recommendedOrder": 50,
4
2
  "evaluations": {
5
3
  "claude-code-2.1.10:claude-code-2.1.11": {
6
4
  "diffSizePercent": 100,
7
- "evaluatedAt": "2026-02-07T21:16:51.038Z",
8
- "changedLines": 0,
9
- "durationSeconds": 192.863831332,
10
- "stableNames": 1357
5
+ "evaluatedAt": "2026-02-21T09:21:11.297Z",
6
+ "changedLines": 16,
7
+ "durationSeconds": 28.464535625,
8
+ "stableNames": 1358
11
9
  }
12
- }
10
+ },
11
+ "recommended": true,
12
+ "recommendedOrder": 50
13
13
  }
@@ -1,7 +1,7 @@
1
1
  import { createRequire } from "node:module";
2
2
  import { RenameGroup, isStableRenamed } from "../../core/stable-naming.js";
3
3
  import { getFilesToProcess, } from "../../core/types.js";
4
- import { hasDynamicNameLookup } from "../../transforms/has-dynamic-name-lookup.js";
4
+ import { hasDynamicNameLookup } from "../stabilize-nested-bindings/has-dynamic-name-lookup.js";
5
5
  const require = createRequire(import.meta.url);
6
6
  // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
7
7
  const traverse = require("@babel/traverse").default;
@@ -37,8 +37,6 @@ export const renameHttpMethodParametersTransform = {
37
37
  let nodesVisited = 0;
38
38
  let transformationsApplied = 0;
39
39
  for (const fileInfo of getFilesToProcess(context)) {
40
- if (hasDynamicNameLookup(fileInfo.ast))
41
- continue;
42
40
  const group = new RenameGroup();
43
41
  traverse(fileInfo.ast, {
44
42
  CallExpression(path) {
@@ -70,6 +68,8 @@ export const renameHttpMethodParametersTransform = {
70
68
  if (!callbackPath.isFunctionExpression() &&
71
69
  !callbackPath.isArrowFunctionExpression())
72
70
  return;
71
+ if (hasDynamicNameLookup(callbackPath))
72
+ return;
73
73
  const parameterPaths = callbackPath.get("params");
74
74
  if (!Array.isArray(parameterPaths))
75
75
  return;
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "evaluations": {
3
3
  "claude-code-2.1.10:claude-code-2.1.11": {
4
- "diffSizePercent": 100,
5
- "evaluatedAt": "2026-02-12T13:18:42.927Z",
6
- "changedLines": 1240,
7
- "durationSeconds": 37.765023916,
4
+ "diffSizePercent": 99.99811014098348,
5
+ "evaluatedAt": "2026-02-21T09:45:41.024Z",
6
+ "changedLines": 1814,
7
+ "durationSeconds": 43.250947458999995,
8
8
  "stableNames": 1359
9
9
  }
10
10
  },
@@ -73,8 +73,10 @@ const renameIteratorBinding = (path, identifier, group, options) => {
73
73
  const binding = path.scope.getBinding(currentName);
74
74
  if (!binding)
75
75
  return;
76
- if (!binding.constant)
76
+ if (binding.kind === "var" &&
77
+ [...binding.referencePaths, ...binding.constantViolations].some((usagePath) => !usagePath.isDescendant(path))) {
77
78
  return;
79
+ }
78
80
  group.add({
79
81
  scope: binding.scope,
80
82
  currentName,
@@ -13,6 +13,19 @@ const getPropertyName = (key) => {
13
13
  }
14
14
  return undefined;
15
15
  };
16
+ const getReturnObjectExpression = (argument) => {
17
+ if (!argument)
18
+ return undefined;
19
+ if (argument.type === "ObjectExpression")
20
+ return argument;
21
+ if (argument.type !== "SequenceExpression")
22
+ return undefined;
23
+ const lastExpression = argument.expressions.at(-1);
24
+ if (lastExpression?.type !== "ObjectExpression") {
25
+ return undefined;
26
+ }
27
+ return lastExpression;
28
+ };
16
29
  const collectCandidatesForObjectExpression = (options) => {
17
30
  const { objectExpression, scope, isScript, exportedNames, candidates } = options;
18
31
  for (const property of objectExpression.properties) {
@@ -68,11 +81,11 @@ export const collectReturnObjectBindingCandidates = (options) => {
68
81
  traverse(ast, {
69
82
  ReturnStatement(path) {
70
83
  nodesVisited++;
71
- const argument = path.node.argument;
72
- if (argument?.type !== "ObjectExpression")
84
+ const objectExpression = getReturnObjectExpression(path.node.argument);
85
+ if (!objectExpression)
73
86
  return;
74
87
  collectCandidatesForObjectExpression({
75
- objectExpression: argument,
88
+ objectExpression,
76
89
  scope: path.scope,
77
90
  isScript,
78
91
  exportedNames,
@@ -8,7 +8,7 @@ export declare const getBodyStatements: (bodyPath: NodePath<BlockStatement> | No
8
8
  export declare const getZeroInitializedVariable: (path: NodePath<Statement>) => ZeroInitMatch | undefined;
9
9
  export declare const getUint8ArrayInitializer: (path: NodePath<Statement>, totalLengthName: string) => ZeroInitMatch | undefined;
10
10
  export declare const getLoopVariable: (path: NodePath<ForOfStatement>) => NodePath<Identifier> | undefined;
11
- export declare const getArrayIdentifier: (expression: Expression) => string | undefined;
11
+ export declare const getArrayExpression: (expression: Expression) => Expression;
12
12
  export declare const isPlusEqualsLength: (expression: Expression, targetName: string, loopVariableName: string) => boolean;
13
13
  export declare const isSetCall: (expression: Expression, outputName: string, loopVariableName: string, offsetName: string) => boolean;
14
14
  export {};
@@ -61,11 +61,7 @@ export const getLoopVariable = (path) => {
61
61
  return undefined;
62
62
  return idPath;
63
63
  };
64
- export const getArrayIdentifier = (expression) => {
65
- if (!t.isIdentifier(expression))
66
- return undefined;
67
- return expression.name;
68
- };
64
+ export const getArrayExpression = (expression) => expression;
69
65
  const isLengthAccess = (expression, objectName) => {
70
66
  if (!t.isMemberExpression(expression))
71
67
  return false;
@@ -0,0 +1,13 @@
1
+ import type { Binding, NodePath, Scope } from "@babel/traverse";
2
+ import type { ForOfStatement } from "@babel/types";
3
+ export type LoopCopyMatch = {
4
+ outputBinding: Binding;
5
+ outputName: string;
6
+ offsetName: string;
7
+ offsetScope: Scope;
8
+ loopVariableName: string;
9
+ loopVarScope: Scope;
10
+ arrayName: string;
11
+ arrayScope: Scope | undefined;
12
+ };
13
+ export declare const findLoopCopyMatch: (loopPath: NodePath<ForOfStatement>) => LoopCopyMatch | undefined;
@@ -0,0 +1,68 @@
1
+ import * as t from "@babel/types";
2
+ import { getLoopSetAndOffsetExpressions } from "./get-loop-set-and-offset-expressions.js";
3
+ import { isPlusEqualsLengthOrByteLength } from "./is-plus-equals-length-or-bytelength.js";
4
+ import { isSetMemberExpression } from "./is-set-member-expression.js";
5
+ export const findLoopCopyMatch = (loopPath) => {
6
+ const leftPath = loopPath.get("left");
7
+ if (!leftPath.isVariableDeclaration())
8
+ return undefined;
9
+ if (leftPath.node.declarations.length !== 1)
10
+ return undefined;
11
+ const loopVariablePath = loopPath.get("left.declarations.0.id");
12
+ if (!loopVariablePath.isIdentifier())
13
+ return undefined;
14
+ const loopVariableName = loopVariablePath.node.name;
15
+ const bodyPath = loopPath.get("body");
16
+ const bodyStatements = bodyPath.isBlockStatement()
17
+ ? bodyPath.get("body")
18
+ : [bodyPath];
19
+ const setAndOffsetExpressions = getLoopSetAndOffsetExpressions(bodyStatements);
20
+ if (!setAndOffsetExpressions)
21
+ return undefined;
22
+ const [setExpression, offsetExpression] = setAndOffsetExpressions;
23
+ if (!t.isCallExpression(setExpression))
24
+ return undefined;
25
+ if (!t.isMemberExpression(setExpression.callee))
26
+ return undefined;
27
+ if (!t.isIdentifier(setExpression.callee.object))
28
+ return undefined;
29
+ if (!isSetMemberExpression(setExpression.callee))
30
+ return undefined;
31
+ if (setExpression.arguments.length !== 2)
32
+ return undefined;
33
+ const [valueArgument, offsetArgument] = setExpression.arguments;
34
+ if (!valueArgument || !offsetArgument)
35
+ return undefined;
36
+ if (!t.isIdentifier(valueArgument, { name: loopVariableName }))
37
+ return undefined;
38
+ if (!t.isIdentifier(offsetArgument))
39
+ return undefined;
40
+ const outputName = setExpression.callee.object.name;
41
+ const offsetName = offsetArgument.name;
42
+ if (!isPlusEqualsLengthOrByteLength(offsetExpression, offsetName, loopVariableName)) {
43
+ return undefined;
44
+ }
45
+ const outputBinding = loopPath.scope.getBinding(outputName);
46
+ if (!outputBinding)
47
+ return undefined;
48
+ const offsetBinding = loopPath.scope.getBinding(offsetName);
49
+ if (!offsetBinding)
50
+ return undefined;
51
+ const arrayName = t.isIdentifier(loopPath.node.right)
52
+ ? loopPath.node.right.name
53
+ : "";
54
+ const arrayBinding = arrayName
55
+ ? loopPath.scope.getBinding(arrayName)
56
+ : undefined;
57
+ const arrayScope = arrayBinding?.kind === "param" ? arrayBinding.scope : undefined;
58
+ return {
59
+ outputBinding,
60
+ outputName,
61
+ offsetName,
62
+ offsetScope: offsetBinding.scope,
63
+ loopVariableName,
64
+ loopVarScope: loopVariablePath.scope,
65
+ arrayName,
66
+ arrayScope,
67
+ };
68
+ };
@@ -44,17 +44,23 @@ const findMatchesInStatementList = (functionPath, statementPaths) => {
44
44
  const secondLoopMatch = getForOfSet(secondLoopStatement, outputMatch.name, offsetMatch.name);
45
45
  if (!secondLoopMatch)
46
46
  continue;
47
- if (firstLoopMatch.arrayName !== secondLoopMatch.arrayName)
47
+ if (!t.isNodesEquivalent(firstLoopMatch.arrayExpression, secondLoopMatch.arrayExpression)) {
48
48
  continue;
49
+ }
49
50
  const returnName = getReturnName(returnStatement);
50
51
  if (!returnName)
51
52
  continue;
52
53
  if (returnName !== outputMatch.name)
53
54
  continue;
54
- const arrayBinding = functionPath.scope.getBinding(firstLoopMatch.arrayName);
55
+ const arrayName = t.isIdentifier(firstLoopMatch.arrayExpression)
56
+ ? firstLoopMatch.arrayExpression.name
57
+ : "";
58
+ const arrayBinding = arrayName
59
+ ? functionPath.scope.getBinding(arrayName)
60
+ : undefined;
55
61
  const arrayScope = arrayBinding?.kind === "param" ? arrayBinding.scope : undefined;
56
62
  matches.push({
57
- arrayName: firstLoopMatch.arrayName,
63
+ arrayName,
58
64
  arrayScope,
59
65
  totalLengthName: totalLengthMatch.name,
60
66
  totalLengthScope: totalLengthMatch.scope,
@@ -0,0 +1,14 @@
1
+ import type { NodePath, Scope } from "@babel/traverse";
2
+ import type { Function } from "@babel/types";
3
+ type Uint8ArrayOutputRenameMatch = {
4
+ outputScope: Scope;
5
+ outputName: string;
6
+ offsetName: string;
7
+ offsetScope: Scope;
8
+ loopVariableName: string;
9
+ loopVarScope: Scope;
10
+ arrayName: string;
11
+ arrayScope: Scope | undefined;
12
+ };
13
+ export declare const findUint8ArrayOutputRenameMatches: (functionPath: NodePath<Function>) => Uint8ArrayOutputRenameMatch[];
14
+ export {};
@@ -0,0 +1,60 @@
1
+ import * as t from "@babel/types";
2
+ import { findLoopCopyMatch, } from "./find-loop-copy-match.js";
3
+ export const findUint8ArrayOutputRenameMatches = (functionPath) => {
4
+ const uint8ArrayBindings = new Set();
5
+ const returnedBindings = new Set();
6
+ const loopMatches = [];
7
+ functionPath.traverse({
8
+ Function(innerPath) {
9
+ innerPath.skip();
10
+ },
11
+ VariableDeclarator(variablePath) {
12
+ const idPath = variablePath.get("id");
13
+ if (!idPath.isIdentifier())
14
+ return;
15
+ const initPath = variablePath.get("init");
16
+ if (!initPath.isNewExpression())
17
+ return;
18
+ if (!t.isIdentifier(initPath.node.callee, { name: "Uint8Array" })) {
19
+ return;
20
+ }
21
+ const binding = variablePath.scope.getBinding(idPath.node.name);
22
+ if (!binding)
23
+ return;
24
+ uint8ArrayBindings.add(binding);
25
+ },
26
+ ReturnStatement(returnPath) {
27
+ const argumentPath = returnPath.get("argument");
28
+ if (!argumentPath.isIdentifier())
29
+ return;
30
+ const binding = returnPath.scope.getBinding(argumentPath.node.name);
31
+ if (!binding)
32
+ return;
33
+ returnedBindings.add(binding);
34
+ },
35
+ ForOfStatement(loopPath) {
36
+ const loopMatch = findLoopCopyMatch(loopPath);
37
+ if (!loopMatch)
38
+ return;
39
+ loopMatches.push(loopMatch);
40
+ },
41
+ });
42
+ const outputMatches = [];
43
+ for (const match of loopMatches) {
44
+ if (!uint8ArrayBindings.has(match.outputBinding))
45
+ continue;
46
+ if (!returnedBindings.has(match.outputBinding))
47
+ continue;
48
+ outputMatches.push({
49
+ outputScope: match.outputBinding.scope,
50
+ outputName: match.outputName,
51
+ offsetScope: match.offsetScope,
52
+ offsetName: match.offsetName,
53
+ loopVarScope: match.loopVarScope,
54
+ loopVariableName: match.loopVariableName,
55
+ arrayName: match.arrayName,
56
+ arrayScope: match.arrayScope,
57
+ });
58
+ }
59
+ return outputMatches;
60
+ };
@@ -0,0 +1,3 @@
1
+ import type { NodePath } from "@babel/traverse";
2
+ import type { Expression, Statement } from "@babel/types";
3
+ export declare const getLoopSetAndOffsetExpressions: (statements: NodePath<Statement>[]) => [setExpression: Expression, offsetExpression: Expression] | undefined;
@@ -0,0 +1,28 @@
1
+ import * as t from "@babel/types";
2
+ export const getLoopSetAndOffsetExpressions = (statements) => {
3
+ if (statements.length === 2) {
4
+ const [setStatement, offsetStatement] = statements;
5
+ if (!setStatement || !offsetStatement)
6
+ return undefined;
7
+ if (!setStatement.isExpressionStatement())
8
+ return undefined;
9
+ if (!offsetStatement.isExpressionStatement())
10
+ return undefined;
11
+ return [setStatement.node.expression, offsetStatement.node.expression];
12
+ }
13
+ if (statements.length !== 1)
14
+ return undefined;
15
+ const [singleStatement] = statements;
16
+ if (!singleStatement)
17
+ return undefined;
18
+ if (!singleStatement.isExpressionStatement())
19
+ return undefined;
20
+ if (!t.isSequenceExpression(singleStatement.node.expression))
21
+ return undefined;
22
+ if (singleStatement.node.expression.expressions.length !== 2)
23
+ return undefined;
24
+ const [setExpression, offsetExpression] = singleStatement.node.expression.expressions;
25
+ if (!setExpression || !offsetExpression)
26
+ return undefined;
27
+ return [setExpression, offsetExpression];
28
+ };
@@ -0,0 +1,3 @@
1
+ import type { NodePath } from "@babel/traverse";
2
+ import type { Function } from "@babel/types";
3
+ export declare const hasLocalDynamicNameLookup: (functionPath: NodePath<Function>) => boolean;
@@ -0,0 +1,38 @@
1
+ import * as t from "@babel/types";
2
+ import { isEvalLikeCallCallee } from "../../transforms/dynamic-name-lookup/eval-like-call-detection.js";
3
+ export const hasLocalDynamicNameLookup = (functionPath) => {
4
+ let found = false;
5
+ functionPath.traverse({
6
+ WithStatement(path) {
7
+ found = true;
8
+ path.stop();
9
+ },
10
+ ReferencedIdentifier(path) {
11
+ if (found)
12
+ return;
13
+ if (!t.isIdentifier(path.node, { name: "eval" }))
14
+ return;
15
+ if (path.scope.getBinding("eval"))
16
+ return;
17
+ found = true;
18
+ path.stop();
19
+ },
20
+ CallExpression(path) {
21
+ if (found)
22
+ return;
23
+ if (!isEvalLikeCallCallee(path.node.callee, path.scope))
24
+ return;
25
+ found = true;
26
+ path.stop();
27
+ },
28
+ OptionalCallExpression(path) {
29
+ if (found)
30
+ return;
31
+ if (!isEvalLikeCallCallee(path.node.callee, path.scope))
32
+ return;
33
+ found = true;
34
+ path.stop();
35
+ },
36
+ });
37
+ return found;
38
+ };
@@ -0,0 +1,2 @@
1
+ import type { Expression } from "@babel/types";
2
+ export declare const isPlusEqualsLengthOrByteLength: (expression: Expression, targetName: string, loopVariableName: string) => boolean;
@@ -0,0 +1,25 @@
1
+ import * as t from "@babel/types";
2
+ const isLengthOrByteLengthAccess = (expression, objectName) => {
3
+ if (!t.isMemberExpression(expression))
4
+ return false;
5
+ if (!t.isIdentifier(expression.object, { name: objectName }))
6
+ return false;
7
+ if (!expression.computed && t.isIdentifier(expression.property)) {
8
+ return (expression.property.name === "length" ||
9
+ expression.property.name === "byteLength");
10
+ }
11
+ if (expression.computed && t.isStringLiteral(expression.property)) {
12
+ return (expression.property.value === "length" ||
13
+ expression.property.value === "byteLength");
14
+ }
15
+ return false;
16
+ };
17
+ export const isPlusEqualsLengthOrByteLength = (expression, targetName, loopVariableName) => {
18
+ if (!t.isAssignmentExpression(expression))
19
+ return false;
20
+ if (expression.operator !== "+=")
21
+ return false;
22
+ if (!t.isIdentifier(expression.left, { name: targetName }))
23
+ return false;
24
+ return isLengthOrByteLengthAccess(expression.right, loopVariableName);
25
+ };
@@ -0,0 +1,2 @@
1
+ import type { MemberExpression } from "@babel/types";
2
+ export declare const isSetMemberExpression: (callee: MemberExpression) => boolean;
@@ -0,0 +1,10 @@
1
+ import * as t from "@babel/types";
2
+ export const isSetMemberExpression = (callee) => {
3
+ if (!callee.computed && t.isIdentifier(callee.property, { name: "set" })) {
4
+ return true;
5
+ }
6
+ if (callee.computed && t.isStringLiteral(callee.property, { value: "set" })) {
7
+ return true;
8
+ }
9
+ return false;
10
+ };
@@ -1,12 +1,12 @@
1
1
  import type { NodePath, Scope } from "@babel/traverse";
2
- import type { Statement } from "@babel/types";
2
+ import type { Expression, Statement } from "@babel/types";
3
3
  type ForOfLengthSumMatch = {
4
- arrayName: string;
4
+ arrayExpression: Expression;
5
5
  loopVariableName: string;
6
6
  loopVarScope: Scope;
7
7
  };
8
8
  type ForOfSetMatch = {
9
- arrayName: string;
9
+ arrayExpression: Expression;
10
10
  loopVariableName: string;
11
11
  loopVarScope: Scope;
12
12
  };
@@ -1,13 +1,39 @@
1
- import { getArrayIdentifier, getBodyStatements, getLoopVariable, isPlusEqualsLength, isSetCall, } from "./ast-helpers.js";
1
+ import * as t from "@babel/types";
2
+ import { getArrayExpression, getBodyStatements, getLoopVariable, isPlusEqualsLength, isSetCall, } from "./ast-helpers.js";
3
+ const getSetAndOffsetExpressions = (statements) => {
4
+ if (statements.length === 2) {
5
+ const [setStatement, offsetStatement] = statements;
6
+ if (!setStatement || !offsetStatement)
7
+ return undefined;
8
+ if (!setStatement.isExpressionStatement())
9
+ return undefined;
10
+ if (!offsetStatement.isExpressionStatement())
11
+ return undefined;
12
+ return [setStatement.node.expression, offsetStatement.node.expression];
13
+ }
14
+ if (statements.length !== 1)
15
+ return undefined;
16
+ const [singleStatement] = statements;
17
+ if (!singleStatement)
18
+ return undefined;
19
+ if (!singleStatement.isExpressionStatement())
20
+ return undefined;
21
+ if (!t.isSequenceExpression(singleStatement.node.expression))
22
+ return undefined;
23
+ if (singleStatement.node.expression.expressions.length !== 2)
24
+ return undefined;
25
+ const [setExpression, offsetExpression] = singleStatement.node.expression.expressions;
26
+ if (!setExpression || !offsetExpression)
27
+ return undefined;
28
+ return [setExpression, offsetExpression];
29
+ };
2
30
  export const getForOfLengthSum = (path, totalLengthName) => {
3
31
  if (!path.isForOfStatement())
4
32
  return undefined;
5
33
  const loopVariablePath = getLoopVariable(path);
6
34
  if (!loopVariablePath)
7
35
  return undefined;
8
- const arrayName = getArrayIdentifier(path.node.right);
9
- if (!arrayName)
10
- return undefined;
36
+ const arrayExpression = getArrayExpression(path.node.right);
11
37
  const statements = getBodyStatements(path.get("body"));
12
38
  if (statements.length !== 1)
13
39
  return undefined;
@@ -21,7 +47,7 @@ export const getForOfLengthSum = (path, totalLengthName) => {
21
47
  return undefined;
22
48
  }
23
49
  return {
24
- arrayName,
50
+ arrayExpression,
25
51
  loopVariableName: loopVariablePath.node.name,
26
52
  loopVarScope: loopVariablePath.scope,
27
53
  };
@@ -32,29 +58,20 @@ export const getForOfSet = (path, outputName, offsetName) => {
32
58
  const loopVariablePath = getLoopVariable(path);
33
59
  if (!loopVariablePath)
34
60
  return undefined;
35
- const arrayName = getArrayIdentifier(path.node.right);
36
- if (!arrayName)
37
- return undefined;
61
+ const arrayExpression = getArrayExpression(path.node.right);
38
62
  const statements = getBodyStatements(path.get("body"));
39
- if (statements.length !== 2)
40
- return undefined;
41
- const [setStatement, offsetStatement] = statements;
42
- if (!setStatement || !offsetStatement)
43
- return undefined;
44
- if (!setStatement.isExpressionStatement())
45
- return undefined;
46
- if (!offsetStatement.isExpressionStatement())
63
+ const setAndOffsetExpressions = getSetAndOffsetExpressions(statements);
64
+ if (!setAndOffsetExpressions)
47
65
  return undefined;
48
- const setExpression = setStatement.node.expression;
66
+ const [setExpression, offsetExpression] = setAndOffsetExpressions;
49
67
  if (!isSetCall(setExpression, outputName, loopVariablePath.node.name, offsetName)) {
50
68
  return undefined;
51
69
  }
52
- const offsetExpression = offsetStatement.node.expression;
53
70
  if (!isPlusEqualsLength(offsetExpression, offsetName, loopVariablePath.node.name)) {
54
71
  return undefined;
55
72
  }
56
73
  return {
57
- arrayName,
74
+ arrayExpression,
58
75
  loopVariableName: loopVariablePath.node.name,
59
76
  loopVarScope: loopVariablePath.scope,
60
77
  };
@@ -1,14 +1,14 @@
1
1
  {
2
- "recommended": true,
3
- "recommendedOrder": 100,
4
2
  "notes": "Measured with baseline none: 0.00%. Added to recommended for readability.",
5
3
  "evaluations": {
6
4
  "claude-code-2.1.10:claude-code-2.1.11": {
7
5
  "diffSizePercent": 100,
8
- "evaluatedAt": "2026-02-07T21:19:46.535Z",
9
- "changedLines": 0,
10
- "durationSeconds": 223.248709286,
11
- "stableNames": 1357
6
+ "evaluatedAt": "2026-02-21T07:21:13.555Z",
7
+ "changedLines": 88,
8
+ "durationSeconds": 31.42075075,
9
+ "stableNames": 1361
12
10
  }
13
- }
11
+ },
12
+ "recommended": true,
13
+ "recommendedOrder": 100
14
14
  }
@@ -2,6 +2,8 @@ import { createRequire } from "node:module";
2
2
  import { RenameGroup } from "../../core/stable-naming.js";
3
3
  import { getFilesToProcess, } from "../../core/types.js";
4
4
  import { findUint8ArrayConcatMatches } from "./find-uint8array-concat-match.js";
5
+ import { findUint8ArrayOutputRenameMatches } from "./find-uint8array-output-rename-matches.js";
6
+ import { hasLocalDynamicNameLookup } from "./has-local-dynamic-name-lookup.js";
5
7
  const require = createRequire(import.meta.url);
6
8
  // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
7
9
  const traverse = require("@babel/traverse").default;
@@ -30,9 +32,33 @@ export const renameUint8arrayConcatVariablesTransform = {
30
32
  traverse(fileInfo.ast, {
31
33
  Function(path) {
32
34
  nodesVisited++;
33
- const matches = findUint8ArrayConcatMatches(path);
34
- if (matches.length === 0)
35
+ if (hasLocalDynamicNameLookup(path))
35
36
  return;
37
+ for (const outputMatch of findUint8ArrayOutputRenameMatches(path)) {
38
+ if (outputMatch.arrayScope) {
39
+ addRenameOnce(renamedVariablesByScope, group, {
40
+ scope: outputMatch.arrayScope,
41
+ currentName: outputMatch.arrayName,
42
+ baseName: "chunks",
43
+ });
44
+ }
45
+ addRenameOnce(renamedVariablesByScope, group, {
46
+ scope: outputMatch.outputScope,
47
+ currentName: outputMatch.outputName,
48
+ baseName: "bytes",
49
+ });
50
+ addRenameOnce(renamedVariablesByScope, group, {
51
+ scope: outputMatch.offsetScope,
52
+ currentName: outputMatch.offsetName,
53
+ baseName: "byteOffset",
54
+ });
55
+ addRenameOnce(renamedVariablesByScope, group, {
56
+ scope: outputMatch.loopVarScope,
57
+ currentName: outputMatch.loopVariableName,
58
+ baseName: "chunk",
59
+ });
60
+ }
61
+ const matches = findUint8ArrayConcatMatches(path);
36
62
  for (const match of matches) {
37
63
  if (match.arrayScope) {
38
64
  addRenameOnce(renamedVariablesByScope, group, {
@@ -1,12 +1,12 @@
1
1
  {
2
- "notes": "Stabilizes deferred top-level vars whose RHS contains only stable identifiers (globals or $h_/$f_/$v_ prefixed names). Derives names by hashing the RHS code. Complements stabilize-deferred-top-level-bindings by handling multi-var functions.",
2
+ "notes": "Stabilizes deferred top-level var/let bindings whose RHS contains only stable identifiers (globals or $h_/$f_/$v_ prefixed names). Derives names by hashing the RHS code. Complements stabilize-deferred-top-level-bindings by handling multi-var functions.",
3
3
  "evaluations": {
4
4
  "claude-code-2.1.10:claude-code-2.1.11": {
5
- "diffSizePercent": 98.27455871791965,
6
- "evaluatedAt": "2026-02-18T06:53:13.858Z",
7
- "changedLines": 6008,
8
- "durationSeconds": 43.110313000000005,
9
- "stableNames": 1996
5
+ "diffSizePercent": 97.66602411460104,
6
+ "evaluatedAt": "2026-02-21T09:23:30.126Z",
7
+ "changedLines": 9309,
8
+ "durationSeconds": 44.433428834,
9
+ "stableNames": 2231
10
10
  }
11
11
  },
12
12
  "recommended": true,
@@ -14,9 +14,25 @@ const traverse = require("@babel/traverse").default;
14
14
  // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
15
15
  const generate = require("@babel/generator").default;
16
16
  const hashCode = (code) => createHash("sha256").update(code).digest("hex").slice(0, 16);
17
+ const hasNameIndependentShadowingRisk = (bindingScope, bindingName) => {
18
+ const binding = bindingScope.getBinding(bindingName);
19
+ if (!binding)
20
+ return true;
21
+ const identifierPaths = [
22
+ ...binding.referencePaths,
23
+ ...binding.constantViolations,
24
+ ];
25
+ return identifierPaths.some((identifierPath) => {
26
+ if (identifierPath.scope === bindingScope)
27
+ return false;
28
+ return (identifierPath.findParent((path) => {
29
+ return t.isWithStatement(path.node);
30
+ }) !== null);
31
+ });
32
+ };
17
33
  export const stabilizeDeferredStableRhsTransform = {
18
34
  id: "stabilize-deferred-stable-rhs",
19
- description: "Renames deferred top-level vars whose RHS contains only stable identifiers " +
35
+ description: "Renames deferred top-level var/let bindings whose RHS contains only stable identifiers " +
20
36
  "(globals or $h_/$f_/$v_ prefixed). Derives stable names by hashing the RHS code. " +
21
37
  "Complements stabilize-deferred-top-level-bindings by handling multi-var functions.",
22
38
  scope: "file",
@@ -37,14 +53,14 @@ export const stabilizeDeferredStableRhsTransform = {
37
53
  continue;
38
54
  const fileProgramScope = programScope;
39
55
  const programScopeBindings = new Set(Object.keys(fileProgramScope.bindings));
40
- // Step 1: Find all uninitialized top-level vars that aren't already stable
56
+ // Step 1: Find all uninitialized top-level var/let bindings that are not already stable
41
57
  const uninitializedVariables = new Set();
42
58
  traverse(fileInfo.ast, {
43
59
  VariableDeclaration(path) {
44
60
  nodesVisited++;
45
61
  if (path.parent.type !== "Program")
46
62
  return;
47
- if (path.node.kind !== "var")
63
+ if (path.node.kind !== "var" && path.node.kind !== "let")
48
64
  return;
49
65
  for (const declaration of path.node.declarations) {
50
66
  if (!declaration.init &&
@@ -55,7 +71,7 @@ export const stabilizeDeferredStableRhsTransform = {
55
71
  }
56
72
  },
57
73
  });
58
- // Step 2: Find assignments to those vars inside functions.
74
+ // Step 2: Find assignments to those bindings inside functions.
59
75
  // Exclude shadowed locals by requiring the assignment target to resolve
60
76
  // to the program-scope binding for that name.
61
77
  const assignments = [];
@@ -72,9 +88,6 @@ export const stabilizeDeferredStableRhsTransform = {
72
88
  return;
73
89
  if (path.scope.getBinding(variableName) !== programBinding)
74
90
  return;
75
- const functionPath = path.findParent((p) => p.isFunction());
76
- if (!functionPath)
77
- return;
78
91
  // Check stability and capture RHS code NOW, before any renames happen.
79
92
  // This prevents order-dependent cascading where renaming one var
80
93
  // makes another var's RHS appear stable.
@@ -82,7 +95,6 @@ export const stabilizeDeferredStableRhsTransform = {
82
95
  const isStable = hasAllStableReferences(rhsNode, programScopeBindings);
83
96
  assignments.push({
84
97
  variableName,
85
- rhsNode,
86
98
  rhsCode: isStable
87
99
  ? generate(rhsNode, { concise: true }).code
88
100
  : undefined,
@@ -90,7 +102,7 @@ export const stabilizeDeferredStableRhsTransform = {
90
102
  },
91
103
  });
92
104
  // Step 3: Apply renames for assignments with all-stable RHS.
93
- // Deduplicate by variable name — if a var has multiple assignments,
105
+ // Deduplicate by variable name — if a binding has multiple assignments,
94
106
  // use only the first (order is AST traversal order).
95
107
  // Track used target names to prevent collisions: two vars with identical
96
108
  // RHS code produce the same hash, and renameBindingInPlace does not update
@@ -104,13 +116,19 @@ export const stabilizeDeferredStableRhsTransform = {
104
116
  if (seen.has(variableName))
105
117
  continue;
106
118
  seen.add(variableName);
107
- const newName = `$v_${hashCode(rhsCode)}`;
108
- if (usedNewNames.has(newName))
109
- continue;
110
- if (!canRenameBindingSafely(programScope, variableName, newName))
119
+ if (hasNameIndependentShadowingRisk(programScope, variableName)) {
111
120
  continue;
112
- if (renameBindingInPlace(programScope, variableName, newName)) {
113
- usedNewNames.add(newName);
121
+ }
122
+ const baseName = `$v_${hashCode(rhsCode)}`;
123
+ let candidateName = baseName;
124
+ let suffix = 2;
125
+ while (usedNewNames.has(candidateName) ||
126
+ !canRenameBindingSafely(programScope, variableName, candidateName)) {
127
+ candidateName = `${baseName}_${suffix}`;
128
+ suffix++;
129
+ }
130
+ if (renameBindingInPlace(programScope, variableName, candidateName)) {
131
+ usedNewNames.add(candidateName);
114
132
  fileTransformations++;
115
133
  }
116
134
  }
@@ -1,6 +1,11 @@
1
+ import type { NodePath } from "@babel/traverse";
1
2
  import type { ArrowFunctionExpression, FunctionExpression } from "@babel/types";
2
3
  /**
3
4
  * Checks if a function looks like a CommonJS factory callback.
4
- * Heuristic: 1-2 parameters with the first being an identifier (e.g., exports, module).
5
+ * Heuristic:
6
+ * - 1-2 parameters: preserve existing broad behavior (first parameter must be an identifier).
7
+ * - 3 parameters: preserve broad matching, but reject known middleware
8
+ * handler signatures (req/res/next-like) that are outside this transform's
9
+ * intended scope.
5
10
  */
6
- export declare const isFactoryCallback: (callback: FunctionExpression | ArrowFunctionExpression) => boolean;
11
+ export declare const isFactoryCallback: (callbackPath: NodePath<FunctionExpression | ArrowFunctionExpression>) => boolean;
@@ -1,11 +1,37 @@
1
1
  import * as t from "@babel/types";
2
+ const MIDDLEWARE_PARAMETER_SIGNATURES = new Set([
3
+ "req|res|next",
4
+ "request|response|next",
5
+ "ctx|next|error",
6
+ "context|next|error",
7
+ ]);
8
+ const normalizeParameterName = (name) => name.replace(/^[$_]+/u, "").toLowerCase();
9
+ const isKnownMiddlewareSignature = (names) => MIDDLEWARE_PARAMETER_SIGNATURES.has(names.join("|"));
2
10
  /**
3
11
  * Checks if a function looks like a CommonJS factory callback.
4
- * Heuristic: 1-2 parameters with the first being an identifier (e.g., exports, module).
12
+ * Heuristic:
13
+ * - 1-2 parameters: preserve existing broad behavior (first parameter must be an identifier).
14
+ * - 3 parameters: preserve broad matching, but reject known middleware
15
+ * handler signatures (req/res/next-like) that are outside this transform's
16
+ * intended scope.
5
17
  */
6
- export const isFactoryCallback = (callback) => {
7
- const parameters = callback.params;
8
- if (parameters.length === 0 || parameters.length > 2)
18
+ export const isFactoryCallback = (callbackPath) => {
19
+ const parameters = callbackPath.node.params;
20
+ if (parameters.length === 0 || parameters.length > 3)
9
21
  return false;
10
- return t.isIdentifier(parameters[0]);
22
+ const firstParameter = parameters[0];
23
+ if (!t.isIdentifier(firstParameter))
24
+ return false;
25
+ if (parameters.length < 3)
26
+ return true;
27
+ const secondParameter = parameters[1];
28
+ const thirdParameter = parameters[2];
29
+ if (!t.isIdentifier(secondParameter) || !t.isIdentifier(thirdParameter))
30
+ return true;
31
+ const normalizedParameterNames = [
32
+ normalizeParameterName(firstParameter.name),
33
+ normalizeParameterName(secondParameter.name),
34
+ normalizeParameterName(thirdParameter.name),
35
+ ];
36
+ return !isKnownMiddlewareSignature(normalizedParameterNames);
11
37
  };
@@ -2,11 +2,11 @@
2
2
  "notes": "Stabilizes local bindings inside CommonJS factory callbacks using content-hash naming ($f_<hash>). Must run AFTER stabilize-top-level-bindings. Combined with top-level stabilization, achieves 33.49% diff size (67% reduction from baseline).",
3
3
  "evaluations": {
4
4
  "claude-code-2.1.10:claude-code-2.1.11": {
5
- "diffSizePercent": 56.44536417583248,
6
- "evaluatedAt": "2026-02-18T06:56:26.504Z",
7
- "changedLines": 115147,
8
- "durationSeconds": 85.89585675000001,
9
- "stableNames": 13284
5
+ "diffSizePercent": 56.432135162716854,
6
+ "evaluatedAt": "2026-02-21T09:46:16.200Z",
7
+ "changedLines": 115623,
8
+ "durationSeconds": 81.049385083,
9
+ "stableNames": 13327
10
10
  }
11
11
  },
12
12
  "recommended": true,
@@ -19,14 +19,14 @@ const renameCallbackLocalBindings = (path) => {
19
19
  if (!t.isFunctionExpression(firstArgument) &&
20
20
  !t.isArrowFunctionExpression(firstArgument))
21
21
  return 0;
22
- // Verify it looks like a callback shape this transform should process
23
- if (!isFactoryCallback(firstArgument))
24
- return 0;
25
22
  // Get the path to the function argument
26
23
  const functionArgumentPath = path.get("arguments.0");
27
24
  if (!functionArgumentPath.isFunctionExpression() &&
28
25
  !functionArgumentPath.isArrowFunctionExpression())
29
26
  return 0;
27
+ // Verify it looks like a callback shape this transform should process
28
+ if (!isFactoryCallback(functionArgumentPath))
29
+ return 0;
30
30
  // Skip renaming if callback contains dynamic name lookup (eval, with, etc.)
31
31
  if (hasDynamicNameLookup(functionArgumentPath))
32
32
  return 0;
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "miniread",
3
3
  "author": "Łukasz Jerciński",
4
4
  "license": "MIT",
5
- "version": "1.115.3",
5
+ "version": "1.115.4",
6
6
  "description": "Transform minified JavaScript/TypeScript into a more readable form using deterministic AST-based transforms.",
7
7
  "repository": {
8
8
  "type": "git",