@webpieces/rules-config 0.3.356 → 0.3.358

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@webpieces/rules-config",
3
- "version": "0.3.356",
3
+ "version": "0.3.358",
4
4
  "description": "Shared webpieces.config.json loader. Single source of truth for validation rule configuration consumed by @webpieces/ai-hook-rules, @webpieces/code-rules, and @webpieces/nx-webpieces-rules.",
5
5
  "type": "commonjs",
6
6
  "main": "./src/index.js",
@@ -14,8 +14,8 @@
14
14
  "README.md"
15
15
  ],
16
16
  "dependencies": {
17
- "@webpieces/core-context": "0.3.356",
18
- "@webpieces/core-util": "0.3.356",
17
+ "@webpieces/core-context": "0.3.358",
18
+ "@webpieces/core-util": "0.3.358",
19
19
  "@inversifyjs/binding-decorators": "1.1.5",
20
20
  "inversify": "7.10.4",
21
21
  "reflect-metadata": "0.2.2",
@@ -9,6 +9,16 @@ export interface RawConfigFile {
9
9
  rulesDir?: string[];
10
10
  'pr-gate'?: unknown;
11
11
  }
12
+ /**
13
+ * Locates + reads webpieces.config.json. `@provideSingleton` so it can be injected into the config
14
+ * loader and appear in the rules-config DI design.
15
+ */
16
+ export declare class ConfigFile {
17
+ /** Walk up from `startDir` looking for webpieces.config.json. Returns its absolute path or null. */
18
+ findConfigFile(startDir: string): string | null;
19
+ /** Read + JSON.parse webpieces.config.json, surfacing parse failures as a readable InformAiError. */
20
+ readRawConfig(configPath: string): RawConfigFile;
21
+ }
12
22
  /**
13
23
  * Walk up from `startDir` looking for webpieces.config.json. Returns its absolute path or null.
14
24
  */
@@ -1,43 +1,67 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.CONFIG_FILENAME = void 0;
3
+ exports.ConfigFile = exports.CONFIG_FILENAME = void 0;
4
4
  exports.findConfigFile = findConfigFile;
5
5
  exports.readRawConfig = readRawConfig;
6
6
  const tslib_1 = require("tslib");
7
7
  const fs = tslib_1.__importStar(require("fs"));
8
8
  const path = tslib_1.__importStar(require("path"));
9
+ const core_context_1 = require("@webpieces/core-context");
10
+ const inversify_1 = require("inversify");
9
11
  const inform_ai_error_1 = require("./inform-ai-error");
10
12
  const to_error_1 = require("./to-error");
11
13
  exports.CONFIG_FILENAME = 'webpieces.config.json';
14
+ /**
15
+ * Locates + reads webpieces.config.json. `@provideSingleton` so it can be injected into the config
16
+ * loader and appear in the rules-config DI design.
17
+ */
18
+ let ConfigFile = class ConfigFile {
19
+ /** Walk up from `startDir` looking for webpieces.config.json. Returns its absolute path or null. */
20
+ findConfigFile(startDir) {
21
+ let dir = startDir;
22
+ while (true) {
23
+ const primary = path.join(dir, exports.CONFIG_FILENAME);
24
+ if (fs.existsSync(primary))
25
+ return primary;
26
+ const parent = path.dirname(dir);
27
+ if (parent === dir)
28
+ return null;
29
+ dir = parent;
30
+ }
31
+ }
32
+ /** Read + JSON.parse webpieces.config.json, surfacing parse failures as a readable InformAiError. */
33
+ readRawConfig(configPath) {
34
+ const raw = fs.readFileSync(configPath, 'utf8');
35
+ // eslint-disable-next-line @webpieces/no-unmanaged-exceptions
36
+ try {
37
+ return JSON.parse(raw);
38
+ }
39
+ catch (err) {
40
+ const error = (0, to_error_1.toError)(err);
41
+ throw new inform_ai_error_1.InformAiError(`webpieces.config.json has invalid JSON — fix the file, then retry.\n` +
42
+ `Parse error: ${error.message}\n` +
43
+ `File: ${configPath}`);
44
+ }
45
+ }
46
+ };
47
+ exports.ConfigFile = ConfigFile;
48
+ exports.ConfigFile = ConfigFile = tslib_1.__decorate([
49
+ (0, core_context_1.provideSingleton)(),
50
+ (0, inversify_1.injectable)()
51
+ ], ConfigFile);
52
+ // Temporary migration delegators — consumers migrate to injecting ConfigFile over follow-up PRs, then
53
+ // these free functions are removed. Declarations kept identical to the originals (unchanged lines).
54
+ const configFileSvc = new ConfigFile();
12
55
  /**
13
56
  * Walk up from `startDir` looking for webpieces.config.json. Returns its absolute path or null.
14
57
  */
15
58
  function findConfigFile(startDir) {
16
- let dir = startDir;
17
- while (true) {
18
- const primary = path.join(dir, exports.CONFIG_FILENAME);
19
- if (fs.existsSync(primary))
20
- return primary;
21
- const parent = path.dirname(dir);
22
- if (parent === dir)
23
- return null;
24
- dir = parent;
25
- }
59
+ return configFileSvc.findConfigFile(startDir);
26
60
  }
27
61
  /**
28
62
  * Read + JSON.parse webpieces.config.json, surfacing parse failures as a readable InformAiError.
29
63
  */
30
64
  function readRawConfig(configPath) {
31
- const raw = fs.readFileSync(configPath, 'utf8');
32
- // eslint-disable-next-line @webpieces/no-unmanaged-exceptions
33
- try {
34
- return JSON.parse(raw);
35
- }
36
- catch (err) {
37
- const error = (0, to_error_1.toError)(err);
38
- throw new inform_ai_error_1.InformAiError(`webpieces.config.json has invalid JSON — fix the file, then retry.\n` +
39
- `Parse error: ${error.message}\n` +
40
- `File: ${configPath}`);
41
- }
65
+ return configFileSvc.readRawConfig(configPath);
42
66
  }
43
67
  //# sourceMappingURL=config-file.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"config-file.js","sourceRoot":"","sources":["../../../../../packages/tooling/rules-config/src/config-file.ts"],"names":[],"mappings":";;;AAuCA,wCASC;AAKD,sCAaC;;AAlED,+CAAyB;AACzB,mDAA6B;AAE7B,uDAAkD;AAClD,yCAAqC;AAExB,QAAA,eAAe,GAAG,uBAAuB,CAAC;AA8BvD;;GAEG;AACH,SAAgB,cAAc,CAAC,QAAgB;IAC3C,IAAI,GAAG,GAAG,QAAQ,CAAC;IACnB,OAAO,IAAI,EAAE,CAAC;QACV,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,uBAAe,CAAC,CAAC;QAChD,IAAI,EAAE,CAAC,UAAU,CAAC,OAAO,CAAC;YAAE,OAAO,OAAO,CAAC;QAC3C,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QACjC,IAAI,MAAM,KAAK,GAAG;YAAE,OAAO,IAAI,CAAC;QAChC,GAAG,GAAG,MAAM,CAAC;IACjB,CAAC;AACL,CAAC;AAED;;GAEG;AACH,SAAgB,aAAa,CAAC,UAAkB;IAC5C,MAAM,GAAG,GAAG,EAAE,CAAC,YAAY,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;IAChD,8DAA8D;IAC9D,IAAI,CAAC;QACD,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAkB,CAAC;IAC5C,CAAC;IAAC,OAAO,GAAY,EAAE,CAAC;QACpB,MAAM,KAAK,GAAG,IAAA,kBAAO,EAAC,GAAG,CAAC,CAAC;QAC3B,MAAM,IAAI,+BAAa,CACnB,sEAAsE;YACtE,gBAAgB,KAAK,CAAC,OAAO,IAAI;YACjC,SAAS,UAAU,EAAE,CACxB,CAAC;IACN,CAAC;AACL,CAAC","sourcesContent":["import * as fs from 'fs';\nimport * as path from 'path';\n\nimport { InformAiError } from './inform-ai-error';\nimport { toError } from './to-error';\n\nexport const CONFIG_FILENAME = 'webpieces.config.json';\n\n// Raw shape of webpieces.config.json as parsed from JSON, before validation/typing.\n// - `rules` — code-style validators (scope edit/file).\n// - `hookGuards` — git/PR/branch protection guards (scope bash).\n// - `commands` — gated command config the guards point at; `pr-gate` lives inside it. Carried as\n// opaque JSON because its nested `gates` array can't be expressed in the FieldDef\n// schema; validated structurally by validateCommandsSection.\n// - `pr-gate` — DEPRECATED top-level block (pre-migration layout). Read only as a back-compat\n// fallback / to emit a \"move it under commands\" migration error.\n// webpieces-disable no-any-unknown -- consumer JSON config has opaque rule option values\nexport interface RawConfigFile {\n extends?: string;\n rules?: Record<string, Record<string, unknown>>;\n hookGuards?: Record<string, Record<string, unknown>>;\n // webpieces-disable no-any-unknown -- opaque commands JSON, validated by validateCommandsSection\n commands?: unknown;\n // REQUIRED top-level block: two glob lists that suppress hook enforcement per file path.\n // Opaque here (validated structurally by validateExcludePaths, then parsed into ExcludePaths).\n // webpieces-disable no-any-unknown -- opaque excludePaths JSON, validated by validateExcludePaths\n excludePaths?: unknown;\n // REQUIRED top-level array of client-authored content guards (regex patterns + message + scoping).\n // Opaque here; validated structurally by validateMatchRulesSection, then parsed into MatchRuleConfig[].\n // webpieces-disable no-any-unknown -- opaque match-rules JSON, validated by validateMatchRulesSection\n 'match-rules'?: unknown;\n rulesDir?: string[];\n // webpieces-disable no-any-unknown -- DEPRECATED top-level pr-gate, migrated under `commands`\n 'pr-gate'?: unknown;\n}\n\n/**\n * Walk up from `startDir` looking for webpieces.config.json. Returns its absolute path or null.\n */\nexport function findConfigFile(startDir: string): string | null {\n let dir = startDir;\n while (true) {\n const primary = path.join(dir, CONFIG_FILENAME);\n if (fs.existsSync(primary)) return primary;\n const parent = path.dirname(dir);\n if (parent === dir) return null;\n dir = parent;\n }\n}\n\n/**\n * Read + JSON.parse webpieces.config.json, surfacing parse failures as a readable InformAiError.\n */\nexport function readRawConfig(configPath: string): RawConfigFile {\n const raw = fs.readFileSync(configPath, 'utf8');\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions\n try {\n return JSON.parse(raw) as RawConfigFile;\n } catch (err: unknown) {\n const error = toError(err);\n throw new InformAiError(\n `webpieces.config.json has invalid JSON — fix the file, then retry.\\n` +\n `Parse error: ${error.message}\\n` +\n `File: ${configPath}`,\n );\n }\n}\n"]}
1
+ {"version":3,"file":"config-file.js","sourceRoot":"","sources":["../../../../../packages/tooling/rules-config/src/config-file.ts"],"names":[],"mappings":";;;AAiFA,wCAEC;AAKD,sCAEC;;AA1FD,+CAAyB;AACzB,mDAA6B;AAC7B,0DAA2D;AAC3D,yCAAuC;AAEvC,uDAAkD;AAClD,yCAAqC;AAExB,QAAA,eAAe,GAAG,uBAAuB,CAAC;AA8BvD;;;GAGG;AAGI,IAAM,UAAU,GAAhB,MAAM,UAAU;IACnB,oGAAoG;IACpG,cAAc,CAAC,QAAgB;QAC3B,IAAI,GAAG,GAAG,QAAQ,CAAC;QACnB,OAAO,IAAI,EAAE,CAAC;YACV,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,uBAAe,CAAC,CAAC;YAChD,IAAI,EAAE,CAAC,UAAU,CAAC,OAAO,CAAC;gBAAE,OAAO,OAAO,CAAC;YAC3C,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;YACjC,IAAI,MAAM,KAAK,GAAG;gBAAE,OAAO,IAAI,CAAC;YAChC,GAAG,GAAG,MAAM,CAAC;QACjB,CAAC;IACL,CAAC;IAED,qGAAqG;IACrG,aAAa,CAAC,UAAkB;QAC5B,MAAM,GAAG,GAAG,EAAE,CAAC,YAAY,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;QAChD,8DAA8D;QAC9D,IAAI,CAAC;YACD,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAkB,CAAC;QAC5C,CAAC;QAAC,OAAO,GAAY,EAAE,CAAC;YACpB,MAAM,KAAK,GAAG,IAAA,kBAAO,EAAC,GAAG,CAAC,CAAC;YAC3B,MAAM,IAAI,+BAAa,CACnB,sEAAsE;gBACtE,gBAAgB,KAAK,CAAC,OAAO,IAAI;gBACjC,SAAS,UAAU,EAAE,CACxB,CAAC;QACN,CAAC;IACL,CAAC;CACJ,CAAA;AA5BY,gCAAU;qBAAV,UAAU;IAFtB,IAAA,+BAAgB,GAAE;IAClB,IAAA,sBAAU,GAAE;GACA,UAAU,CA4BtB;AAED,sGAAsG;AACtG,oGAAoG;AACpG,MAAM,aAAa,GAAG,IAAI,UAAU,EAAE,CAAC;AAEvC;;GAEG;AACH,SAAgB,cAAc,CAAC,QAAgB;IAC3C,OAAO,aAAa,CAAC,cAAc,CAAC,QAAQ,CAAC,CAAC;AAClD,CAAC;AAED;;GAEG;AACH,SAAgB,aAAa,CAAC,UAAkB;IAC5C,OAAO,aAAa,CAAC,aAAa,CAAC,UAAU,CAAC,CAAC;AACnD,CAAC","sourcesContent":["import * as fs from 'fs';\nimport * as path from 'path';\nimport { provideSingleton } from '@webpieces/core-context';\nimport { injectable } from 'inversify';\n\nimport { InformAiError } from './inform-ai-error';\nimport { toError } from './to-error';\n\nexport const CONFIG_FILENAME = 'webpieces.config.json';\n\n// Raw shape of webpieces.config.json as parsed from JSON, before validation/typing.\n// - `rules` — code-style validators (scope edit/file).\n// - `hookGuards` — git/PR/branch protection guards (scope bash).\n// - `commands` — gated command config the guards point at; `pr-gate` lives inside it. Carried as\n// opaque JSON because its nested `gates` array can't be expressed in the FieldDef\n// schema; validated structurally by validateCommandsSection.\n// - `pr-gate` — DEPRECATED top-level block (pre-migration layout). Read only as a back-compat\n// fallback / to emit a \"move it under commands\" migration error.\n// webpieces-disable no-any-unknown -- consumer JSON config has opaque rule option values\nexport interface RawConfigFile {\n extends?: string;\n rules?: Record<string, Record<string, unknown>>;\n hookGuards?: Record<string, Record<string, unknown>>;\n // webpieces-disable no-any-unknown -- opaque commands JSON, validated by validateCommandsSection\n commands?: unknown;\n // REQUIRED top-level block: two glob lists that suppress hook enforcement per file path.\n // Opaque here (validated structurally by validateExcludePaths, then parsed into ExcludePaths).\n // webpieces-disable no-any-unknown -- opaque excludePaths JSON, validated by validateExcludePaths\n excludePaths?: unknown;\n // REQUIRED top-level array of client-authored content guards (regex patterns + message + scoping).\n // Opaque here; validated structurally by validateMatchRulesSection, then parsed into MatchRuleConfig[].\n // webpieces-disable no-any-unknown -- opaque match-rules JSON, validated by validateMatchRulesSection\n 'match-rules'?: unknown;\n rulesDir?: string[];\n // webpieces-disable no-any-unknown -- DEPRECATED top-level pr-gate, migrated under `commands`\n 'pr-gate'?: unknown;\n}\n\n/**\n * Locates + reads webpieces.config.json. `@provideSingleton` so it can be injected into the config\n * loader and appear in the rules-config DI design.\n */\n@provideSingleton()\n@injectable()\nexport class ConfigFile {\n /** Walk up from `startDir` looking for webpieces.config.json. Returns its absolute path or null. */\n findConfigFile(startDir: string): string | null {\n let dir = startDir;\n while (true) {\n const primary = path.join(dir, CONFIG_FILENAME);\n if (fs.existsSync(primary)) return primary;\n const parent = path.dirname(dir);\n if (parent === dir) return null;\n dir = parent;\n }\n }\n\n /** Read + JSON.parse webpieces.config.json, surfacing parse failures as a readable InformAiError. */\n readRawConfig(configPath: string): RawConfigFile {\n const raw = fs.readFileSync(configPath, 'utf8');\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions\n try {\n return JSON.parse(raw) as RawConfigFile;\n } catch (err: unknown) {\n const error = toError(err);\n throw new InformAiError(\n `webpieces.config.json has invalid JSON — fix the file, then retry.\\n` +\n `Parse error: ${error.message}\\n` +\n `File: ${configPath}`,\n );\n }\n }\n}\n\n// Temporary migration delegators — consumers migrate to injecting ConfigFile over follow-up PRs, then\n// these free functions are removed. Declarations kept identical to the originals (unchanged lines).\nconst configFileSvc = new ConfigFile();\n\n/**\n * Walk up from `startDir` looking for webpieces.config.json. Returns its absolute path or null.\n */\nexport function findConfigFile(startDir: string): string | null {\n return configFileSvc.findConfigFile(startDir);\n}\n\n/**\n * Read + JSON.parse webpieces.config.json, surfacing parse failures as a readable InformAiError.\n */\nexport function readRawConfig(configPath: string): RawConfigFile {\n return configFileSvc.readRawConfig(configPath);\n}\n"]}
@@ -1,13 +1,10 @@
1
1
  /**
2
- * Shared git-diff + diff-scoping helpers for ALL rule validators (code-rules) and nx executors
3
- * (nx-webpieces-rules). Centralized here in rules-config because it is the one package both of
4
- * those depend on, and it already shells out to git (see skip-rule.ts).
2
+ * Shared git-diff + diff-scoping service for ALL rule validators (code-rules) and nx executors
3
+ * (nx-webpieces-rules). Centralized here in rules-config because it is the one package both depend on.
5
4
  *
6
- * Before this module these functions were copy-pasted ~15× (one private copy per validator + the
7
- * validate-ts-in-src executor). They are now defined once; every consumer imports from
8
- * `@webpieces/rules-config`. The two historical variants are preserved as parameters:
9
- * - `getChangedFiles(..., { tsOnly: false })` reproduces validate-dtos' all-files diff.
10
- * - `detectBase` replaces both the nested-try and the ref-loop spellings (identical behavior).
5
+ * `@provideSingleton` so it can be injected and appear in the rules-config DI design. Free-function
6
+ * delegators are kept temporarily so the many existing consumers stay green; they migrate to injecting
7
+ * {@link DiffScope} over follow-up PRs, then the delegators are removed.
11
8
  */
12
9
  /** A git diff range: the base ref to compare against and an optional head (else the working tree). */
13
10
  export declare class DiffRange {
@@ -18,48 +15,34 @@ export declare class DiffRange {
18
15
  export declare class ChangedFilesOptions {
19
16
  tsOnly?: boolean;
20
17
  }
21
- /**
22
- * Auto-detect the diff base: the merge-base of HEAD with origin/main, falling back to local main.
23
- * Returns null when neither ref resolves (e.g. shallow clone with no main) callers treat null as
24
- * "no base detected" and typically skip.
25
- */
18
+ export declare class DiffScope {
19
+ /** Auto-detect the diff base: merge-base of HEAD with origin/main, falling back to local main. */
20
+ detectBase(workspaceRoot: string): string | null;
21
+ /** Resolve the diff range a rule should compare against (honors nx's NX_BASE / NX_HEAD). */
22
+ resolveBase(workspaceRoot: string): DiffRange;
23
+ /**
24
+ * Changed files between base and head (or base→working-tree when head is omitted). Untracked files
25
+ * are unioned in for the working-tree case. `tsOnly` (default true) restricts to *.ts/*.tsx and
26
+ * drops test files. Deletions are excluded (`--diff-filter=d`).
27
+ */
28
+ getChangedFiles(workspaceRoot: string, base: string, head?: string, opts?: ChangedFilesOptions): string[];
29
+ /** Diff content for a single file (synthetic all-added diff for an untracked file with no head). */
30
+ getFileDiff(workspaceRoot: string, file: string, base: string, head?: string): string;
31
+ /** Added/changed line numbers (the `+` lines per hunk) — basis of NEW_AND_MODIFIED_CODE scoping. */
32
+ getChangedLineNumbers(diffContent: string): Set<number>;
33
+ /** Method names whose signature line is a `+` addition in the diff — the basis of "NEW" methods. */
34
+ findNewMethodSignaturesInDiff(diffContent: string): Set<string>;
35
+ /** True if any line in [startLine, endLine] is in the changedLines set. */
36
+ hasChangesInRange(startLine: number, endLine: number, changedLines: Set<number>): boolean;
37
+ /** True if a node (method/function) is newly added or has any changed line in its range. */
38
+ isNewOrModified(name: string, startLine: number, endLine: number, changedLines: Set<number>, newMethodNames: Set<string>): boolean;
39
+ private isTestFile;
40
+ }
26
41
  export declare function detectBase(workspaceRoot: string): string | null;
27
- /**
28
- * Resolve the diff range a rule should compare against. Honors nx's NX_BASE / NX_HEAD (set by
29
- * `nx affected --base=.. --head=..`); when NX_BASE is unset, auto-detects via detectBase. A returned
30
- * `base` of undefined means "could not determine a base" (caller should skip).
31
- */
32
42
  export declare function resolveBase(workspaceRoot: string): DiffRange;
33
- /**
34
- * Changed files between base and head (or base→working-tree when head is omitted). When head is
35
- * omitted, untracked files are unioned in too (matching `nx affected`). `tsOnly` (default true)
36
- * restricts to *.ts/*.tsx and drops test files; pass false for an all-files diff.
37
- *
38
- * Deletions are excluded (`--diff-filter=d`): every consumer reasons about a file's current
39
- * content or location, and a path that no longer exists can't violate anything. This also fixes
40
- * renames — without rename detection git reports a rename as delete+add, so filtering the delete
41
- * side leaves only the file's NEW path in the list.
42
- */
43
43
  export declare function getChangedFiles(workspaceRoot: string, base: string, head?: string, opts?: ChangedFilesOptions): string[];
44
- /**
45
- * Diff content for a single file. When the file is untracked (and no head is given) a synthetic
46
- * all-added diff is produced so new files count as fully-changed.
47
- */
48
44
  export declare function getFileDiff(workspaceRoot: string, file: string, base: string, head?: string): string;
49
- /**
50
- * Parse a unified diff and return the set of added/changed line numbers (the `+` lines per hunk).
51
- * This is the basis of NEW_AND_MODIFIED_CODE (line-level) scoping.
52
- */
53
45
  export declare function getChangedLineNumbers(diffContent: string): Set<number>;
54
- /**
55
- * Method names whose signature line is a `+` addition in the diff — the basis of "NEW" methods.
56
- */
57
46
  export declare function findNewMethodSignaturesInDiff(diffContent: string): Set<string>;
58
- /**
59
- * True if any line in [startLine, endLine] is in the changedLines set.
60
- */
61
47
  export declare function hasChangesInRange(startLine: number, endLine: number, changedLines: Set<number>): boolean;
62
- /**
63
- * True if a node (method/function) is newly added or has any changed line in its range.
64
- */
65
48
  export declare function isNewOrModified(name: string, startLine: number, endLine: number, changedLines: Set<number>, newMethodNames: Set<string>): boolean;