@wrongstack/plugins 0.293.0 → 0.295.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 (189) hide show
  1. package/README.md +7 -71
  2. package/dist/accessibility-auditor/index.d.ts +1 -1
  3. package/dist/accessibility-auditor/index.d.ts.map +1 -1
  4. package/dist/accessibility-auditor.js +31 -31
  5. package/dist/agent-handoff/index.d.ts +1 -1
  6. package/dist/agent-handoff/index.d.ts.map +1 -1
  7. package/dist/api-compatibility-gate/index.d.ts +1 -1
  8. package/dist/api-compatibility-gate/index.d.ts.map +1 -1
  9. package/dist/api-compatibility-gate.js +21 -16
  10. package/dist/audit/index.d.ts +386 -0
  11. package/dist/audit/index.d.ts.map +1 -0
  12. package/dist/audit.d.ts +1 -0
  13. package/dist/audit.js +454 -0
  14. package/dist/auto-doc/index.d.ts +1 -1
  15. package/dist/auto-doc/index.d.ts.map +1 -1
  16. package/dist/auto-escalate/index.d.ts +1 -1
  17. package/dist/auto-escalate/index.d.ts.map +1 -1
  18. package/dist/auto-i18n-extractor/index.d.ts +1 -1
  19. package/dist/auto-i18n-extractor/index.d.ts.map +1 -1
  20. package/dist/auto-i18n-extractor.js +2 -1
  21. package/dist/branch-guard/index.d.ts +1 -1
  22. package/dist/branch-guard/index.d.ts.map +1 -1
  23. package/dist/branch-guard.js +9 -1
  24. package/dist/catalog.d.ts +5 -30
  25. package/dist/catalog.d.ts.map +1 -1
  26. package/dist/changelog-writer/index.d.ts +1 -1
  27. package/dist/changelog-writer/index.d.ts.map +1 -1
  28. package/dist/checkpoint/index.d.ts +1 -1
  29. package/dist/checkpoint/index.d.ts.map +1 -1
  30. package/dist/checkpoint.js +7 -8
  31. package/dist/code-metrics/index.d.ts +1 -1
  32. package/dist/code-metrics/index.d.ts.map +1 -1
  33. package/dist/code-metrics.js +29 -29
  34. package/dist/commit-validator/index.d.ts +1 -1
  35. package/dist/commit-validator/index.d.ts.map +1 -1
  36. package/dist/config-validator/index.d.ts +1 -1
  37. package/dist/config-validator/index.d.ts.map +1 -1
  38. package/dist/context-pins/index.d.ts +1 -1
  39. package/dist/context-pins/index.d.ts.map +1 -1
  40. package/dist/cost-tracker/index.d.ts +1 -1
  41. package/dist/cost-tracker/index.d.ts.map +1 -1
  42. package/dist/cost-tracker.js +9 -2
  43. package/dist/cron/index.d.ts +1 -1
  44. package/dist/cron/index.d.ts.map +1 -1
  45. package/dist/dead-code-detector/index.d.ts +1 -1
  46. package/dist/dead-code-detector/index.d.ts.map +1 -1
  47. package/dist/dead-code-detector.js +26 -18
  48. package/dist/dep-guard/index.d.ts +1 -1
  49. package/dist/dep-guard/index.d.ts.map +1 -1
  50. package/dist/dependency-vulnerability-gate/index.d.ts +1 -1
  51. package/dist/dependency-vulnerability-gate/index.d.ts.map +1 -1
  52. package/dist/dependency-vulnerability-gate.js +34 -22
  53. package/dist/diff-summary/index.d.ts +1 -1
  54. package/dist/diff-summary/index.d.ts.map +1 -1
  55. package/dist/diff-summary.js +28 -29
  56. package/dist/doc-sync-guard/index.d.ts +1 -1
  57. package/dist/doc-sync-guard/index.d.ts.map +1 -1
  58. package/dist/doc-sync-guard.js +2 -1
  59. package/dist/duplicate-code-detector/index.d.ts +26 -1
  60. package/dist/duplicate-code-detector/index.d.ts.map +1 -1
  61. package/dist/duplicate-code-detector.js +178 -67
  62. package/dist/error-lens/index.d.ts +1 -1
  63. package/dist/error-lens/index.d.ts.map +1 -1
  64. package/dist/factories/index.d.ts +4 -0
  65. package/dist/factories/index.d.ts.map +1 -0
  66. package/dist/factories.d.ts +1 -0
  67. package/dist/factories.js +74 -0
  68. package/dist/feature-flag-tracker/index.d.ts +1 -1
  69. package/dist/feature-flag-tracker/index.d.ts.map +1 -1
  70. package/dist/feature-flag-tracker.js +29 -29
  71. package/dist/file-watcher/index.d.ts +1 -1
  72. package/dist/file-watcher/index.d.ts.map +1 -1
  73. package/dist/format-on-save/index.d.ts +1 -1
  74. package/dist/format-on-save/index.d.ts.map +1 -1
  75. package/dist/format-on-save.js +2 -1
  76. package/dist/generated-plugin-exports.d.ts +65 -0
  77. package/dist/generated-plugin-exports.d.ts.map +1 -0
  78. package/dist/git-autocommit/index.d.ts +1 -1
  79. package/dist/git-autocommit/index.d.ts.map +1 -1
  80. package/dist/import-organizer/index.d.ts +1 -1
  81. package/dist/import-organizer/index.d.ts.map +1 -1
  82. package/dist/import-organizer.js +2 -1
  83. package/dist/index.d.ts +4 -183
  84. package/dist/index.d.ts.map +1 -1
  85. package/dist/index.js +17675 -17377
  86. package/dist/injection-shield/index.d.ts +1 -1
  87. package/dist/injection-shield/index.d.ts.map +1 -1
  88. package/dist/interface-contract-guard/index.d.ts +1 -1
  89. package/dist/interface-contract-guard/index.d.ts.map +1 -1
  90. package/dist/interface-contract-guard.js +29 -29
  91. package/dist/knowledge-graph/index.d.ts +1 -1
  92. package/dist/knowledge-graph/index.d.ts.map +1 -1
  93. package/dist/license-audit-gate/index.d.ts +1 -1
  94. package/dist/license-audit-gate/index.d.ts.map +1 -1
  95. package/dist/lint-gate/index.d.ts +1 -1
  96. package/dist/lint-gate/index.d.ts.map +1 -1
  97. package/dist/lint-gate.js +10 -1
  98. package/dist/llm-cache/index.d.ts +5 -1
  99. package/dist/llm-cache/index.d.ts.map +1 -1
  100. package/dist/llm-cache.js +16 -8
  101. package/dist/loop-breaker/index.d.ts +1 -1
  102. package/dist/loop-breaker/index.d.ts.map +1 -1
  103. package/dist/loop-breaker.js +10 -4
  104. package/dist/manifest/index.d.ts +16 -0
  105. package/dist/manifest/index.d.ts.map +1 -0
  106. package/dist/manifest.d.ts +1 -0
  107. package/dist/manifest.js +152 -0
  108. package/dist/migration-planner/index.d.ts +1 -1
  109. package/dist/migration-planner/index.d.ts.map +1 -1
  110. package/dist/migration-planner.js +2 -1
  111. package/dist/model-router/index.d.ts +1 -1
  112. package/dist/model-router/index.d.ts.map +1 -1
  113. package/dist/notify-hub/index.d.ts +1 -1
  114. package/dist/notify-hub/index.d.ts.map +1 -1
  115. package/dist/notify-hub/webhook-channel.d.ts +1 -1
  116. package/dist/notify-hub/webhook-channel.d.ts.map +1 -1
  117. package/dist/path-guard/index.d.ts +1 -1
  118. package/dist/path-guard/index.d.ts.map +1 -1
  119. package/dist/performance-regression-gate/index.d.ts +1 -1
  120. package/dist/performance-regression-gate/index.d.ts.map +1 -1
  121. package/dist/plugin-stack-observer/index.d.ts +1 -1
  122. package/dist/plugin-stack-observer/index.d.ts.map +1 -1
  123. package/dist/pr-drafter/index.d.ts +1 -1
  124. package/dist/pr-drafter/index.d.ts.map +1 -1
  125. package/dist/pr-drafter.js +28 -29
  126. package/dist/process-guard/index.d.ts +1 -1
  127. package/dist/process-guard/index.d.ts.map +1 -1
  128. package/dist/prompt-firewall/index.d.ts +1 -1
  129. package/dist/prompt-firewall/index.d.ts.map +1 -1
  130. package/dist/refactor-suggester/index.d.ts +1 -1
  131. package/dist/refactor-suggester/index.d.ts.map +1 -1
  132. package/dist/refactor-suggester.js +29 -29
  133. package/dist/release-notes-generator/index.d.ts +1 -1
  134. package/dist/release-notes-generator/index.d.ts.map +1 -1
  135. package/dist/release-notes-generator.js +29 -22
  136. package/dist/runtime/index.d.ts +15 -0
  137. package/dist/runtime/index.d.ts.map +1 -1
  138. package/dist/runtime/llm.d.ts +1 -1
  139. package/dist/runtime/llm.d.ts.map +1 -1
  140. package/dist/runtime.js +43 -2
  141. package/dist/schema-evolution-guard/index.d.ts +1 -1
  142. package/dist/schema-evolution-guard/index.d.ts.map +1 -1
  143. package/dist/schema-evolution-guard.js +2 -1
  144. package/dist/secret-scanner/index.d.ts +1 -1
  145. package/dist/secret-scanner/index.d.ts.map +1 -1
  146. package/dist/secret-scanner.js +7 -1
  147. package/dist/security-hotspot-scanner/index.d.ts +1 -1
  148. package/dist/security-hotspot-scanner/index.d.ts.map +1 -1
  149. package/dist/security-hotspot-scanner.js +26 -19
  150. package/dist/semantic-search-indexer/index.d.ts +1 -1
  151. package/dist/semantic-search-indexer/index.d.ts.map +1 -1
  152. package/dist/semver-bump/index.d.ts +1 -1
  153. package/dist/semver-bump/index.d.ts.map +1 -1
  154. package/dist/semver-bump.js +78 -43
  155. package/dist/session-recap/index.d.ts +1 -1
  156. package/dist/session-recap/index.d.ts.map +1 -1
  157. package/dist/shell-check/index.d.ts +1 -1
  158. package/dist/shell-check/index.d.ts.map +1 -1
  159. package/dist/smart-rename/index.d.ts +1 -1
  160. package/dist/smart-rename/index.d.ts.map +1 -1
  161. package/dist/spec-linker/index.d.ts +1 -1
  162. package/dist/spec-linker/index.d.ts.map +1 -1
  163. package/dist/spec-linker.js +222 -20772
  164. package/dist/template-engine/index.d.ts +1 -1
  165. package/dist/template-engine/index.d.ts.map +1 -1
  166. package/dist/template-engine.js +4 -6
  167. package/dist/test-coverage-gate/index.d.ts +1 -1
  168. package/dist/test-coverage-gate/index.d.ts.map +1 -1
  169. package/dist/test-coverage-gate.js +29 -14
  170. package/dist/test-flake-detector/index.d.ts +1 -1
  171. package/dist/test-flake-detector/index.d.ts.map +1 -1
  172. package/dist/test-flake-detector.js +31 -20
  173. package/dist/test-generator/index.d.ts +1 -1
  174. package/dist/test-generator/index.d.ts.map +1 -1
  175. package/dist/test-runner-gate/index.d.ts +1 -1
  176. package/dist/test-runner-gate/index.d.ts.map +1 -1
  177. package/dist/test-runner-gate.js +2 -1
  178. package/dist/todo-listener/index.d.ts +1 -32
  179. package/dist/todo-listener/index.d.ts.map +1 -1
  180. package/dist/todo-tracker/index.d.ts +1 -1
  181. package/dist/todo-tracker/index.d.ts.map +1 -1
  182. package/dist/token-budget/index.d.ts +1 -1
  183. package/dist/token-budget/index.d.ts.map +1 -1
  184. package/dist/token-throttle/index.d.ts +1 -1
  185. package/dist/token-throttle/index.d.ts.map +1 -1
  186. package/dist/type-gate/index.d.ts +1 -1
  187. package/dist/type-gate/index.d.ts.map +1 -1
  188. package/dist/type-gate.js +11 -4
  189. package/package.json +80 -68
@@ -1,5 +1,5 @@
1
1
  // src/diff-summary/index.ts
2
- import { execFileSync } from "node:child_process";
2
+ import { execFile } from "node:child_process";
3
3
 
4
4
  // src/runtime/index.ts
5
5
  import { basename, extname, isAbsolute, relative, resolve } from "node:path";
@@ -15,7 +15,8 @@ function withinProjectPath(projectRoot, candidate) {
15
15
  return rel === "" || !rel.startsWith("..") && !isAbsolute(rel);
16
16
  }
17
17
  function withinProject(p) {
18
- return withinProjectPath(process.cwd(), p) || relative(process.cwd(), p) === ".";
18
+ const cwd = process.cwd();
19
+ return withinProjectPath(cwd, p) || relative(cwd, p) === ".";
19
20
  }
20
21
 
21
22
  // src/diff-summary/index.ts
@@ -64,36 +65,34 @@ function contentHash(s) {
64
65
  return h >>> 0;
65
66
  }
66
67
  var pathMemo = /* @__PURE__ */ new Map();
67
- function getGitDiff(filePath, contextLines, cwd) {
68
- const opts = {
69
- encoding: "utf-8",
70
- timeout: 3e3,
71
- cwd,
72
- stdio: ["pipe", "pipe", "pipe"]
73
- };
74
- let isTracked = false;
75
- try {
76
- execFileSync("git", ["ls-files", "--error-unmatch", "--", filePath], opts);
77
- isTracked = true;
78
- } catch {
79
- isTracked = false;
80
- }
68
+ function runGit(args, cwd) {
69
+ return new Promise((resolveCommand) => {
70
+ execFile(
71
+ "git",
72
+ args,
73
+ {
74
+ encoding: "utf8",
75
+ timeout: 3e3,
76
+ cwd,
77
+ windowsHide: true,
78
+ maxBuffer: 5 * 1024 * 1024
79
+ },
80
+ (error, stdout) => resolveCommand({ ok: !error, stdout })
81
+ );
82
+ });
83
+ }
84
+ async function getGitDiff(filePath, contextLines, cwd) {
85
+ const tracked = await runGit(["ls-files", "--error-unmatch", "--", filePath], cwd);
86
+ const isTracked = tracked.ok;
81
87
  try {
82
88
  let rawDiff;
83
89
  const contextFlag = `-U${contextLines}`;
84
90
  if (isTracked) {
85
- rawDiff = execFileSync("git", ["diff", contextFlag, "--", filePath], opts);
91
+ const result = await runGit(["diff", contextFlag, "--", filePath], cwd);
92
+ if (!result.ok) return null;
93
+ rawDiff = result.stdout;
86
94
  } else {
87
- try {
88
- rawDiff = execFileSync(
89
- "git",
90
- ["diff", "--no-index", contextFlag, "/dev/null", filePath],
91
- opts
92
- );
93
- } catch (err) {
94
- const e = err;
95
- rawDiff = e.stdout ?? "";
96
- }
95
+ rawDiff = (await runGit(["diff", "--no-index", contextFlag, "/dev/null", filePath], cwd)).stdout;
97
96
  }
98
97
  if (!rawDiff.trim()) {
99
98
  return { diff: "", added: 0, removed: 0, isNewFile: !isTracked };
@@ -185,7 +184,7 @@ var plugin = {
185
184
  pathMemo.clear();
186
185
  const cfg = readConfig(api.config.extensions?.["diff-summary"]);
187
186
  const cwd = typeof process.cwd === "function" ? process.cwd() : void 0;
188
- const hook = (input) => {
187
+ const hook = async (input) => {
189
188
  if (cfg.mode === "off") return;
190
189
  if (input.toolResult?.isError) return;
191
190
  const toolName = input.toolName ?? "";
@@ -212,7 +211,7 @@ var plugin = {
212
211
  return;
213
212
  }
214
213
  }
215
- const result = getGitDiff(filePath, cfg.includeContext, cwd);
214
+ const result = await getGitDiff(filePath, cfg.includeContext, cwd);
216
215
  if (!result) {
217
216
  state.fallbackCount += 1;
218
217
  return;
@@ -25,7 +25,7 @@
25
25
  *
26
26
  * @public
27
27
  */
28
- import type { Plugin } from '@wrongstack/core';
28
+ import type { Plugin } from '@wrongstack/core/types';
29
29
  declare const plugin: Plugin;
30
30
  export default plugin;
31
31
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/doc-sync-guard/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAGH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AA0I/C,QAAA,MAAM,MAAM,EAAE,MAuJb,CAAC;eAEa,MAAM"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/doc-sync-guard/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAGH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AA0IrD,QAAA,MAAM,MAAM,EAAE,MAuJb,CAAC;eAEa,MAAM"}
@@ -15,7 +15,8 @@ function withinProjectPath(projectRoot, candidate) {
15
15
  return rel === "" || !rel.startsWith("..") && !isAbsolute(rel);
16
16
  }
17
17
  function withinProject(p) {
18
- return withinProjectPath(process.cwd(), p) || relative(process.cwd(), p) === ".";
18
+ const cwd = process.cwd();
19
+ return withinProjectPath(cwd, p) || relative(cwd, p) === ".";
19
20
  }
20
21
 
21
22
  // src/doc-sync-guard/index.ts
@@ -25,7 +25,32 @@
25
25
  *
26
26
  * @public
27
27
  */
28
- import type { Plugin } from '@wrongstack/core';
28
+ import type { Plugin } from '@wrongstack/core/types';
29
+ /**
30
+ * Tunable budgets that bound the hook fingerprint index. Keeping the defaults
31
+ * together makes the file, fingerprint, and byte limits explicit.
32
+ */
33
+ export declare const hookIndexBudgets: {
34
+ /** Max number of files retained in the hook fingerprint index (LRU-evicted). */
35
+ maxFiles: number;
36
+ /** Max total fingerprints across the whole index (running-sum enforced, LRU-evicted). */
37
+ maxFingerprints: number;
38
+ /**
39
+ * Max candidate windows hashed and cached per file by the advisory hook. The
40
+ * on-demand detect_duplicate_code tool intentionally scans every window.
41
+ */
42
+ maxFingerprintsPerFile: number;
43
+ /** Files larger than this are skipped entirely by the hook (never read/extracted/cached). */
44
+ maxFileBytes: number;
45
+ };
46
+ /**
47
+ * Compact 53-bit hash of a fingerprint string. The hook index can retain up to
48
+ * 300k fingerprints, where a 32-bit hash has a material birthday-collision risk
49
+ * and can emit a false duplicate warning. The result remains a JS-safe integer,
50
+ * so the index keeps the compact `Set<number>` representation instead of storing
51
+ * raw fingerprint text.
52
+ */
53
+ export declare function hashFingerprint(fp: string): number;
29
54
  declare const plugin: Plugin;
30
55
  export default plugin;
31
56
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/duplicate-code-detector/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAIH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAiN/C,QAAA,MAAM,MAAM,EAAE,MAySb,CAAC;eAEa,MAAM"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/duplicate-code-detector/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAIH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AA8ErD;;;GAGG;AACH,eAAO,MAAM,gBAAgB;IAC3B,gFAAgF;IAChF,QAAQ;IACR,yFAAyF;IACzF,eAAe;IACf;;;OAGG;IACH,sBAAsB;IACtB,6FAA6F;IAC7F,YAAY;CACb,CAAC;AAyGF;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAelD;AA4HD,QAAA,MAAM,MAAM,EAAE,MA4Wb,CAAC;eAEa,MAAM"}
@@ -1,9 +1,8 @@
1
1
  // src/duplicate-code-detector/index.ts
2
- import { readFileSync, statSync as statSync2 } from "node:fs";
3
- import { isAbsolute as isAbsolute2, relative as relative2, resolve as resolve2 } from "node:path";
2
+ import { readFile, realpath, stat } from "node:fs/promises";
3
+ import { isAbsolute as isAbsolute2, relative as relative2, resolve as resolve2, sep } from "node:path";
4
4
 
5
5
  // src/runtime/index.ts
6
- import { existsSync, readdirSync, statSync } from "node:fs";
7
6
  import { basename, extname, isAbsolute, relative, resolve } from "node:path";
8
7
  var MAX_BUFFER_BYTES = 16 * 1024 * 1024;
9
8
  function hasLeadingDash(arg) {
@@ -17,44 +16,45 @@ function withinProjectPath(projectRoot, candidate) {
17
16
  return rel === "" || !rel.startsWith("..") && !isAbsolute(rel);
18
17
  }
19
18
  function withinProject(p) {
20
- return withinProjectPath(process.cwd(), p) || relative(process.cwd(), p) === ".";
19
+ const cwd = process.cwd();
20
+ return withinProjectPath(cwd, p) || relative(cwd, p) === ".";
21
21
  }
22
22
  var DEFAULT_EXCLUDE_DIRS = ["node_modules", "dist", ".git", "coverage"];
23
- function collectSourceFiles(root, opts) {
23
+ async function collectSourceFilesAsync(root, opts) {
24
+ const { readdir, stat: stat2 } = await import("node:fs/promises");
24
25
  const files = [];
25
- if (!existsSync(root)) return files;
26
- const s = statSync(root);
27
- if (s.isFile()) {
28
- if (matchesExtension(root, opts.extensions)) files.push(root);
26
+ try {
27
+ const s = await stat2(root);
28
+ if (s.isFile()) {
29
+ if (matchesExtension(root, opts.extensions)) files.push(root);
30
+ return files;
31
+ }
32
+ if (!s.isDirectory()) return files;
33
+ } catch {
29
34
  return files;
30
35
  }
31
- if (!s.isDirectory()) return files;
32
36
  const exclude = opts.excludeDirs ?? DEFAULT_EXCLUDE_DIRS;
33
- function walk(dir, depth) {
37
+ const excludeSet = new Set(exclude);
38
+ async function walk(dir, depth) {
34
39
  if (opts.maxDepth !== void 0 && depth > opts.maxDepth) return;
35
40
  let entries;
36
41
  try {
37
- entries = readdirSync(dir);
42
+ entries = await readdir(dir, { withFileTypes: true });
38
43
  } catch {
39
44
  return;
40
45
  }
46
+ entries.sort((a, b) => a.name.localeCompare(b.name));
41
47
  for (const entry of entries) {
42
- if (exclude.includes(entry)) continue;
43
- const full = resolve(dir, entry);
44
- let st;
45
- try {
46
- st = statSync(full);
47
- } catch {
48
- continue;
49
- }
50
- if (st.isDirectory()) {
51
- walk(full, depth + 1);
52
- } else if (st.isFile() && matchesExtension(full, opts.extensions)) {
48
+ if (excludeSet.has(entry.name)) continue;
49
+ const full = resolve(dir, entry.name);
50
+ if (entry.isDirectory()) {
51
+ await walk(full, depth + 1);
52
+ } else if (entry.isFile() && matchesExtension(full, opts.extensions)) {
53
53
  files.push(full);
54
54
  }
55
55
  }
56
56
  }
57
- walk(root, 0);
57
+ await walk(root, 0);
58
58
  return files;
59
59
  }
60
60
  function matchesExtension(p, exts) {
@@ -71,7 +71,24 @@ var state = {
71
71
  errorCount: 0,
72
72
  hookUnregister: null,
73
73
  lastHookWarning: /* @__PURE__ */ new Map(),
74
- fileIndex: /* @__PURE__ */ new Map()
74
+ fileIndex: /* @__PURE__ */ new Map(),
75
+ inFlightFingerprintReads: /* @__PURE__ */ new Map(),
76
+ indexFingerprintCount: 0,
77
+ hookIndexEvictions: 0,
78
+ oversizedFileSkips: 0
79
+ };
80
+ var hookIndexBudgets = {
81
+ /** Max number of files retained in the hook fingerprint index (LRU-evicted). */
82
+ maxFiles: 5e3,
83
+ /** Max total fingerprints across the whole index (running-sum enforced, LRU-evicted). */
84
+ maxFingerprints: 3e5,
85
+ /**
86
+ * Max candidate windows hashed and cached per file by the advisory hook. The
87
+ * on-demand detect_duplicate_code tool intentionally scans every window.
88
+ */
89
+ maxFingerprintsPerFile: 25e3,
90
+ /** Files larger than this are skipped entirely by the hook (never read/extracted/cached). */
91
+ maxFileBytes: 2 * 1024 * 1024
75
92
  };
76
93
  var DEFAULTS = {
77
94
  enabled: false,
@@ -81,18 +98,28 @@ var DEFAULTS = {
81
98
  excludeDirs: ["node_modules", "dist", ".git", "coverage"],
82
99
  maxFindings: 5
83
100
  };
101
+ var extensionsSet = new Set(DEFAULTS.extensions);
84
102
  function readConfig(raw) {
85
- if (!raw || typeof raw !== "object") return { ...DEFAULTS };
103
+ if (!raw || typeof raw !== "object") {
104
+ extensionsSet = new Set(DEFAULTS.extensions);
105
+ return { ...DEFAULTS };
106
+ }
86
107
  const r = raw;
108
+ const extensions = Array.isArray(r["extensions"]) ? r["extensions"].filter((x) => typeof x === "string") : DEFAULTS.extensions;
109
+ extensionsSet = new Set(extensions);
87
110
  return {
88
111
  enabled: r["enabled"] === true,
89
112
  minLines: typeof r["minLines"] === "number" && r["minLines"] >= 2 && r["minLines"] <= 100 ? r["minLines"] : DEFAULTS.minLines,
90
113
  threshold: typeof r["threshold"] === "number" && r["threshold"] > 0 && r["threshold"] <= 1 ? r["threshold"] : DEFAULTS.threshold,
91
- extensions: Array.isArray(r["extensions"]) ? r["extensions"].filter((x) => typeof x === "string") : DEFAULTS.extensions,
114
+ extensions,
92
115
  excludeDirs: Array.isArray(r["excludeDirs"]) ? r["excludeDirs"].filter((x) => typeof x === "string") : DEFAULTS.excludeDirs,
93
116
  maxFindings: typeof r["maxFindings"] === "number" && r["maxFindings"] >= 1 && r["maxFindings"] <= 500 ? r["maxFindings"] : DEFAULTS.maxFindings
94
117
  };
95
118
  }
119
+ function isWithinRoot(projectRoot, candidate) {
120
+ const rel = relative2(projectRoot, candidate);
121
+ return rel === "" || rel !== ".." && !rel.startsWith(`..${sep}`) && !isAbsolute2(rel);
122
+ }
96
123
  function toPosix(p) {
97
124
  return p.replace(/\\/g, "/");
98
125
  }
@@ -111,6 +138,40 @@ function normalizeLine(line) {
111
138
  function buildFingerprint(lines) {
112
139
  return lines.map(normalizeLine).filter((l) => l.length > 0).join("\n");
113
140
  }
141
+ function hashFingerprint(fp) {
142
+ let low = 3735928559;
143
+ let high = 1103547991;
144
+ for (let index = 0; index < fp.length; index += 1) {
145
+ const code = fp.charCodeAt(index);
146
+ low = Math.imul(low ^ code, 2654435761);
147
+ high = Math.imul(high ^ code, 1597334677);
148
+ }
149
+ low = Math.imul(low ^ low >>> 16, 2246822507) ^ Math.imul(high ^ high >>> 13, 3266489909);
150
+ high = Math.imul(high ^ high >>> 16, 2246822507) ^ Math.imul(low ^ low >>> 13, 3266489909);
151
+ return 4294967296 * (high & 2097151) + (low >>> 0);
152
+ }
153
+ function extractFingerprintHashes(content, minLines, maxWindows) {
154
+ const rawLines = content.split(/\r?\n/);
155
+ const normalizedLines = rawLines.map(normalizeLine);
156
+ const fingerprints = /* @__PURE__ */ new Set();
157
+ const windowCount = Math.min(Math.max(rawLines.length - minLines + 1, 0), maxWindows);
158
+ for (let index = 0; index < windowCount; index += 1) {
159
+ const fingerprint = normalizedLines.slice(index, index + minLines).filter((line) => line.length > 0).join("\n");
160
+ if (fingerprint.length > 0) fingerprints.add(hashFingerprint(fingerprint));
161
+ }
162
+ return fingerprints;
163
+ }
164
+ function evictHookIndex() {
165
+ while (state.fileIndex.size > hookIndexBudgets.maxFiles || state.indexFingerprintCount > hookIndexBudgets.maxFingerprints) {
166
+ const oldest = state.fileIndex.keys().next();
167
+ if (oldest.done) break;
168
+ const key = oldest.value;
169
+ const entry = state.fileIndex.get(key);
170
+ if (entry) state.indexFingerprintCount -= entry.fingerprints.size;
171
+ state.fileIndex.delete(key);
172
+ state.hookIndexEvictions += 1;
173
+ }
174
+ }
114
175
  function extractWindows(filePath, content, minLines) {
115
176
  const rawLines = content.split(/\r?\n/);
116
177
  const windows = [];
@@ -153,14 +214,17 @@ function findDuplicates(files, minLines, maxFindings) {
153
214
  }
154
215
  return findings;
155
216
  }
156
- function scanPath(rawPath, cfg) {
217
+ async function scanPath(rawPath, cfg) {
157
218
  const root = process.cwd();
158
219
  const resolved = isAbsolute2(rawPath) ? resolve2(rawPath) : resolve2(root, rawPath);
159
- const filePaths = collectSourceFiles(resolved, { extensions: cfg.extensions, excludeDirs: cfg.excludeDirs });
220
+ const filePaths = await collectSourceFilesAsync(resolved, {
221
+ extensions: cfg.extensions,
222
+ excludeDirs: cfg.excludeDirs
223
+ });
160
224
  const files = /* @__PURE__ */ new Map();
161
225
  for (const p of filePaths) {
162
226
  try {
163
- files.set(p, readFileSync(p, "utf-8"));
227
+ files.set(p, await readFile(p, "utf-8"));
164
228
  } catch {
165
229
  }
166
230
  }
@@ -220,6 +284,9 @@ var plugin = {
220
284
  state.errorCount = 0;
221
285
  state.lastHookWarning.clear();
222
286
  state.fileIndex.clear();
287
+ state.indexFingerprintCount = 0;
288
+ state.hookIndexEvictions = 0;
289
+ state.oversizedFileSkips = 0;
223
290
  if (state.hookUnregister) {
224
291
  try {
225
292
  state.hookUnregister();
@@ -228,77 +295,103 @@ var plugin = {
228
295
  state.hookUnregister = null;
229
296
  }
230
297
  const cfg = readConfig(api.config.extensions?.["duplicate-code-detector"]);
231
- function readCachedWindows(filePath, minLines) {
298
+ async function readCachedFingerprints(filePath, minLines) {
232
299
  let st;
233
300
  try {
234
- st = statSync2(filePath);
301
+ st = await stat(filePath);
235
302
  } catch {
236
303
  return null;
237
304
  }
238
305
  const cached = state.fileIndex.get(filePath);
239
306
  if (cached && cached.mtimeMs === st.mtimeMs && cached.size === st.size) {
240
- return cached.windows;
307
+ state.fileIndex.delete(filePath);
308
+ state.fileIndex.set(filePath, cached);
309
+ return cached.fingerprints;
310
+ }
311
+ if (st.size > hookIndexBudgets.maxFileBytes) {
312
+ if (cached) {
313
+ state.indexFingerprintCount -= cached.fingerprints.size;
314
+ state.fileIndex.delete(filePath);
315
+ }
316
+ state.oversizedFileSkips += 1;
317
+ api.log.trace("duplicate-code-detector: skipped oversized file in hook index", {
318
+ file: relativePath(filePath),
319
+ sizeBytes: st.size,
320
+ maxFileBytes: hookIndexBudgets.maxFileBytes
321
+ });
322
+ return /* @__PURE__ */ new Set();
241
323
  }
242
324
  let content;
243
325
  try {
244
- content = readFileSync(filePath, "utf-8");
326
+ content = await readFile(filePath, "utf-8");
245
327
  } catch {
246
328
  return null;
247
329
  }
248
- const windows = extractWindows(filePath, content, minLines);
249
- state.fileIndex.set(filePath, { mtimeMs: st.mtimeMs, size: st.size, windows });
250
- return windows;
330
+ const fingerprints = extractFingerprintHashes(
331
+ content,
332
+ minLines,
333
+ hookIndexBudgets.maxFingerprintsPerFile
334
+ );
335
+ if (cached) state.indexFingerprintCount -= cached.fingerprints.size;
336
+ state.fileIndex.set(filePath, { mtimeMs: st.mtimeMs, size: st.size, fingerprints });
337
+ state.indexFingerprintCount += fingerprints.size;
338
+ evictHookIndex();
339
+ return fingerprints;
251
340
  }
252
- const hook = (input) => {
341
+ const hook = async (input) => {
253
342
  if (!cfg.enabled) return;
254
343
  if (input.toolResult?.isError) return;
255
344
  const inp = input.toolInput ?? {};
256
345
  const sourcePath = inp["path"];
257
346
  if (!sourcePath || typeof sourcePath !== "string") return;
258
- if (!withinProject(sourcePath)) return;
347
+ const projectRoot = resolve2(process.cwd());
348
+ const resolvedFile = isAbsolute2(sourcePath) ? resolve2(sourcePath) : resolve2(projectRoot, sourcePath);
349
+ if (!isWithinRoot(projectRoot, resolvedFile)) return;
350
+ let changedFile;
351
+ try {
352
+ changedFile = await realpath(resolvedFile);
353
+ } catch {
354
+ state.errorCount += 1;
355
+ return;
356
+ }
357
+ if (!isWithinRoot(projectRoot, changedFile)) return;
259
358
  const ext = sourcePath.includes(".") ? sourcePath.slice(sourcePath.lastIndexOf(".")).toLowerCase() : "";
260
- if (!cfg.extensions.includes(ext)) return;
359
+ if (!extensionsSet.has(ext)) return;
261
360
  state.hookInvocationCount += 1;
262
361
  const now = Date.now();
263
- const lastWarning = state.lastHookWarning.get(sourcePath);
362
+ const lastWarning = state.lastHookWarning.get(changedFile);
264
363
  if (lastWarning !== void 0 && now - lastWarning < 6e4) return;
265
- const changedFile = resolve2(process.cwd(), sourcePath);
266
- const changedWindows = readCachedWindows(changedFile, cfg.minLines);
267
- if (changedWindows === null) {
364
+ const changedFps = await readCachedFingerprints(changedFile, cfg.minLines);
365
+ if (changedFps === null) {
268
366
  state.errorCount += 1;
269
367
  return;
270
368
  }
271
- if (changedWindows.length === 0) return;
272
- const projectRoot = resolve2(process.cwd());
369
+ if (changedFps.size === 0) return;
273
370
  let otherFilePaths;
274
371
  try {
275
- otherFilePaths = collectSourceFiles(projectRoot, {
372
+ otherFilePaths = await collectSourceFilesAsync(projectRoot, {
276
373
  extensions: cfg.extensions,
277
374
  excludeDirs: cfg.excludeDirs
278
- }).filter((p) => p !== changedFile);
375
+ });
279
376
  } catch {
280
377
  state.errorCount += 1;
281
378
  return;
282
379
  }
283
- const existingWindows = [];
380
+ const matched = /* @__PURE__ */ new Set();
284
381
  for (const p of otherFilePaths) {
285
- const w = readCachedWindows(p, cfg.minLines);
286
- if (w !== null) existingWindows.push(...w);
287
- }
288
- const hits = [];
289
- for (const cw of changedWindows) {
290
- for (const ew of existingWindows) {
291
- if (cw.fingerprint === ew.fingerprint) {
292
- hits.push(ew);
293
- break;
294
- }
382
+ if (resolve2(p) === resolve2(changedFile)) continue;
383
+ const otherFps = await readCachedFingerprints(p, cfg.minLines);
384
+ if (otherFps === null || otherFps.size === 0) continue;
385
+ for (const fp of changedFps) {
386
+ if (!matched.has(fp) && otherFps.has(fp)) matched.add(fp);
295
387
  }
388
+ if (matched.size === changedFps.size) break;
296
389
  }
297
- if (hits.length === 0) return;
298
- state.warningCount += hits.length;
299
- state.lastHookWarning.set(sourcePath, now);
390
+ if (matched.size === 0) return;
391
+ state.warningCount += matched.size;
392
+ state.lastHookWarning.set(changedFile, now);
300
393
  return {
301
- additionalContext: `\u26A0\uFE0F duplicate-code-detector: ${sourcePath} contains ${hits.length} block(s) already present elsewhere. Run detect_duplicate_code for details.`,
394
+ additionalContext: `\u26A0\uFE0F duplicate-code-detector: ${sourcePath} contains ${matched.size} block(s) already present elsewhere. Run detect_duplicate_code for details.`,
302
395
  contextAs: "separate"
303
396
  };
304
397
  };
@@ -324,7 +417,7 @@ var plugin = {
324
417
  state.scanCount += 1;
325
418
  let result;
326
419
  try {
327
- result = scanPath(rawPath, cfg);
420
+ result = await scanPath(rawPath, cfg);
328
421
  } catch (err) {
329
422
  state.errorCount += 1;
330
423
  return { ok: false, error: String(err) };
@@ -360,7 +453,15 @@ var plugin = {
360
453
  findings: state.findingCount,
361
454
  hookInvocations: state.hookInvocationCount,
362
455
  warnings: state.warningCount,
363
- errors: state.errorCount
456
+ errors: state.errorCount,
457
+ // Hook fingerprint-index footprint (bounded; see hookIndexBudgets).
458
+ indexedFiles: state.fileIndex.size,
459
+ indexedFingerprints: state.indexFingerprintCount,
460
+ hookIndexEvictions: state.hookIndexEvictions,
461
+ oversizedFileSkips: state.oversizedFileSkips,
462
+ // Rough retained-bytes estimate: ~8B per numeric fingerprint + ~120B
463
+ // per file entry (key string + Set/entry overhead). Compact by design.
464
+ approxIndexBytes: state.indexFingerprintCount * 8 + state.fileIndex.size * 120
364
465
  }
365
466
  };
366
467
  }
@@ -392,6 +493,10 @@ var plugin = {
392
493
  state.warningCount = 0;
393
494
  state.errorCount = 0;
394
495
  state.lastHookWarning.clear();
496
+ state.fileIndex.clear();
497
+ state.indexFingerprintCount = 0;
498
+ state.hookIndexEvictions = 0;
499
+ state.oversizedFileSkips = 0;
395
500
  api.log.info("duplicate-code-detector: teardown complete", { final });
396
501
  },
397
502
  async health() {
@@ -403,12 +508,18 @@ var plugin = {
403
508
  findings: state.findingCount,
404
509
  hookInvocations: state.hookInvocationCount,
405
510
  warnings: state.warningCount,
406
- errors: state.errorCount
511
+ errors: state.errorCount,
512
+ indexedFiles: state.fileIndex.size,
513
+ indexedFingerprints: state.indexFingerprintCount,
514
+ hookIndexEvictions: state.hookIndexEvictions,
515
+ oversizedFileSkips: state.oversizedFileSkips
407
516
  }
408
517
  };
409
518
  }
410
519
  };
411
520
  var duplicate_code_detector_default = plugin;
412
521
  export {
413
- duplicate_code_detector_default as default
522
+ duplicate_code_detector_default as default,
523
+ hashFingerprint,
524
+ hookIndexBudgets
414
525
  };
@@ -43,7 +43,7 @@
43
43
  *
44
44
  * @public
45
45
  */
46
- import type { Plugin } from '@wrongstack/core';
46
+ import type { Plugin } from '@wrongstack/core/types';
47
47
  export interface FailureRecord {
48
48
  when: string;
49
49
  tool: string;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/error-lens/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4CG;AACH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAM/C,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;CACjB;AA6FD,kEAAkE;AAClE,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAM9D;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,EAAE,CAuBzE;AAUD,QAAA,MAAM,MAAM,EAAE,MAoOb,CAAC;eAEa,MAAM"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/error-lens/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4CG;AACH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AAMrD,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;CACjB;AA6FD,kEAAkE;AAClE,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAM9D;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,EAAE,CAuBzE;AAUD,QAAA,MAAM,MAAM,EAAE,MAoOb,CAAC;eAEa,MAAM"}
@@ -0,0 +1,4 @@
1
+ import type { Plugin } from '@wrongstack/core/types';
2
+ export declare const OFFICIAL_PLUGIN_SPECIFIERS: readonly ['@wrongstack/plugins/agent-handoff', '@wrongstack/plugins/cost-tracker', '@wrongstack/plugins/file-watcher', '@wrongstack/plugins/git-autocommit', '@wrongstack/plugins/auto-doc', '@wrongstack/plugins/shell-check', '@wrongstack/plugins/cron', '@wrongstack/plugins/template-engine', '@wrongstack/plugins/semver-bump', '@wrongstack/plugins/secret-scanner', '@wrongstack/plugins/todo-tracker', '@wrongstack/plugins/token-budget', '@wrongstack/plugins/lint-gate', '@wrongstack/plugins/branch-guard', '@wrongstack/plugins/diff-summary', '@wrongstack/plugins/commit-validator', '@wrongstack/plugins/format-on-save', '@wrongstack/plugins/test-runner-gate', '@wrongstack/plugins/import-organizer', '@wrongstack/plugins/knowledge-graph', '@wrongstack/plugins/todo-listener', '@wrongstack/plugins/session-recap', '@wrongstack/plugins/spec-linker', '@wrongstack/plugins/loop-breaker', '@wrongstack/plugins/path-guard', '@wrongstack/plugins/process-guard', '@wrongstack/plugins/context-pins', '@wrongstack/plugins/checkpoint', '@wrongstack/plugins/error-lens', '@wrongstack/plugins/dep-guard', '@wrongstack/plugins/config-validator', '@wrongstack/plugins/notify-hub', '@wrongstack/plugins/changelog-writer', '@wrongstack/plugins/injection-shield', '@wrongstack/plugins/llm-cache', '@wrongstack/plugins/model-router', '@wrongstack/plugins/pr-drafter', '@wrongstack/plugins/prompt-firewall', '@wrongstack/plugins/auto-escalate', '@wrongstack/plugins/test-coverage-gate', '@wrongstack/plugins/type-gate', '@wrongstack/plugins/token-throttle', '@wrongstack/plugins/plugin-stack-observer', '@wrongstack/plugins/dead-code-detector', '@wrongstack/plugins/dependency-vulnerability-gate', '@wrongstack/plugins/migration-planner', '@wrongstack/plugins/semantic-search-indexer', '@wrongstack/plugins/auto-i18n-extractor', '@wrongstack/plugins/doc-sync-guard', '@wrongstack/plugins/api-compatibility-gate', '@wrongstack/plugins/performance-regression-gate', '@wrongstack/plugins/test-flake-detector', '@wrongstack/plugins/schema-evolution-guard', '@wrongstack/plugins/license-audit-gate', '@wrongstack/plugins/accessibility-auditor', '@wrongstack/plugins/security-hotspot-scanner', '@wrongstack/plugins/duplicate-code-detector', '@wrongstack/plugins/code-metrics', '@wrongstack/plugins/refactor-suggester', '@wrongstack/plugins/test-generator', '@wrongstack/plugins/release-notes-generator', '@wrongstack/plugins/smart-rename', '@wrongstack/plugins/feature-flag-tracker', '@wrongstack/plugins/interface-contract-guard'];
3
+ export declare const OFFICIAL_PLUGIN_FACTORIES: readonly (() => Promise<Plugin>)[];
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/factories/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AAErD,eAAO,MAAM,0BAA0B,YACrC,mCAAmC,EACnC,kCAAkC,EAClC,kCAAkC,EAClC,oCAAoC,EACpC,8BAA8B,EAC9B,iCAAiC,EACjC,0BAA0B,EAC1B,qCAAqC,EACrC,iCAAiC,EACjC,oCAAoC,EACpC,kCAAkC,EAClC,kCAAkC,EAClC,+BAA+B,EAC/B,kCAAkC,EAClC,kCAAkC,EAClC,sCAAsC,EACtC,oCAAoC,EACpC,sCAAsC,EACtC,sCAAsC,EACtC,qCAAqC,EACrC,mCAAmC,EACnC,mCAAmC,EACnC,iCAAiC,EACjC,kCAAkC,EAClC,gCAAgC,EAChC,mCAAmC,EACnC,kCAAkC,EAClC,gCAAgC,EAChC,gCAAgC,EAChC,+BAA+B,EAC/B,sCAAsC,EACtC,gCAAgC,EAChC,sCAAsC,EACtC,sCAAsC,EACtC,+BAA+B,EAC/B,kCAAkC,EAClC,gCAAgC,EAChC,qCAAqC,EACrC,mCAAmC,EACnC,wCAAwC,EACxC,+BAA+B,EAC/B,oCAAoC,EACpC,2CAA2C,EAC3C,wCAAwC,EACxC,mDAAmD,EACnD,uCAAuC,EACvC,6CAA6C,EAC7C,yCAAyC,EACzC,oCAAoC,EACpC,4CAA4C,EAC5C,iDAAiD,EACjD,yCAAyC,EACzC,4CAA4C,EAC5C,wCAAwC,EACxC,2CAA2C,EAC3C,8CAA8C,EAC9C,6CAA6C,EAC7C,kCAAkC,EAClC,wCAAwC,EACxC,oCAAoC,EACpC,6CAA6C,EAC7C,kCAAkC,EAClC,0CAA0C,EAC1C,8CAA8C,CACtC,CAAC;AAEX,eAAO,MAAM,yBAAyB,EAAE,SAAS,CAAC,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC,EAGrE,CAAC"}
@@ -0,0 +1 @@
1
+ export * from './factories/index.js';