@webpieces/rules-config 0.3.360 → 0.3.361
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 +1 -3
- package/src/branch-mutation-log.js +2 -2
- package/src/branch-mutation-log.js.map +1 -1
- package/src/config-file.js +2 -2
- package/src/config-file.js.map +1 -1
- package/src/di.d.ts +30 -0
- package/src/di.js +53 -0
- package/src/di.js.map +1 -0
- package/src/diff-scope.js +2 -2
- package/src/diff-scope.js.map +1 -1
- package/src/load-config.js +2 -2
- package/src/load-config.js.map +1 -1
- package/src/load-template.js +2 -2
- package/src/load-template.js.map +1 -1
- package/src/main-sync-status.js +2 -2
- package/src/main-sync-status.js.map +1 -1
- package/src/repo-root.js +2 -2
- package/src/repo-root.js.map +1 -1
- package/src/review-json.js +2 -2
- package/src/review-json.js.map +1 -1
- package/src/rules-config-design.js +4 -4
- package/src/rules-config-design.js.map +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@webpieces/rules-config",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.361",
|
|
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,6 @@
|
|
|
14
14
|
"README.md"
|
|
15
15
|
],
|
|
16
16
|
"dependencies": {
|
|
17
|
-
"@webpieces/core-context": "0.3.360",
|
|
18
|
-
"@webpieces/core-util": "0.3.360",
|
|
19
17
|
"@inversifyjs/binding-decorators": "1.1.5",
|
|
20
18
|
"inversify": "7.10.4",
|
|
21
19
|
"reflect-metadata": "0.2.2",
|
|
@@ -6,7 +6,7 @@ exports.logBranchMutation = logBranchMutation;
|
|
|
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
|
|
9
|
+
const di_1 = require("./di");
|
|
10
10
|
const inversify_1 = require("inversify");
|
|
11
11
|
const constants_1 = require("./constants");
|
|
12
12
|
const to_error_1 = require("./to-error");
|
|
@@ -108,7 +108,7 @@ let BranchMutationLog = class BranchMutationLog {
|
|
|
108
108
|
};
|
|
109
109
|
exports.BranchMutationLog = BranchMutationLog;
|
|
110
110
|
exports.BranchMutationLog = BranchMutationLog = tslib_1.__decorate([
|
|
111
|
-
(0,
|
|
111
|
+
(0, di_1.provideSingleton)(),
|
|
112
112
|
(0, inversify_1.injectable)()
|
|
113
113
|
], BranchMutationLog);
|
|
114
114
|
// Temporary migration delegators to BranchMutationLog — removed once consumers inject it.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"branch-mutation-log.js","sourceRoot":"","sources":["../../../../../packages/tooling/rules-config/src/branch-mutation-log.ts"],"names":[],"mappings":";;;AAwHA,sDAEC;AAGD,8CAEC;;AA/HD,+CAAyB;AACzB,mDAA6B;AAC7B,
|
|
1
|
+
{"version":3,"file":"branch-mutation-log.js","sourceRoot":"","sources":["../../../../../packages/tooling/rules-config/src/branch-mutation-log.ts"],"names":[],"mappings":"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","sourcesContent":["import * as fs from 'fs';\nimport * as path from 'path';\nimport { provideSingleton } from './di';\nimport { injectable } from 'inversify';\n\nimport { WEBPIECES_TMP_DIR } from './constants';\nimport { toError } from './to-error';\n\n// The BRANCH-MUTATION log — an audit trail for every workflow verb that RENAMES or MOVES branches.\n// Records START / each phase boundary / END-with-outcome so the next agent (or a human) can\n// reconstruct what the tooling did to the branches. Writes to `.webpieces/hooks/branch-mutations.log`.\n// Lives in rules-config (the shared dep of pr-gate) so the pr-gate scripts can call it directly.\n\nconst HOOKS_DIR = 'hooks';\nconst LOG_FILE = 'branch-mutations.log';\nconst LOG_FILE_PREV = 'branch-mutations.1.log';\nconst MAX_LOG_BYTES = 512 * 1024; // 512 KB — rotate when exceeded (mirrors the other webpieces logs)\nconst MAX_DETAIL_LEN = 400;\n\n// The workflow verb whose branch mutation is being logged (the bin the AI/human invoked).\nexport type MutationVerb = 'wp-start-update' | 'wp-finish-update' | 'wp-start-upsert-pr' | 'wp-finish-upsert-pr';\n\n// A boundary within a verb's execution. START/END bracket the whole run; the middle phases mark each\n// irreversible git step so an interrupt leaves a breadcrumb at the last phase reached.\nexport type MutationPhase =\n | 'START' | 'BACKUP' | 'CHECKOUT_MAIN' | 'PULL' | 'SQUASH' | 'RENAME'\n | 'FINALIZE' | 'CONFLICT' | 'INTERRUPTED' | 'END';\n\n// Data-only record of one branch-mutation event (per CLAUDE.md: classes for data, explicit construction).\nexport class BranchMutationEvent {\n verb: MutationVerb;\n phase: MutationPhase;\n fromBranch: string = '';\n toBranch: string = '';\n oldMain: string = '';\n newMain: string = '';\n conflict: boolean = false;\n conflictFiles: string[] = [];\n outcome: string = '';\n artifacts: string[] = [];\n\n constructor(verb: MutationVerb, phase: MutationPhase) {\n this.verb = verb;\n this.phase = phase;\n }\n}\n\n/** Appends branch-mutation audit lines. `@provideSingleton` so it's injectable + drawn in the design. */\n@provideSingleton()\n@injectable()\nexport class BranchMutationLog {\n branchMutationLogPath(root: string): string {\n return path.join(root, WEBPIECES_TMP_DIR, HOOKS_DIR, LOG_FILE);\n }\n\n /**\n * Append one tab-separated line per branch-mutation event to\n * `.webpieces/hooks/branch-mutations.log`. Swallows all errors — logging must NEVER block or fail\n * the workflow it is observing.\n */\n logBranchMutation(root: string, event: BranchMutationEvent): void {\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions\n try {\n const timestamp = new Date().toISOString();\n const hooksDir = path.join(root, WEBPIECES_TMP_DIR, HOOKS_DIR);\n fs.mkdirSync(hooksDir, { recursive: true });\n\n const logPath = path.join(hooksDir, LOG_FILE);\n this.rotateLogFile(logPath, path.join(hooksDir, LOG_FILE_PREV));\n\n const line = [\n `[${timestamp}]`,\n event.verb,\n event.phase,\n this.oneLine(this.formatDetail(event)),\n ].join('\\t') + '\\n';\n fs.appendFileSync(logPath, line);\n } catch (err: unknown) {\n const error = toError(err);\n void error;\n }\n }\n\n // Render only the fields this event actually set, as `key=value` tokens — greppable on one line.\n private formatDetail(event: BranchMutationEvent): string {\n const parts: string[] = [];\n if (event.fromBranch !== '' || event.toBranch !== '') parts.push(`from=${event.fromBranch || '?'} to=${event.toBranch || '?'}`);\n if (event.oldMain !== '' || event.newMain !== '') parts.push(`oldMain=${event.oldMain || '?'} newMain=${event.newMain || '?'}`);\n if (event.conflict) parts.push('conflict=true');\n if (event.conflictFiles.length > 0) parts.push(`conflictFiles=${event.conflictFiles.length}(${event.conflictFiles.join(',')})`);\n if (event.outcome !== '') parts.push(`outcome=${event.outcome}`);\n for (const artifact of event.artifacts) parts.push(`artifact=${artifact}`);\n return parts.join(' ');\n }\n\n // Collapse newlines/tabs and cap length so one event is always exactly one log line.\n private oneLine(value: string): string {\n const flat = value.replace(/[\\t\\r\\n]+/g, ' ').trim();\n return flat.length <= MAX_DETAIL_LEN ? flat : flat.slice(0, MAX_DETAIL_LEN) + '…';\n }\n\n private rotateLogFile(logPath: string, prevPath: string): void {\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions\n try {\n const stat = fs.statSync(logPath);\n if (stat.size > MAX_LOG_BYTES) {\n if (fs.existsSync(prevPath)) fs.unlinkSync(prevPath);\n fs.renameSync(logPath, prevPath);\n }\n } catch (err: unknown) {\n const error = toError(err);\n void error;\n }\n }\n}\n\n// Temporary migration delegators to BranchMutationLog — removed once consumers inject it.\nconst branchMutationLogSvc = new BranchMutationLog();\n\n// webpieces-disable no-function-outside-class -- temporary back-compat delegator to BranchMutationLog; removed once consumers inject it\nexport function branchMutationLogPath(root: string): string {\n return branchMutationLogSvc.branchMutationLogPath(root);\n}\n\n// webpieces-disable no-function-outside-class -- temporary back-compat delegator to BranchMutationLog; removed once consumers inject it\nexport function logBranchMutation(root: string, event: BranchMutationEvent): void {\n branchMutationLogSvc.logBranchMutation(root, event);\n}\n"]}
|
package/src/config-file.js
CHANGED
|
@@ -6,7 +6,7 @@ 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
|
|
9
|
+
const di_1 = require("./di");
|
|
10
10
|
const inversify_1 = require("inversify");
|
|
11
11
|
const inform_ai_error_1 = require("./inform-ai-error");
|
|
12
12
|
const to_error_1 = require("./to-error");
|
|
@@ -46,7 +46,7 @@ let ConfigFile = class ConfigFile {
|
|
|
46
46
|
};
|
|
47
47
|
exports.ConfigFile = ConfigFile;
|
|
48
48
|
exports.ConfigFile = ConfigFile = tslib_1.__decorate([
|
|
49
|
-
(0,
|
|
49
|
+
(0, di_1.provideSingleton)(),
|
|
50
50
|
(0, inversify_1.injectable)()
|
|
51
51
|
], ConfigFile);
|
|
52
52
|
// Temporary migration delegators — consumers migrate to injecting ConfigFile over follow-up PRs, then
|
package/src/config-file.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
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,
|
|
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,6BAAwC;AACxC,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,qBAAgB,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 './di';\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"]}
|
package/src/di.d.ts
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import 'reflect-metadata';
|
|
2
|
+
/**
|
|
3
|
+
* LOCAL copies of the `@provideSingleton` and `@DocumentDesign` DI decorators.
|
|
4
|
+
*
|
|
5
|
+
* WHY here and not imported from `@webpieces/core-context` / `@webpieces/core-util`:
|
|
6
|
+
* `@webpieces/rules-config` is the FOUNDATION package — nearly everything in the monorepo (including
|
|
7
|
+
* browser + platform packages) depends on it — so it must NEVER depend UP on the platform packages.
|
|
8
|
+
* `core-util` is browser-based and `core-context` is the server-side DI seam; both are the correct
|
|
9
|
+
* home for these decorators for the APPS, but rules-config depending on them would invert the layering.
|
|
10
|
+
*
|
|
11
|
+
* These are byte-for-byte the same decorators (identical behavior + the same
|
|
12
|
+
* `'webpieces:document-design'` metadata key and the same `@inversifyjs/binding-decorators` provider
|
|
13
|
+
* registry), so the DI-graph analyzer — which matches `@DocumentDesign` / `@provideSingleton` by
|
|
14
|
+
* decorator NAME, not import source — recognizes them exactly the same, and a consumer's
|
|
15
|
+
* `buildProviderModule()` still auto-includes rules-config's `@provideSingleton` classes.
|
|
16
|
+
*/
|
|
17
|
+
export declare const DESIGN_METADATA_KEYS: {
|
|
18
|
+
DOCUMENT_DESIGN: string;
|
|
19
|
+
};
|
|
20
|
+
/**
|
|
21
|
+
* Provides a singleton-scoped dependency. When called without arguments, the class binds to itself.
|
|
22
|
+
*/
|
|
23
|
+
export declare function provideSingleton(): ClassDecorator;
|
|
24
|
+
/**
|
|
25
|
+
* @DocumentDesign decorator — marks a class as a DI-design ROOT (the top-of-DAG class the design-doc
|
|
26
|
+
* generator walks). A pure marker read STATICALLY by the DI-graph analyzer by decorator name.
|
|
27
|
+
*/
|
|
28
|
+
export declare function DocumentDesign(): ClassDecorator;
|
|
29
|
+
/** True when a class is marked `@DocumentDesign`. */
|
|
30
|
+
export declare function isDocumentDesign(designClass: object): boolean;
|
package/src/di.js
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DESIGN_METADATA_KEYS = void 0;
|
|
4
|
+
exports.provideSingleton = provideSingleton;
|
|
5
|
+
exports.DocumentDesign = DocumentDesign;
|
|
6
|
+
exports.isDocumentDesign = isDocumentDesign;
|
|
7
|
+
require("reflect-metadata");
|
|
8
|
+
const binding_decorators_1 = require("@inversifyjs/binding-decorators");
|
|
9
|
+
/**
|
|
10
|
+
* LOCAL copies of the `@provideSingleton` and `@DocumentDesign` DI decorators.
|
|
11
|
+
*
|
|
12
|
+
* WHY here and not imported from `@webpieces/core-context` / `@webpieces/core-util`:
|
|
13
|
+
* `@webpieces/rules-config` is the FOUNDATION package — nearly everything in the monorepo (including
|
|
14
|
+
* browser + platform packages) depends on it — so it must NEVER depend UP on the platform packages.
|
|
15
|
+
* `core-util` is browser-based and `core-context` is the server-side DI seam; both are the correct
|
|
16
|
+
* home for these decorators for the APPS, but rules-config depending on them would invert the layering.
|
|
17
|
+
*
|
|
18
|
+
* These are byte-for-byte the same decorators (identical behavior + the same
|
|
19
|
+
* `'webpieces:document-design'` metadata key and the same `@inversifyjs/binding-decorators` provider
|
|
20
|
+
* registry), so the DI-graph analyzer — which matches `@DocumentDesign` / `@provideSingleton` by
|
|
21
|
+
* decorator NAME, not import source — recognizes them exactly the same, and a consumer's
|
|
22
|
+
* `buildProviderModule()` still auto-includes rules-config's `@provideSingleton` classes.
|
|
23
|
+
*/
|
|
24
|
+
// Metadata key for the @DocumentDesign marker (must match core-util's so the analyzer + any reader agree).
|
|
25
|
+
exports.DESIGN_METADATA_KEYS = {
|
|
26
|
+
DOCUMENT_DESIGN: 'webpieces:document-design',
|
|
27
|
+
};
|
|
28
|
+
/**
|
|
29
|
+
* Provides a singleton-scoped dependency. When called without arguments, the class binds to itself.
|
|
30
|
+
*/
|
|
31
|
+
// webpieces-disable no-function-outside-class -- a decorator factory cannot be a class method
|
|
32
|
+
function provideSingleton() {
|
|
33
|
+
// webpieces-disable no-any-unknown -- decorator target is any class constructor
|
|
34
|
+
return (target) => {
|
|
35
|
+
return (0, binding_decorators_1.provide)(target, (bind) => bind.inSingletonScope())(target);
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* @DocumentDesign decorator — marks a class as a DI-design ROOT (the top-of-DAG class the design-doc
|
|
40
|
+
* generator walks). A pure marker read STATICALLY by the DI-graph analyzer by decorator name.
|
|
41
|
+
*/
|
|
42
|
+
// webpieces-disable no-function-outside-class -- a decorator factory cannot be a class method
|
|
43
|
+
function DocumentDesign() {
|
|
44
|
+
return (target) => {
|
|
45
|
+
Reflect.defineMetadata(exports.DESIGN_METADATA_KEYS.DOCUMENT_DESIGN, true, target);
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
/** True when a class is marked `@DocumentDesign`. */
|
|
49
|
+
// webpieces-disable no-function-outside-class -- reflect-metadata reader; not an injectable service
|
|
50
|
+
function isDocumentDesign(designClass) {
|
|
51
|
+
return Reflect.getMetadata(exports.DESIGN_METADATA_KEYS.DOCUMENT_DESIGN, designClass) === true;
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=di.js.map
|
package/src/di.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"di.js","sourceRoot":"","sources":["../../../../../packages/tooling/rules-config/src/di.ts"],"names":[],"mappings":";;;AA6BA,4CAKC;AAOD,wCAIC;AAID,4CAEC;AAnDD,4BAA0B;AAC1B,wEAA0D;AAG1D;;;;;;;;;;;;;;GAcG;AAEH,2GAA2G;AAC9F,QAAA,oBAAoB,GAAG;IAChC,eAAe,EAAE,2BAA2B;CAC/C,CAAC;AAEF;;GAEG;AACH,8FAA8F;AAC9F,SAAgB,gBAAgB;IAC5B,gFAAgF;IAChF,OAAO,CAAC,MAAW,EAAE,EAAE;QACnB,OAAO,IAAA,4BAAO,EAAC,MAAM,EAAE,CAAC,IAAuC,EAAE,EAAE,CAAC,IAAI,CAAC,gBAAgB,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC;IACzG,CAAC,CAAC;AACN,CAAC;AAED;;;GAGG;AACH,8FAA8F;AAC9F,SAAgB,cAAc;IAC1B,OAAO,CAAC,MAAc,EAAE,EAAE;QACtB,OAAO,CAAC,cAAc,CAAC,4BAAoB,CAAC,eAAe,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC;IAC/E,CAAC,CAAC;AACN,CAAC;AAED,qDAAqD;AACrD,oGAAoG;AACpG,SAAgB,gBAAgB,CAAC,WAAmB;IAChD,OAAO,OAAO,CAAC,WAAW,CAAC,4BAAoB,CAAC,eAAe,EAAE,WAAW,CAAC,KAAK,IAAI,CAAC;AAC3F,CAAC","sourcesContent":["import 'reflect-metadata';\nimport { provide } from '@inversifyjs/binding-decorators';\nimport type { BindInWhenOnFluentSyntax } from 'inversify';\n\n/**\n * LOCAL copies of the `@provideSingleton` and `@DocumentDesign` DI decorators.\n *\n * WHY here and not imported from `@webpieces/core-context` / `@webpieces/core-util`:\n * `@webpieces/rules-config` is the FOUNDATION package — nearly everything in the monorepo (including\n * browser + platform packages) depends on it — so it must NEVER depend UP on the platform packages.\n * `core-util` is browser-based and `core-context` is the server-side DI seam; both are the correct\n * home for these decorators for the APPS, but rules-config depending on them would invert the layering.\n *\n * These are byte-for-byte the same decorators (identical behavior + the same\n * `'webpieces:document-design'` metadata key and the same `@inversifyjs/binding-decorators` provider\n * registry), so the DI-graph analyzer — which matches `@DocumentDesign` / `@provideSingleton` by\n * decorator NAME, not import source — recognizes them exactly the same, and a consumer's\n * `buildProviderModule()` still auto-includes rules-config's `@provideSingleton` classes.\n */\n\n// Metadata key for the @DocumentDesign marker (must match core-util's so the analyzer + any reader agree).\nexport const DESIGN_METADATA_KEYS = {\n DOCUMENT_DESIGN: 'webpieces:document-design',\n};\n\n/**\n * Provides a singleton-scoped dependency. When called without arguments, the class binds to itself.\n */\n// webpieces-disable no-function-outside-class -- a decorator factory cannot be a class method\nexport function provideSingleton(): ClassDecorator {\n // webpieces-disable no-any-unknown -- decorator target is any class constructor\n return (target: any) => {\n return provide(target, (bind: BindInWhenOnFluentSyntax<unknown>) => bind.inSingletonScope())(target);\n };\n}\n\n/**\n * @DocumentDesign decorator — marks a class as a DI-design ROOT (the top-of-DAG class the design-doc\n * generator walks). A pure marker read STATICALLY by the DI-graph analyzer by decorator name.\n */\n// webpieces-disable no-function-outside-class -- a decorator factory cannot be a class method\nexport function DocumentDesign(): ClassDecorator {\n return (target: object) => {\n Reflect.defineMetadata(DESIGN_METADATA_KEYS.DOCUMENT_DESIGN, true, target);\n };\n}\n\n/** True when a class is marked `@DocumentDesign`. */\n// webpieces-disable no-function-outside-class -- reflect-metadata reader; not an injectable service\nexport function isDocumentDesign(designClass: object): boolean {\n return Reflect.getMetadata(DESIGN_METADATA_KEYS.DOCUMENT_DESIGN, designClass) === true;\n}\n"]}
|
package/src/diff-scope.js
CHANGED
|
@@ -21,7 +21,7 @@ const tslib_1 = require("tslib");
|
|
|
21
21
|
const child_process_1 = require("child_process");
|
|
22
22
|
const fs = tslib_1.__importStar(require("fs"));
|
|
23
23
|
const path = tslib_1.__importStar(require("path"));
|
|
24
|
-
const
|
|
24
|
+
const di_1 = require("./di");
|
|
25
25
|
const inversify_1 = require("inversify");
|
|
26
26
|
const to_error_1 = require("./to-error");
|
|
27
27
|
/** A git diff range: the base ref to compare against and an optional head (else the working tree). */
|
|
@@ -210,7 +210,7 @@ let DiffScope = class DiffScope {
|
|
|
210
210
|
};
|
|
211
211
|
exports.DiffScope = DiffScope;
|
|
212
212
|
exports.DiffScope = DiffScope = tslib_1.__decorate([
|
|
213
|
-
(0,
|
|
213
|
+
(0, di_1.provideSingleton)(),
|
|
214
214
|
(0, inversify_1.injectable)()
|
|
215
215
|
], DiffScope);
|
|
216
216
|
// Temporary migration delegators to DiffScope — removed once consumers inject it.
|
package/src/diff-scope.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"diff-scope.js","sourceRoot":"","sources":["../../../../../packages/tooling/rules-config/src/diff-scope.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Shared git-diff + diff-scoping service for ALL rule validators (code-rules) and nx executors\n * (nx-webpieces-rules). Centralized here in rules-config because it is the one package both depend on.\n *\n * `@provideSingleton` so it can be injected and appear in the rules-config DI design. Free-function\n * delegators are kept temporarily so the many existing consumers stay green; they migrate to injecting\n * {@link DiffScope} over follow-up PRs, then the delegators are removed.\n */\n\nimport { execSync } from 'child_process';\nimport * as fs from 'fs';\nimport * as path from 'path';\nimport { provideSingleton } from '@webpieces/core-context';\nimport { injectable } from 'inversify';\n\nimport { toError } from './to-error';\n\n/** A git diff range: the base ref to compare against and an optional head (else the working tree). */\nexport class DiffRange {\n base?: string;\n head?: string;\n}\n\n/** Options for getChangedFiles. `tsOnly` (default true) restricts to *.ts/*.tsx and drops test files. */\nexport class ChangedFilesOptions {\n tsOnly?: boolean;\n}\n\n@provideSingleton()\n@injectable()\nexport class DiffScope {\n /** Auto-detect the diff base: merge-base of HEAD with origin/main, falling back to local main. */\n detectBase(workspaceRoot: string): string | null {\n for (const ref of ['origin/main', 'main']) {\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions\n try {\n const merged = execSync(`git merge-base HEAD ${ref}`, {\n cwd: workspaceRoot, encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'],\n }).trim();\n if (merged) return merged;\n } catch (err: unknown) {\n const error = toError(err);\n void error; // swallow — try the next ref\n }\n }\n return null;\n }\n\n /** Resolve the diff range a rule should compare against (honors nx's NX_BASE / NX_HEAD). */\n resolveBase(workspaceRoot: string): DiffRange {\n const range = new DiffRange();\n range.base = process.env['NX_BASE'];\n range.head = process.env['NX_HEAD'];\n if (!range.base) {\n range.base = this.detectBase(workspaceRoot) ?? undefined;\n }\n return range;\n }\n\n /**\n * Changed files between base and head (or base→working-tree when head is omitted). Untracked files\n * are unioned in for the working-tree case. `tsOnly` (default true) restricts to *.ts/*.tsx and\n * drops test files. Deletions are excluded (`--diff-filter=d`).\n */\n // webpieces-disable max-lines-new-methods -- git command handling with untracked files needs several code paths\n getChangedFiles(workspaceRoot: string, base: string, head?: string, opts?: ChangedFilesOptions): string[] {\n const tsOnly = opts?.tsOnly ?? true;\n const glob = tsOnly ? \" -- '*.ts' '*.tsx'\" : '';\n const keep = (f: string): boolean => f.length > 0 && (!tsOnly || !this.isTestFile(f));\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions\n try {\n const diffTarget = head ? `${base} ${head}` : base;\n const output = execSync(`git diff --name-only --diff-filter=d ${diffTarget}${glob}`, {\n cwd: workspaceRoot,\n encoding: 'utf-8',\n });\n const changedFiles = output.trim().split('\\n').filter(keep);\n\n // Working-tree comparison (no head): also include untracked files, as nx affected does.\n if (!head) {\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions\n try {\n const untrackedOutput = execSync(`git ls-files --others --exclude-standard${glob}`, {\n cwd: workspaceRoot,\n encoding: 'utf-8',\n });\n const untrackedFiles = untrackedOutput.trim().split('\\n').filter(keep);\n return Array.from(new Set([...changedFiles, ...untrackedFiles]));\n } catch (err: unknown) {\n const error = toError(err);\n void error; // swallow — ls-files failure falls back to the tracked list\n return changedFiles;\n }\n }\n\n return changedFiles;\n } catch (err: unknown) {\n const error = toError(err);\n void error; // swallow — git diff failure returns an empty list\n return [];\n }\n }\n\n /** Diff content for a single file (synthetic all-added diff for an untracked file with no head). */\n getFileDiff(workspaceRoot: string, file: string, base: string, head?: string): string {\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions\n try {\n const diffTarget = head ? `${base} ${head}` : base;\n const diff = execSync(`git diff ${diffTarget} -- \"${file}\"`, {\n cwd: workspaceRoot,\n encoding: 'utf-8',\n });\n\n if (!diff && !head) {\n const fullPath = path.join(workspaceRoot, file);\n if (fs.existsSync(fullPath)) {\n const isUntracked = execSync(`git ls-files --others --exclude-standard \"${file}\"`, {\n cwd: workspaceRoot,\n encoding: 'utf-8',\n }).trim();\n\n if (isUntracked) {\n const content = fs.readFileSync(fullPath, 'utf-8');\n return content.split('\\n').map((l: string) => `+${l}`).join('\\n');\n }\n }\n }\n\n return diff;\n } catch (err: unknown) {\n const error = toError(err);\n void error; // swallow — git diff failure returns no diff\n return '';\n }\n }\n\n /** Added/changed line numbers (the `+` lines per hunk) — basis of NEW_AND_MODIFIED_CODE scoping. */\n getChangedLineNumbers(diffContent: string): Set<number> {\n const changedLines = new Set<number>();\n const lines = diffContent.split('\\n');\n let currentLine = 0;\n\n for (const line of lines) {\n const hunkMatch = line.match(/^@@ -\\d+(?:,\\d+)? \\+(\\d+)(?:,\\d+)? @@/);\n if (hunkMatch) {\n currentLine = parseInt(hunkMatch[1], 10);\n continue;\n }\n\n if (line.startsWith('+') && !line.startsWith('+++')) {\n changedLines.add(currentLine);\n currentLine++;\n } else if (line.startsWith('-') && !line.startsWith('---')) {\n // Deletions don't advance the new-file line counter.\n } else {\n currentLine++;\n }\n }\n\n return changedLines;\n }\n\n /** Method names whose signature line is a `+` addition in the diff — the basis of \"NEW\" methods. */\n findNewMethodSignaturesInDiff(diffContent: string): Set<string> {\n const newMethods = new Set<string>();\n const lines = diffContent.split('\\n');\n\n const patterns = [\n /^\\+\\s*(?:export\\s+)?(?:async\\s+)?function\\s+(\\w+)\\s*\\(/,\n /^\\+\\s*(?:export\\s+)?(?:const|let)\\s+(\\w+)\\s*=\\s*(?:async\\s*)?\\(/,\n /^\\+\\s*(?:export\\s+)?(?:const|let)\\s+(\\w+)\\s*=\\s*(?:async\\s+)?function/,\n /^\\+\\s*(?:(?:public|private|protected)\\s+)?(?:static\\s+)?(?:async\\s+)?(\\w+)\\s*\\(/,\n ];\n\n for (const line of lines) {\n if (line.startsWith('+') && !line.startsWith('+++')) {\n for (const pattern of patterns) {\n const match = line.match(pattern);\n if (match) {\n const methodName = match[1];\n if (methodName && !['if', 'for', 'while', 'switch', 'catch', 'constructor'].includes(methodName)) {\n newMethods.add(methodName);\n }\n break;\n }\n }\n }\n }\n\n return newMethods;\n }\n\n /** True if any line in [startLine, endLine] is in the changedLines set. */\n hasChangesInRange(startLine: number, endLine: number, changedLines: Set<number>): boolean {\n for (let line = startLine; line <= endLine; line++) {\n if (changedLines.has(line)) {\n return true;\n }\n }\n return false;\n }\n\n /** True if a node (method/function) is newly added or has any changed line in its range. */\n isNewOrModified(\n name: string,\n startLine: number,\n endLine: number,\n changedLines: Set<number>,\n newMethodNames: Set<string>,\n ): boolean {\n if (newMethodNames.has(name)) return true;\n return this.hasChangesInRange(startLine, endLine, changedLines);\n }\n\n // A file is \"a test file\" (excluded from diff-scoped rules) when it is a .spec/.test file or lives\n // under a __tests__/ directory.\n private isTestFile(file: string): boolean {\n return file.includes('.spec.ts') || file.includes('.test.ts') || file.includes('__tests__/');\n }\n}\n\n// Temporary migration delegators to DiffScope — removed once consumers inject it.\nconst diffScopeSvc = new DiffScope();\n\n// webpieces-disable no-function-outside-class -- temporary back-compat delegator to DiffScope; removed once consumers inject it\nexport function detectBase(workspaceRoot: string): string | null {\n return diffScopeSvc.detectBase(workspaceRoot);\n}\n\n// webpieces-disable no-function-outside-class -- temporary back-compat delegator to DiffScope; removed once consumers inject it\nexport function resolveBase(workspaceRoot: string): DiffRange {\n return diffScopeSvc.resolveBase(workspaceRoot);\n}\n\n// webpieces-disable no-function-outside-class -- temporary back-compat delegator to DiffScope; removed once consumers inject it\nexport function getChangedFiles(workspaceRoot: string, base: string, head?: string, opts?: ChangedFilesOptions): string[] {\n return diffScopeSvc.getChangedFiles(workspaceRoot, base, head, opts);\n}\n\n// webpieces-disable no-function-outside-class -- temporary back-compat delegator to DiffScope; removed once consumers inject it\nexport function getFileDiff(workspaceRoot: string, file: string, base: string, head?: string): string {\n return diffScopeSvc.getFileDiff(workspaceRoot, file, base, head);\n}\n\n// webpieces-disable no-function-outside-class -- temporary back-compat delegator to DiffScope; removed once consumers inject it\nexport function getChangedLineNumbers(diffContent: string): Set<number> {\n return diffScopeSvc.getChangedLineNumbers(diffContent);\n}\n\n// webpieces-disable no-function-outside-class -- temporary back-compat delegator to DiffScope; removed once consumers inject it\nexport function findNewMethodSignaturesInDiff(diffContent: string): Set<string> {\n return diffScopeSvc.findNewMethodSignaturesInDiff(diffContent);\n}\n\n// webpieces-disable no-function-outside-class -- temporary back-compat delegator to DiffScope; removed once consumers inject it\nexport function hasChangesInRange(startLine: number, endLine: number, changedLines: Set<number>): boolean {\n return diffScopeSvc.hasChangesInRange(startLine, endLine, changedLines);\n}\n\n// webpieces-disable no-function-outside-class -- temporary back-compat delegator to DiffScope; removed once consumers inject it\nexport function isNewOrModified(\n name: string,\n startLine: number,\n endLine: number,\n changedLines: Set<number>,\n newMethodNames: Set<string>,\n): boolean {\n return diffScopeSvc.isNewOrModified(name, startLine, endLine, changedLines, newMethodNames);\n}\n"]}
|
|
1
|
+
{"version":3,"file":"diff-scope.js","sourceRoot":"","sources":["../../../../../packages/tooling/rules-config/src/diff-scope.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Shared git-diff + diff-scoping service for ALL rule validators (code-rules) and nx executors\n * (nx-webpieces-rules). Centralized here in rules-config because it is the one package both depend on.\n *\n * `@provideSingleton` so it can be injected and appear in the rules-config DI design. Free-function\n * delegators are kept temporarily so the many existing consumers stay green; they migrate to injecting\n * {@link DiffScope} over follow-up PRs, then the delegators are removed.\n */\n\nimport { execSync } from 'child_process';\nimport * as fs from 'fs';\nimport * as path from 'path';\nimport { provideSingleton } from './di';\nimport { injectable } from 'inversify';\n\nimport { toError } from './to-error';\n\n/** A git diff range: the base ref to compare against and an optional head (else the working tree). */\nexport class DiffRange {\n base?: string;\n head?: string;\n}\n\n/** Options for getChangedFiles. `tsOnly` (default true) restricts to *.ts/*.tsx and drops test files. */\nexport class ChangedFilesOptions {\n tsOnly?: boolean;\n}\n\n@provideSingleton()\n@injectable()\nexport class DiffScope {\n /** Auto-detect the diff base: merge-base of HEAD with origin/main, falling back to local main. */\n detectBase(workspaceRoot: string): string | null {\n for (const ref of ['origin/main', 'main']) {\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions\n try {\n const merged = execSync(`git merge-base HEAD ${ref}`, {\n cwd: workspaceRoot, encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'],\n }).trim();\n if (merged) return merged;\n } catch (err: unknown) {\n const error = toError(err);\n void error; // swallow — try the next ref\n }\n }\n return null;\n }\n\n /** Resolve the diff range a rule should compare against (honors nx's NX_BASE / NX_HEAD). */\n resolveBase(workspaceRoot: string): DiffRange {\n const range = new DiffRange();\n range.base = process.env['NX_BASE'];\n range.head = process.env['NX_HEAD'];\n if (!range.base) {\n range.base = this.detectBase(workspaceRoot) ?? undefined;\n }\n return range;\n }\n\n /**\n * Changed files between base and head (or base→working-tree when head is omitted). Untracked files\n * are unioned in for the working-tree case. `tsOnly` (default true) restricts to *.ts/*.tsx and\n * drops test files. Deletions are excluded (`--diff-filter=d`).\n */\n // webpieces-disable max-lines-new-methods -- git command handling with untracked files needs several code paths\n getChangedFiles(workspaceRoot: string, base: string, head?: string, opts?: ChangedFilesOptions): string[] {\n const tsOnly = opts?.tsOnly ?? true;\n const glob = tsOnly ? \" -- '*.ts' '*.tsx'\" : '';\n const keep = (f: string): boolean => f.length > 0 && (!tsOnly || !this.isTestFile(f));\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions\n try {\n const diffTarget = head ? `${base} ${head}` : base;\n const output = execSync(`git diff --name-only --diff-filter=d ${diffTarget}${glob}`, {\n cwd: workspaceRoot,\n encoding: 'utf-8',\n });\n const changedFiles = output.trim().split('\\n').filter(keep);\n\n // Working-tree comparison (no head): also include untracked files, as nx affected does.\n if (!head) {\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions\n try {\n const untrackedOutput = execSync(`git ls-files --others --exclude-standard${glob}`, {\n cwd: workspaceRoot,\n encoding: 'utf-8',\n });\n const untrackedFiles = untrackedOutput.trim().split('\\n').filter(keep);\n return Array.from(new Set([...changedFiles, ...untrackedFiles]));\n } catch (err: unknown) {\n const error = toError(err);\n void error; // swallow — ls-files failure falls back to the tracked list\n return changedFiles;\n }\n }\n\n return changedFiles;\n } catch (err: unknown) {\n const error = toError(err);\n void error; // swallow — git diff failure returns an empty list\n return [];\n }\n }\n\n /** Diff content for a single file (synthetic all-added diff for an untracked file with no head). */\n getFileDiff(workspaceRoot: string, file: string, base: string, head?: string): string {\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions\n try {\n const diffTarget = head ? `${base} ${head}` : base;\n const diff = execSync(`git diff ${diffTarget} -- \"${file}\"`, {\n cwd: workspaceRoot,\n encoding: 'utf-8',\n });\n\n if (!diff && !head) {\n const fullPath = path.join(workspaceRoot, file);\n if (fs.existsSync(fullPath)) {\n const isUntracked = execSync(`git ls-files --others --exclude-standard \"${file}\"`, {\n cwd: workspaceRoot,\n encoding: 'utf-8',\n }).trim();\n\n if (isUntracked) {\n const content = fs.readFileSync(fullPath, 'utf-8');\n return content.split('\\n').map((l: string) => `+${l}`).join('\\n');\n }\n }\n }\n\n return diff;\n } catch (err: unknown) {\n const error = toError(err);\n void error; // swallow — git diff failure returns no diff\n return '';\n }\n }\n\n /** Added/changed line numbers (the `+` lines per hunk) — basis of NEW_AND_MODIFIED_CODE scoping. */\n getChangedLineNumbers(diffContent: string): Set<number> {\n const changedLines = new Set<number>();\n const lines = diffContent.split('\\n');\n let currentLine = 0;\n\n for (const line of lines) {\n const hunkMatch = line.match(/^@@ -\\d+(?:,\\d+)? \\+(\\d+)(?:,\\d+)? @@/);\n if (hunkMatch) {\n currentLine = parseInt(hunkMatch[1], 10);\n continue;\n }\n\n if (line.startsWith('+') && !line.startsWith('+++')) {\n changedLines.add(currentLine);\n currentLine++;\n } else if (line.startsWith('-') && !line.startsWith('---')) {\n // Deletions don't advance the new-file line counter.\n } else {\n currentLine++;\n }\n }\n\n return changedLines;\n }\n\n /** Method names whose signature line is a `+` addition in the diff — the basis of \"NEW\" methods. */\n findNewMethodSignaturesInDiff(diffContent: string): Set<string> {\n const newMethods = new Set<string>();\n const lines = diffContent.split('\\n');\n\n const patterns = [\n /^\\+\\s*(?:export\\s+)?(?:async\\s+)?function\\s+(\\w+)\\s*\\(/,\n /^\\+\\s*(?:export\\s+)?(?:const|let)\\s+(\\w+)\\s*=\\s*(?:async\\s*)?\\(/,\n /^\\+\\s*(?:export\\s+)?(?:const|let)\\s+(\\w+)\\s*=\\s*(?:async\\s+)?function/,\n /^\\+\\s*(?:(?:public|private|protected)\\s+)?(?:static\\s+)?(?:async\\s+)?(\\w+)\\s*\\(/,\n ];\n\n for (const line of lines) {\n if (line.startsWith('+') && !line.startsWith('+++')) {\n for (const pattern of patterns) {\n const match = line.match(pattern);\n if (match) {\n const methodName = match[1];\n if (methodName && !['if', 'for', 'while', 'switch', 'catch', 'constructor'].includes(methodName)) {\n newMethods.add(methodName);\n }\n break;\n }\n }\n }\n }\n\n return newMethods;\n }\n\n /** True if any line in [startLine, endLine] is in the changedLines set. */\n hasChangesInRange(startLine: number, endLine: number, changedLines: Set<number>): boolean {\n for (let line = startLine; line <= endLine; line++) {\n if (changedLines.has(line)) {\n return true;\n }\n }\n return false;\n }\n\n /** True if a node (method/function) is newly added or has any changed line in its range. */\n isNewOrModified(\n name: string,\n startLine: number,\n endLine: number,\n changedLines: Set<number>,\n newMethodNames: Set<string>,\n ): boolean {\n if (newMethodNames.has(name)) return true;\n return this.hasChangesInRange(startLine, endLine, changedLines);\n }\n\n // A file is \"a test file\" (excluded from diff-scoped rules) when it is a .spec/.test file or lives\n // under a __tests__/ directory.\n private isTestFile(file: string): boolean {\n return file.includes('.spec.ts') || file.includes('.test.ts') || file.includes('__tests__/');\n }\n}\n\n// Temporary migration delegators to DiffScope — removed once consumers inject it.\nconst diffScopeSvc = new DiffScope();\n\n// webpieces-disable no-function-outside-class -- temporary back-compat delegator to DiffScope; removed once consumers inject it\nexport function detectBase(workspaceRoot: string): string | null {\n return diffScopeSvc.detectBase(workspaceRoot);\n}\n\n// webpieces-disable no-function-outside-class -- temporary back-compat delegator to DiffScope; removed once consumers inject it\nexport function resolveBase(workspaceRoot: string): DiffRange {\n return diffScopeSvc.resolveBase(workspaceRoot);\n}\n\n// webpieces-disable no-function-outside-class -- temporary back-compat delegator to DiffScope; removed once consumers inject it\nexport function getChangedFiles(workspaceRoot: string, base: string, head?: string, opts?: ChangedFilesOptions): string[] {\n return diffScopeSvc.getChangedFiles(workspaceRoot, base, head, opts);\n}\n\n// webpieces-disable no-function-outside-class -- temporary back-compat delegator to DiffScope; removed once consumers inject it\nexport function getFileDiff(workspaceRoot: string, file: string, base: string, head?: string): string {\n return diffScopeSvc.getFileDiff(workspaceRoot, file, base, head);\n}\n\n// webpieces-disable no-function-outside-class -- temporary back-compat delegator to DiffScope; removed once consumers inject it\nexport function getChangedLineNumbers(diffContent: string): Set<number> {\n return diffScopeSvc.getChangedLineNumbers(diffContent);\n}\n\n// webpieces-disable no-function-outside-class -- temporary back-compat delegator to DiffScope; removed once consumers inject it\nexport function findNewMethodSignaturesInDiff(diffContent: string): Set<string> {\n return diffScopeSvc.findNewMethodSignaturesInDiff(diffContent);\n}\n\n// webpieces-disable no-function-outside-class -- temporary back-compat delegator to DiffScope; removed once consumers inject it\nexport function hasChangesInRange(startLine: number, endLine: number, changedLines: Set<number>): boolean {\n return diffScopeSvc.hasChangesInRange(startLine, endLine, changedLines);\n}\n\n// webpieces-disable no-function-outside-class -- temporary back-compat delegator to DiffScope; removed once consumers inject it\nexport function isNewOrModified(\n name: string,\n startLine: number,\n endLine: number,\n changedLines: Set<number>,\n newMethodNames: Set<string>,\n): boolean {\n return diffScopeSvc.isNewOrModified(name, startLine, endLine, changedLines, newMethodNames);\n}\n"]}
|
package/src/load-config.js
CHANGED
|
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.ConfigLoader = exports.LoadedConfig = void 0;
|
|
4
4
|
exports.loadAndValidate = loadAndValidate;
|
|
5
5
|
const tslib_1 = require("tslib");
|
|
6
|
-
const
|
|
6
|
+
const di_1 = require("./di");
|
|
7
7
|
const inversify_1 = require("inversify");
|
|
8
8
|
const commands_config_1 = require("./commands-config");
|
|
9
9
|
const config_file_1 = require("./config-file");
|
|
@@ -188,7 +188,7 @@ let ConfigLoader = class ConfigLoader {
|
|
|
188
188
|
};
|
|
189
189
|
exports.ConfigLoader = ConfigLoader;
|
|
190
190
|
exports.ConfigLoader = ConfigLoader = tslib_1.__decorate([
|
|
191
|
-
(0,
|
|
191
|
+
(0, di_1.provideSingleton)(),
|
|
192
192
|
(0, inversify_1.injectable)(),
|
|
193
193
|
tslib_1.__metadata("design:paramtypes", [config_file_1.ConfigFile])
|
|
194
194
|
], ConfigLoader);
|
package/src/load-config.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"load-config.js","sourceRoot":"","sources":["../../../../../packages/tooling/rules-config/src/load-config.ts"],"names":[],"mappings":";;;AAqNA,0CAEC;;AAvND,0DAA2D;AAC3D,yCAAuC;AAEvC,uDAAwE;AACxE,+CAA2C;AAC3C,mDAA+C;AAC/C,6DAAoD;AACpD,uDAAkD;AAElD,mCAA0E;AAC1E,uDAAgK;AAEhK,iEAA8D;AAE9D;;;GAGG;AACH,MAAa,YAAY;IAGR;IACA;IACA;IACA;IACA;IACA;IACA;IARb,yDAAyD;IACzD,YACa,QAAwB,EACxB,WAAiC,EACjC,QAAwB,EACxB,MAAoB,EACpB,YAA0B,EAC1B,UAAsC,EACtC,UAAyB;QANzB,aAAQ,GAAR,QAAQ,CAAgB;QACxB,gBAAW,GAAX,WAAW,CAAsB;QACjC,aAAQ,GAAR,QAAQ,CAAgB;QACxB,WAAM,GAAN,MAAM,CAAc;QACpB,iBAAY,GAAZ,YAAY,CAAc;QAC1B,eAAU,GAAV,UAAU,CAA4B;QACtC,eAAU,GAAV,UAAU,CAAe;IACnC,CAAC;CACP;AAXD,oCAWC;AAED,qGAAqG;AACrG,oGAAoG;AACpG,qGAAqG;AACrG,MAAM,uBAAuB,GAAqC;IAC9D,kBAAkB,EAAE,gBAAgB;IACpC,mBAAmB,EAAE,2BAA2B;CACnD,CAAC;AAKF;;;;GAIG;AAGI,IAAM,YAAY,GAAlB,MAAM,YAAY;IACQ;IAA7B,YAA6B,UAAsB;QAAtB,eAAU,GAAV,UAAU,CAAY;IAAG,CAAC;IAEvD;;;;OAIG;IACH,kGAAkG;IAClG,eAAe,CAAC,GAAW;QACvB,MAAM,UAAU,GAAG,IAAI,CAAC,UAAU,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC;QACvD,IAAI,CAAC,UAAU,EAAE,CAAC;YACd,MAAM,aAAa,GAAG,IAAA,qCAAmB,EAAC,SAAS,CAAC,CAAC;YACrD,OAAO,IAAI,YAAY,CACnB,IAAI,sBAAc,CAAC,IAAI,GAAG,EAAE,EAAE,IAAI,GAAG,EAAE,EAAE,EAAE,EAAE,IAAI,CAAC,EAClD,IAAI,2CAAoB,EAAE,EAC1B,aAAa,EACb,aAAa,CAAC,MAAM,EACpB,IAAI,iCAAY,CAAC,EAAE,EAAE,EAAE,CAAC,EACxB,EAAE,EACF,IAAI,CACP,CAAC;QACN,CAAC;QAED,MAAM,cAAc,GAAG,IAAI,CAAC,UAAU,CAAC,aAAa,CAAC,UAAU,CAAC,CAAC;QACjE,MAAM,YAAY,GAAG,IAAI,CAAC,uBAAuB,CAAC,cAAc,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC;QAC9E,MAAM,iBAAiB,GAAG,IAAI,CAAC,uBAAuB,CAAC,cAAc,CAAC,UAAU,IAAI,EAAE,CAAC,CAAC;QACxF,MAAM,YAAY,GAAG,cAAc,CAAC,SAAS,CAAC,CAAC;QAE/C,8FAA8F;QAC9F,iEAAiE;QACjE,MAAM,aAAa,GAAG,EAAE,GAAG,YAAY,EAAE,GAAG,iBAAiB,EAAE,CAAC;QAEhE,MAAM,QAAQ,GAAG,cAAc,CAAC,QAAQ,IAAI,EAAE,CAAC;QAE/C,MAAM,MAAM,GAAG;YACX,GAAG,IAAA,yCAAuB,EAAC,aAAa,EAAE,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC;YAC9D,GAAG,IAAA,0CAAwB,EAAC,YAAY,EAAE,iBAAiB,CAAC;YAC5D,GAAG,IAAA,yCAAuB,EAAC,cAAc,CAAC,QAAQ,EAAE,YAAY,CAAC;YACjE,GAAG,IAAA,sCAAoB,EAAC,cAAc,CAAC,YAAY,CAAC;YACpD,GAAG,IAAA,2CAAyB,EAAC,cAAc,CAAC,aAAa,CAAC,CAAC;SAC9D,CAAC;QACF,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACpB,MAAM,IAAI,+BAAa,CAAC,IAAI,CAAC,wBAAwB,CAAC,MAAM,CAAC,CAAC,CAAC;QACnE,CAAC;QACD,MAAM,QAAQ,GAAG,IAAA,qCAAmB,EAAC,cAAc,CAAC,QAAQ,EAAE,YAAY,CAAC,CAAC;QAC5E,IAAI,CAAC,oBAAoB,CAAC,aAAa,EAAE,QAAQ,CAAC,CAAC;QAEnD,MAAM,uBAAuB,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC;QACpE,MAAM,WAAW,GAAG,IAAI,GAAG,EAA8B,CAAC;QAC1D,MAAM,YAAY,GAAG,IAAI,GAAG,CAAC;YACzB,GAAG,MAAM,CAAC,IAAI,CAAC,4BAAY,CAAC;YAC5B,GAAG,MAAM,CAAC,IAAI,CAAC,aAAa,CAAC;SAChC,CAAC,CAAC;QACH,KAAK,MAAM,IAAI,IAAI,YAAY,EAAE,CAAC;YAC9B,WAAW,CAAC,GAAG,CAAC,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,4BAAY,CAAC,IAAI,CAAC,EAAE,aAAa,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACnF,CAAC;QACD,MAAM,QAAQ,GAAG,IAAI,sBAAc,CAAC,WAAW,EAAE,uBAAuB,EAAE,QAAQ,EAAE,UAAU,CAAC,CAAC;QAEhG,MAAM,WAAW,GAAG,IAAI,CAAC,yBAAyB,CAAC,aAAa,EAAE,QAAQ,CAAC,CAAC;QAC5E,MAAM,YAAY,GAAG,IAAI,CAAC,iBAAiB,CAAC,cAAc,CAAC,YAAY,CAAC,CAAC;QACzE,MAAM,UAAU,GAAG,IAAI,CAAC,eAAe,CAAC,cAAc,CAAC,aAAa,CAAC,CAAC,CAAC;QAEvE,OAAO,IAAI,YAAY,CAAC,QAAQ,EAAE,WAAW,EAAE,QAAQ,EAAE,QAAQ,CAAC,MAAM,EAAE,YAAY,EAAE,UAAU,EAAE,UAAU,CAAC,CAAC;IACpH,CAAC;IAED,mGAAmG;IACnG,gGAAgG;IACxF,oBAAoB;IACxB,mEAAmE;IACnE,KAA8C,EAC9C,QAAwB;QAExB,MAAM,UAAU,GAAG,KAAK,CAAC,2BAA2B,CAAC,CAAC;QACtD,IAAI,UAAU,IAAI,UAAU,CAAC,iBAAiB,CAAC,KAAK,SAAS,EAAE,CAAC;YAC5D,UAAU,CAAC,iBAAiB,CAAC,GAAG,QAAQ,CAAC,QAAQ,CAAC;QACtD,CAAC;QACD,MAAM,eAAe,GAAG,KAAK,CAAC,yBAAyB,CAAC,CAAC;QACzD,IAAI,eAAe,IAAI,eAAe,CAAC,sBAAsB,CAAC,KAAK,SAAS,EAAE,CAAC;YAC3E,eAAe,CAAC,sBAAsB,CAAC,GAAG,QAAQ,CAAC,aAAa,CAAC;QACrE,CAAC;IACL,CAAC;IAED,kFAAkF;IAC1E,SAAS;IACb,wDAAwD;IACxD,QAA6C;IAC7C,wDAAwD;IACxD,YAAiD;QAEjD,IAAI,CAAC,QAAQ,IAAI,CAAC,YAAY;YAAE,OAAO,IAAI,0BAAkB,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;QAC/E,IAAI,CAAC,QAAQ;YAAE,OAAO,IAAI,0BAAkB,CAAC,YAA4B,CAAC,CAAC;QAC3E,IAAI,CAAC,YAAY;YAAE,OAAO,IAAI,0BAAkB,CAAC,QAAuB,CAAC,CAAC;QAE1E,iEAAiE;QACjE,MAAM,MAAM,GAA4B,EAAE,CAAC;QAC3C,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC;YAAE,MAAM,CAAC,GAAG,CAAC,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC;QACrE,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,YAAY,CAAC;YAAE,MAAM,CAAC,GAAG,CAAC,GAAG,YAAY,CAAC,GAAG,CAAC,CAAC;QAC7E,OAAO,IAAI,0BAAkB,CAAC,MAAqB,CAAC,CAAC;IACzD,CAAC;IAED,oFAAoF;IACpF,wFAAwF;IAChF,iBAAiB,CAAC,GAAY;QAClC,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,KAAK,IAAI,IAAI,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC;YAAE,OAAO,IAAI,iCAAY,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC;QACnG,4FAA4F;QAC5F,MAAM,CAAC,GAAG,GAA+B,CAAC;QAC1C,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,OAAO,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QAC7F,MAAM,MAAM,GAAG,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,OAAO,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QAChG,OAAO,IAAI,iCAAY,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;IAC3C,CAAC;IAED,oFAAoF;IACpF,oGAAoG;IAC5F,eAAe,CAAC,GAAY;QAChC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC;YAAE,OAAO,EAAE,CAAC;QACnC,OAAO,GAAwB,CAAC;IACpC,CAAC;IAEO,yBAAyB;IAC7B,4FAA4F;IAC5F,QAAiD,EACjD,QAAkB;QAElB,MAAM,KAAK,GAAG,IAAI,2CAAoB,EAAE,CAAC;QACzC,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;YACtC,4EAA4E;YAC3E,KAAiC,CAAC,GAAG,CAAC,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC;QAC5D,CAAC;QACD,KAAK,CAAC,QAAQ,GAAG,QAAQ,CAAC;QAC1B,OAAO,KAAK,CAAC;IACjB,CAAC;IAEO,uBAAuB,CAAC,OAAuB;QACnD,MAAM,GAAG,GAAmB,EAAE,CAAC;QAC/B,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;YACrC,GAAG,CAAC,uBAAuB,CAAC,GAAG,CAAC,IAAI,GAAG,CAAC,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;QAC5D,CAAC;QACD,OAAO,GAAG,CAAC;IACf,CAAC;IAED,iGAAiG;IACzF,wBAAwB,CAAC,MAAgB;QAC7C,OAAO,CACH,6BAA6B,MAAM,CAAC,MAAM,iDAAiD;YAC3F,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC;YACtC,yFAAyF;YACzF,6FAA6F;YAC7F,2FAA2F;YAC3F,sFAAsF;YACtF,6FAA6F;YAC7F,qFAAqF;YACrF,sGAAsG;YACtG,gGAAgG;YAChG,uFAAuF,CAC1F,CAAC;IACN,CAAC;CACJ,CAAA;AA7JY,oCAAY;uBAAZ,YAAY;IAFxB,IAAA,+BAAgB,GAAE;IAClB,IAAA,sBAAU,GAAE;6CAEgC,wBAAU;GAD1C,YAAY,CA6JxB;AAED,kGAAkG;AAClG,8FAA8F;AAC9F,MAAM,eAAe,GAAG,IAAI,YAAY,CAAC,IAAI,wBAAU,EAAE,CAAC,CAAC;AAE3D,2IAA2I;AAC3I,SAAgB,eAAe,CAAC,GAAW;IACvC,OAAO,eAAe,CAAC,eAAe,CAAC,GAAG,CAAC,CAAC;AAChD,CAAC","sourcesContent":["import { provideSingleton } from '@webpieces/core-context';\nimport { injectable } from 'inversify';\n\nimport { buildCommandsConfig, CommandsConfig } from './commands-config';\nimport { ConfigFile } from './config-file';\nimport { defaultRules } from './default-rules';\nimport { ExcludePaths } from './exclude-hook-paths';\nimport { InformAiError } from './inform-ai-error';\nimport { PrGateConfig } from './pr-gate-config';\nimport { ResolvedConfig, ResolvedRuleConfig, RuleOptions } from './types';\nimport { validateCommandsSection, validateExcludePaths, validateMatchRulesSection, validateSectionPlacement, validateWebpiecesConfig } from './validate-config';\nimport { MatchRuleConfig } from './match-rules-config';\nimport { WebpiecesRulesConfig } from './WebpiecesRulesConfig';\n\n/**\n * Everything a consumer might need from webpieces.config.json, produced from ONE parse + ONE\n * validation pass. Data-only (per CLAUDE.md, classes for data).\n */\nexport class LoadedConfig {\n // eslint-disable-next-line @typescript-eslint/max-params\n constructor(\n readonly resolved: ResolvedConfig,\n readonly rulesConfig: WebpiecesRulesConfig,\n readonly commands: CommandsConfig,\n readonly prGate: PrGateConfig,\n readonly excludePaths: ExcludePaths,\n readonly matchRules: readonly MatchRuleConfig[],\n readonly configPath: string | null,\n ) {}\n}\n\n// Config keys that were renamed. A project's webpieces.config.json may still use the OLD key (it can\n// legitimately lag the published rules-config by a release), so normalize any deprecated key to its\n// canonical name BEFORE validation/placement/loading — every downstream consumer then sees one name.\nconst DEPRECATED_RULE_ALIASES: Readonly<Record<string, string>> = {\n 'pr-merge-cleanup': 'pr-merge-guard',\n 'pr-creation-guard': 'pr-creation-or-push-guard',\n};\n\n// webpieces-disable no-any-unknown -- opaque per-rule option bags from consumer JSON, validated later\ntype RuleSectionMap = Record<string, Record<string, unknown>>;\n\n/**\n * The single load+validate entry point for ALL consumers (ai-hook-rules, code-rules,\n * nx-webpieces-rules, pr-gate scripts). `@provideSingleton` + injects {@link ConfigFile} so it appears\n * in the rules-config DI design.\n */\n@provideSingleton()\n@injectable()\nexport class ConfigLoader {\n constructor(private readonly configFile: ConfigFile) {}\n\n /**\n * Reads webpieces.config.json once, validates BOTH the `rules` map and the top-level `pr-gate`\n * block, and throws one InformAiError listing every error. When no config file is found it returns\n * lenient empties/defaults (matching prior no-file behavior).\n */\n // webpieces-disable max-lines-new-methods -- the single load+validate pass is one cohesive method\n loadAndValidate(cwd: string): LoadedConfig {\n const configPath = this.configFile.findConfigFile(cwd);\n if (!configPath) {\n const emptyCommands = buildCommandsConfig(undefined);\n return new LoadedConfig(\n new ResolvedConfig(new Map(), new Set(), [], null),\n new WebpiecesRulesConfig(),\n emptyCommands,\n emptyCommands.prGate,\n new ExcludePaths([], []),\n [],\n null,\n );\n }\n\n const consumerConfig = this.configFile.readRawConfig(configPath);\n const rulesSection = this.normalizeDeprecatedKeys(consumerConfig.rules || {});\n const hookGuardsSection = this.normalizeDeprecatedKeys(consumerConfig.hookGuards || {});\n const legacyPrGate = consumerConfig['pr-gate'];\n\n // rules + hookGuards are validated/loaded as one flat name→config map (the runtime dispatches\n // by each rule's own `scope`). Placement is enforced separately.\n const overrideRules = { ...rulesSection, ...hookGuardsSection };\n\n const rulesDir = consumerConfig.rulesDir ?? [];\n\n const errors = [\n ...validateWebpiecesConfig(overrideRules, rulesDir.length > 0),\n ...validateSectionPlacement(rulesSection, hookGuardsSection),\n ...validateCommandsSection(consumerConfig.commands, legacyPrGate),\n ...validateExcludePaths(consumerConfig.excludePaths),\n ...validateMatchRulesSection(consumerConfig['match-rules']),\n ];\n if (errors.length > 0) {\n throw new InformAiError(this.formatConfigErrorsBanner(errors));\n }\n const commands = buildCommandsConfig(consumerConfig.commands, legacyPrGate);\n this.applyCommandDefaults(overrideRules, commands);\n\n const userConfiguredRuleNames = new Set(Object.keys(overrideRules));\n const mergedRules = new Map<string, ResolvedRuleConfig>();\n const allRuleNames = new Set([\n ...Object.keys(defaultRules),\n ...Object.keys(overrideRules),\n ]);\n for (const name of allRuleNames) {\n mergedRules.set(name, this.mergeRule(defaultRules[name], overrideRules[name]));\n }\n const resolved = new ResolvedConfig(mergedRules, userConfiguredRuleNames, rulesDir, configPath);\n\n const rulesConfig = this.buildWebpiecesRulesConfig(overrideRules, rulesDir);\n const excludePaths = this.parseExcludePaths(consumerConfig.excludePaths);\n const matchRules = this.parseMatchRules(consumerConfig['match-rules']);\n\n return new LoadedConfig(resolved, rulesConfig, commands, commands.prGate, excludePaths, matchRules, configPath);\n }\n\n // Inject the canonical command strings (from the `commands` section) as the DEFAULT for the guards\n // that surface them in their fix hints. Only fills a gap — an explicit per-guard override wins.\n private applyCommandDefaults(\n // webpieces-disable no-any-unknown -- opaque merged rule/guard map\n rules: Record<string, Record<string, unknown>>,\n commands: CommandsConfig,\n ): void {\n const prCreation = rules['pr-creation-or-push-guard'];\n if (prCreation && prCreation['upsertPrCommand'] === undefined) {\n prCreation['upsertPrCommand'] = commands.upsertPr;\n }\n const mergeInProgress = rules['merge-in-progress-guard'];\n if (mergeInProgress && mergeInProgress['mergeCompleteCommand'] === undefined) {\n mergeInProgress['mergeCompleteCommand'] = commands.mergeComplete;\n }\n }\n\n // webpieces-disable no-any-unknown -- merging opaque option bags from config JSON\n private mergeRule(\n // webpieces-disable no-any-unknown -- opaque option bag\n baseRule: Record<string, unknown> | undefined,\n // webpieces-disable no-any-unknown -- opaque option bag\n overrideRule: Record<string, unknown> | undefined,\n ): ResolvedRuleConfig {\n if (!baseRule && !overrideRule) return new ResolvedRuleConfig({ mode: 'OFF' });\n if (!baseRule) return new ResolvedRuleConfig(overrideRule! as RuleOptions);\n if (!overrideRule) return new ResolvedRuleConfig(baseRule as RuleOptions);\n\n // webpieces-disable no-any-unknown -- building merged option bag\n const merged: Record<string, unknown> = {};\n for (const key of Object.keys(baseRule)) merged[key] = baseRule[key];\n for (const key of Object.keys(overrideRule)) merged[key] = overrideRule[key];\n return new ResolvedRuleConfig(merged as RuleOptions);\n }\n\n // Parse the (already-validated) raw excludePaths block into the typed ExcludePaths.\n // webpieces-disable no-any-unknown -- `raw` is opaque consumer JSON until narrowed here\n private parseExcludePaths(raw: unknown): ExcludePaths {\n if (typeof raw !== 'object' || raw === null || Array.isArray(raw)) return new ExcludePaths([], []);\n // webpieces-disable no-any-unknown -- validateExcludePaths already proved both are string[]\n const s = raw as Record<string, string[]>;\n const rules = Array.isArray(s['rules']) ? s['rules'].filter(p => typeof p === 'string') : [];\n const guards = Array.isArray(s['guards']) ? s['guards'].filter(p => typeof p === 'string') : [];\n return new ExcludePaths(rules, guards);\n }\n\n // Parse the (already-validated) raw match-rules array into typed MatchRuleConfig[].\n // webpieces-disable no-any-unknown -- validated array; each entry cast to the typed MatchRuleConfig\n private parseMatchRules(raw: unknown): MatchRuleConfig[] {\n if (!Array.isArray(raw)) return [];\n return raw as MatchRuleConfig[];\n }\n\n private buildWebpiecesRulesConfig(\n // webpieces-disable no-any-unknown -- JSON values are opaque until assigned to typed fields\n rawRules: Record<string, Record<string, unknown>>,\n rulesDir: string[],\n ): WebpiecesRulesConfig {\n const typed = new WebpiecesRulesConfig();\n for (const key of Object.keys(rawRules)) {\n // webpieces-disable no-any-unknown -- dynamic key assignment to typed class\n (typed as Record<string, unknown>)[key] = rawRules[key];\n }\n typed.rulesDir = rulesDir;\n return typed;\n }\n\n private normalizeDeprecatedKeys(section: RuleSectionMap): RuleSectionMap {\n const out: RuleSectionMap = {};\n for (const key of Object.keys(section)) {\n out[DEPRECATED_RULE_ALIASES[key] ?? key] = section[key];\n }\n return out;\n }\n\n // Assemble the validation-failure banner. Most of these errors are version skew, not bad config.\n private formatConfigErrorsBanner(errors: string[]): string {\n return (\n `webpieces.config.json has ${errors.length} validation error(s) — fix ALL, then retry:\\n\\n` +\n errors.map(e => ` • ${e}`).join('\\n') +\n `\\n\\n👉 FIX ORDER (do NOT start by deleting keys — that usually deletes VALID config):\\n` +\n ` 1. Run \\`pnpm install\\`. It is ALWAYS allowed through the guard (installer bypass), even ` +\n `while this config is invalid. This is the #1 cause: your installed @webpieces guard is a ` +\n `release BEHIND webpieces.config.json (a dep bump updated the config + lockfile, but ` +\n `node_modules here was never re-installed), so the running validator doesn't know the newer ` +\n `rule names/values yet. \\`pnpm install\\` syncs node_modules to the pinned version.\\n` +\n ` 2. Retry your command. If the errors are gone, you're DONE — do not touch webpieces.config.json.\\n` +\n ` 3. ONLY if an error survives a fresh install is it a genuine typo / removed / renamed rule. ` +\n `Then edit webpieces.config.json (edits to it are ALWAYS allowed) to fix each • above.`\n );\n }\n}\n\n// Temporary migration delegator — consumers migrate to injecting ConfigLoader over follow-up PRs,\n// then this free function is removed. The logic now lives in the injected ConfigLoader class.\nconst configLoaderSvc = new ConfigLoader(new ConfigFile());\n\n// webpieces-disable no-function-outside-class -- temporary back-compat delegator to ConfigLoader; removed once all 118 consumers inject it\nexport function loadAndValidate(cwd: string): LoadedConfig {\n return configLoaderSvc.loadAndValidate(cwd);\n}\n"]}
|
|
1
|
+
{"version":3,"file":"load-config.js","sourceRoot":"","sources":["../../../../../packages/tooling/rules-config/src/load-config.ts"],"names":[],"mappings":";;;AAqNA,0CAEC;;AAvND,6BAAwC;AACxC,yCAAuC;AAEvC,uDAAwE;AACxE,+CAA2C;AAC3C,mDAA+C;AAC/C,6DAAoD;AACpD,uDAAkD;AAElD,mCAA0E;AAC1E,uDAAgK;AAEhK,iEAA8D;AAE9D;;;GAGG;AACH,MAAa,YAAY;IAGR;IACA;IACA;IACA;IACA;IACA;IACA;IARb,yDAAyD;IACzD,YACa,QAAwB,EACxB,WAAiC,EACjC,QAAwB,EACxB,MAAoB,EACpB,YAA0B,EAC1B,UAAsC,EACtC,UAAyB;QANzB,aAAQ,GAAR,QAAQ,CAAgB;QACxB,gBAAW,GAAX,WAAW,CAAsB;QACjC,aAAQ,GAAR,QAAQ,CAAgB;QACxB,WAAM,GAAN,MAAM,CAAc;QACpB,iBAAY,GAAZ,YAAY,CAAc;QAC1B,eAAU,GAAV,UAAU,CAA4B;QACtC,eAAU,GAAV,UAAU,CAAe;IACnC,CAAC;CACP;AAXD,oCAWC;AAED,qGAAqG;AACrG,oGAAoG;AACpG,qGAAqG;AACrG,MAAM,uBAAuB,GAAqC;IAC9D,kBAAkB,EAAE,gBAAgB;IACpC,mBAAmB,EAAE,2BAA2B;CACnD,CAAC;AAKF;;;;GAIG;AAGI,IAAM,YAAY,GAAlB,MAAM,YAAY;IACQ;IAA7B,YAA6B,UAAsB;QAAtB,eAAU,GAAV,UAAU,CAAY;IAAG,CAAC;IAEvD;;;;OAIG;IACH,kGAAkG;IAClG,eAAe,CAAC,GAAW;QACvB,MAAM,UAAU,GAAG,IAAI,CAAC,UAAU,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC;QACvD,IAAI,CAAC,UAAU,EAAE,CAAC;YACd,MAAM,aAAa,GAAG,IAAA,qCAAmB,EAAC,SAAS,CAAC,CAAC;YACrD,OAAO,IAAI,YAAY,CACnB,IAAI,sBAAc,CAAC,IAAI,GAAG,EAAE,EAAE,IAAI,GAAG,EAAE,EAAE,EAAE,EAAE,IAAI,CAAC,EAClD,IAAI,2CAAoB,EAAE,EAC1B,aAAa,EACb,aAAa,CAAC,MAAM,EACpB,IAAI,iCAAY,CAAC,EAAE,EAAE,EAAE,CAAC,EACxB,EAAE,EACF,IAAI,CACP,CAAC;QACN,CAAC;QAED,MAAM,cAAc,GAAG,IAAI,CAAC,UAAU,CAAC,aAAa,CAAC,UAAU,CAAC,CAAC;QACjE,MAAM,YAAY,GAAG,IAAI,CAAC,uBAAuB,CAAC,cAAc,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC;QAC9E,MAAM,iBAAiB,GAAG,IAAI,CAAC,uBAAuB,CAAC,cAAc,CAAC,UAAU,IAAI,EAAE,CAAC,CAAC;QACxF,MAAM,YAAY,GAAG,cAAc,CAAC,SAAS,CAAC,CAAC;QAE/C,8FAA8F;QAC9F,iEAAiE;QACjE,MAAM,aAAa,GAAG,EAAE,GAAG,YAAY,EAAE,GAAG,iBAAiB,EAAE,CAAC;QAEhE,MAAM,QAAQ,GAAG,cAAc,CAAC,QAAQ,IAAI,EAAE,CAAC;QAE/C,MAAM,MAAM,GAAG;YACX,GAAG,IAAA,yCAAuB,EAAC,aAAa,EAAE,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC;YAC9D,GAAG,IAAA,0CAAwB,EAAC,YAAY,EAAE,iBAAiB,CAAC;YAC5D,GAAG,IAAA,yCAAuB,EAAC,cAAc,CAAC,QAAQ,EAAE,YAAY,CAAC;YACjE,GAAG,IAAA,sCAAoB,EAAC,cAAc,CAAC,YAAY,CAAC;YACpD,GAAG,IAAA,2CAAyB,EAAC,cAAc,CAAC,aAAa,CAAC,CAAC;SAC9D,CAAC;QACF,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACpB,MAAM,IAAI,+BAAa,CAAC,IAAI,CAAC,wBAAwB,CAAC,MAAM,CAAC,CAAC,CAAC;QACnE,CAAC;QACD,MAAM,QAAQ,GAAG,IAAA,qCAAmB,EAAC,cAAc,CAAC,QAAQ,EAAE,YAAY,CAAC,CAAC;QAC5E,IAAI,CAAC,oBAAoB,CAAC,aAAa,EAAE,QAAQ,CAAC,CAAC;QAEnD,MAAM,uBAAuB,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC;QACpE,MAAM,WAAW,GAAG,IAAI,GAAG,EAA8B,CAAC;QAC1D,MAAM,YAAY,GAAG,IAAI,GAAG,CAAC;YACzB,GAAG,MAAM,CAAC,IAAI,CAAC,4BAAY,CAAC;YAC5B,GAAG,MAAM,CAAC,IAAI,CAAC,aAAa,CAAC;SAChC,CAAC,CAAC;QACH,KAAK,MAAM,IAAI,IAAI,YAAY,EAAE,CAAC;YAC9B,WAAW,CAAC,GAAG,CAAC,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,4BAAY,CAAC,IAAI,CAAC,EAAE,aAAa,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACnF,CAAC;QACD,MAAM,QAAQ,GAAG,IAAI,sBAAc,CAAC,WAAW,EAAE,uBAAuB,EAAE,QAAQ,EAAE,UAAU,CAAC,CAAC;QAEhG,MAAM,WAAW,GAAG,IAAI,CAAC,yBAAyB,CAAC,aAAa,EAAE,QAAQ,CAAC,CAAC;QAC5E,MAAM,YAAY,GAAG,IAAI,CAAC,iBAAiB,CAAC,cAAc,CAAC,YAAY,CAAC,CAAC;QACzE,MAAM,UAAU,GAAG,IAAI,CAAC,eAAe,CAAC,cAAc,CAAC,aAAa,CAAC,CAAC,CAAC;QAEvE,OAAO,IAAI,YAAY,CAAC,QAAQ,EAAE,WAAW,EAAE,QAAQ,EAAE,QAAQ,CAAC,MAAM,EAAE,YAAY,EAAE,UAAU,EAAE,UAAU,CAAC,CAAC;IACpH,CAAC;IAED,mGAAmG;IACnG,gGAAgG;IACxF,oBAAoB;IACxB,mEAAmE;IACnE,KAA8C,EAC9C,QAAwB;QAExB,MAAM,UAAU,GAAG,KAAK,CAAC,2BAA2B,CAAC,CAAC;QACtD,IAAI,UAAU,IAAI,UAAU,CAAC,iBAAiB,CAAC,KAAK,SAAS,EAAE,CAAC;YAC5D,UAAU,CAAC,iBAAiB,CAAC,GAAG,QAAQ,CAAC,QAAQ,CAAC;QACtD,CAAC;QACD,MAAM,eAAe,GAAG,KAAK,CAAC,yBAAyB,CAAC,CAAC;QACzD,IAAI,eAAe,IAAI,eAAe,CAAC,sBAAsB,CAAC,KAAK,SAAS,EAAE,CAAC;YAC3E,eAAe,CAAC,sBAAsB,CAAC,GAAG,QAAQ,CAAC,aAAa,CAAC;QACrE,CAAC;IACL,CAAC;IAED,kFAAkF;IAC1E,SAAS;IACb,wDAAwD;IACxD,QAA6C;IAC7C,wDAAwD;IACxD,YAAiD;QAEjD,IAAI,CAAC,QAAQ,IAAI,CAAC,YAAY;YAAE,OAAO,IAAI,0BAAkB,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;QAC/E,IAAI,CAAC,QAAQ;YAAE,OAAO,IAAI,0BAAkB,CAAC,YAA4B,CAAC,CAAC;QAC3E,IAAI,CAAC,YAAY;YAAE,OAAO,IAAI,0BAAkB,CAAC,QAAuB,CAAC,CAAC;QAE1E,iEAAiE;QACjE,MAAM,MAAM,GAA4B,EAAE,CAAC;QAC3C,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC;YAAE,MAAM,CAAC,GAAG,CAAC,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC;QACrE,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,YAAY,CAAC;YAAE,MAAM,CAAC,GAAG,CAAC,GAAG,YAAY,CAAC,GAAG,CAAC,CAAC;QAC7E,OAAO,IAAI,0BAAkB,CAAC,MAAqB,CAAC,CAAC;IACzD,CAAC;IAED,oFAAoF;IACpF,wFAAwF;IAChF,iBAAiB,CAAC,GAAY;QAClC,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,KAAK,IAAI,IAAI,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC;YAAE,OAAO,IAAI,iCAAY,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC;QACnG,4FAA4F;QAC5F,MAAM,CAAC,GAAG,GAA+B,CAAC;QAC1C,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,OAAO,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QAC7F,MAAM,MAAM,GAAG,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,OAAO,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QAChG,OAAO,IAAI,iCAAY,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;IAC3C,CAAC;IAED,oFAAoF;IACpF,oGAAoG;IAC5F,eAAe,CAAC,GAAY;QAChC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC;YAAE,OAAO,EAAE,CAAC;QACnC,OAAO,GAAwB,CAAC;IACpC,CAAC;IAEO,yBAAyB;IAC7B,4FAA4F;IAC5F,QAAiD,EACjD,QAAkB;QAElB,MAAM,KAAK,GAAG,IAAI,2CAAoB,EAAE,CAAC;QACzC,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;YACtC,4EAA4E;YAC3E,KAAiC,CAAC,GAAG,CAAC,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC;QAC5D,CAAC;QACD,KAAK,CAAC,QAAQ,GAAG,QAAQ,CAAC;QAC1B,OAAO,KAAK,CAAC;IACjB,CAAC;IAEO,uBAAuB,CAAC,OAAuB;QACnD,MAAM,GAAG,GAAmB,EAAE,CAAC;QAC/B,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;YACrC,GAAG,CAAC,uBAAuB,CAAC,GAAG,CAAC,IAAI,GAAG,CAAC,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;QAC5D,CAAC;QACD,OAAO,GAAG,CAAC;IACf,CAAC;IAED,iGAAiG;IACzF,wBAAwB,CAAC,MAAgB;QAC7C,OAAO,CACH,6BAA6B,MAAM,CAAC,MAAM,iDAAiD;YAC3F,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC;YACtC,yFAAyF;YACzF,6FAA6F;YAC7F,2FAA2F;YAC3F,sFAAsF;YACtF,6FAA6F;YAC7F,qFAAqF;YACrF,sGAAsG;YACtG,gGAAgG;YAChG,uFAAuF,CAC1F,CAAC;IACN,CAAC;CACJ,CAAA;AA7JY,oCAAY;uBAAZ,YAAY;IAFxB,IAAA,qBAAgB,GAAE;IAClB,IAAA,sBAAU,GAAE;6CAEgC,wBAAU;GAD1C,YAAY,CA6JxB;AAED,kGAAkG;AAClG,8FAA8F;AAC9F,MAAM,eAAe,GAAG,IAAI,YAAY,CAAC,IAAI,wBAAU,EAAE,CAAC,CAAC;AAE3D,2IAA2I;AAC3I,SAAgB,eAAe,CAAC,GAAW;IACvC,OAAO,eAAe,CAAC,eAAe,CAAC,GAAG,CAAC,CAAC;AAChD,CAAC","sourcesContent":["import { provideSingleton } from './di';\nimport { injectable } from 'inversify';\n\nimport { buildCommandsConfig, CommandsConfig } from './commands-config';\nimport { ConfigFile } from './config-file';\nimport { defaultRules } from './default-rules';\nimport { ExcludePaths } from './exclude-hook-paths';\nimport { InformAiError } from './inform-ai-error';\nimport { PrGateConfig } from './pr-gate-config';\nimport { ResolvedConfig, ResolvedRuleConfig, RuleOptions } from './types';\nimport { validateCommandsSection, validateExcludePaths, validateMatchRulesSection, validateSectionPlacement, validateWebpiecesConfig } from './validate-config';\nimport { MatchRuleConfig } from './match-rules-config';\nimport { WebpiecesRulesConfig } from './WebpiecesRulesConfig';\n\n/**\n * Everything a consumer might need from webpieces.config.json, produced from ONE parse + ONE\n * validation pass. Data-only (per CLAUDE.md, classes for data).\n */\nexport class LoadedConfig {\n // eslint-disable-next-line @typescript-eslint/max-params\n constructor(\n readonly resolved: ResolvedConfig,\n readonly rulesConfig: WebpiecesRulesConfig,\n readonly commands: CommandsConfig,\n readonly prGate: PrGateConfig,\n readonly excludePaths: ExcludePaths,\n readonly matchRules: readonly MatchRuleConfig[],\n readonly configPath: string | null,\n ) {}\n}\n\n// Config keys that were renamed. A project's webpieces.config.json may still use the OLD key (it can\n// legitimately lag the published rules-config by a release), so normalize any deprecated key to its\n// canonical name BEFORE validation/placement/loading — every downstream consumer then sees one name.\nconst DEPRECATED_RULE_ALIASES: Readonly<Record<string, string>> = {\n 'pr-merge-cleanup': 'pr-merge-guard',\n 'pr-creation-guard': 'pr-creation-or-push-guard',\n};\n\n// webpieces-disable no-any-unknown -- opaque per-rule option bags from consumer JSON, validated later\ntype RuleSectionMap = Record<string, Record<string, unknown>>;\n\n/**\n * The single load+validate entry point for ALL consumers (ai-hook-rules, code-rules,\n * nx-webpieces-rules, pr-gate scripts). `@provideSingleton` + injects {@link ConfigFile} so it appears\n * in the rules-config DI design.\n */\n@provideSingleton()\n@injectable()\nexport class ConfigLoader {\n constructor(private readonly configFile: ConfigFile) {}\n\n /**\n * Reads webpieces.config.json once, validates BOTH the `rules` map and the top-level `pr-gate`\n * block, and throws one InformAiError listing every error. When no config file is found it returns\n * lenient empties/defaults (matching prior no-file behavior).\n */\n // webpieces-disable max-lines-new-methods -- the single load+validate pass is one cohesive method\n loadAndValidate(cwd: string): LoadedConfig {\n const configPath = this.configFile.findConfigFile(cwd);\n if (!configPath) {\n const emptyCommands = buildCommandsConfig(undefined);\n return new LoadedConfig(\n new ResolvedConfig(new Map(), new Set(), [], null),\n new WebpiecesRulesConfig(),\n emptyCommands,\n emptyCommands.prGate,\n new ExcludePaths([], []),\n [],\n null,\n );\n }\n\n const consumerConfig = this.configFile.readRawConfig(configPath);\n const rulesSection = this.normalizeDeprecatedKeys(consumerConfig.rules || {});\n const hookGuardsSection = this.normalizeDeprecatedKeys(consumerConfig.hookGuards || {});\n const legacyPrGate = consumerConfig['pr-gate'];\n\n // rules + hookGuards are validated/loaded as one flat name→config map (the runtime dispatches\n // by each rule's own `scope`). Placement is enforced separately.\n const overrideRules = { ...rulesSection, ...hookGuardsSection };\n\n const rulesDir = consumerConfig.rulesDir ?? [];\n\n const errors = [\n ...validateWebpiecesConfig(overrideRules, rulesDir.length > 0),\n ...validateSectionPlacement(rulesSection, hookGuardsSection),\n ...validateCommandsSection(consumerConfig.commands, legacyPrGate),\n ...validateExcludePaths(consumerConfig.excludePaths),\n ...validateMatchRulesSection(consumerConfig['match-rules']),\n ];\n if (errors.length > 0) {\n throw new InformAiError(this.formatConfigErrorsBanner(errors));\n }\n const commands = buildCommandsConfig(consumerConfig.commands, legacyPrGate);\n this.applyCommandDefaults(overrideRules, commands);\n\n const userConfiguredRuleNames = new Set(Object.keys(overrideRules));\n const mergedRules = new Map<string, ResolvedRuleConfig>();\n const allRuleNames = new Set([\n ...Object.keys(defaultRules),\n ...Object.keys(overrideRules),\n ]);\n for (const name of allRuleNames) {\n mergedRules.set(name, this.mergeRule(defaultRules[name], overrideRules[name]));\n }\n const resolved = new ResolvedConfig(mergedRules, userConfiguredRuleNames, rulesDir, configPath);\n\n const rulesConfig = this.buildWebpiecesRulesConfig(overrideRules, rulesDir);\n const excludePaths = this.parseExcludePaths(consumerConfig.excludePaths);\n const matchRules = this.parseMatchRules(consumerConfig['match-rules']);\n\n return new LoadedConfig(resolved, rulesConfig, commands, commands.prGate, excludePaths, matchRules, configPath);\n }\n\n // Inject the canonical command strings (from the `commands` section) as the DEFAULT for the guards\n // that surface them in their fix hints. Only fills a gap — an explicit per-guard override wins.\n private applyCommandDefaults(\n // webpieces-disable no-any-unknown -- opaque merged rule/guard map\n rules: Record<string, Record<string, unknown>>,\n commands: CommandsConfig,\n ): void {\n const prCreation = rules['pr-creation-or-push-guard'];\n if (prCreation && prCreation['upsertPrCommand'] === undefined) {\n prCreation['upsertPrCommand'] = commands.upsertPr;\n }\n const mergeInProgress = rules['merge-in-progress-guard'];\n if (mergeInProgress && mergeInProgress['mergeCompleteCommand'] === undefined) {\n mergeInProgress['mergeCompleteCommand'] = commands.mergeComplete;\n }\n }\n\n // webpieces-disable no-any-unknown -- merging opaque option bags from config JSON\n private mergeRule(\n // webpieces-disable no-any-unknown -- opaque option bag\n baseRule: Record<string, unknown> | undefined,\n // webpieces-disable no-any-unknown -- opaque option bag\n overrideRule: Record<string, unknown> | undefined,\n ): ResolvedRuleConfig {\n if (!baseRule && !overrideRule) return new ResolvedRuleConfig({ mode: 'OFF' });\n if (!baseRule) return new ResolvedRuleConfig(overrideRule! as RuleOptions);\n if (!overrideRule) return new ResolvedRuleConfig(baseRule as RuleOptions);\n\n // webpieces-disable no-any-unknown -- building merged option bag\n const merged: Record<string, unknown> = {};\n for (const key of Object.keys(baseRule)) merged[key] = baseRule[key];\n for (const key of Object.keys(overrideRule)) merged[key] = overrideRule[key];\n return new ResolvedRuleConfig(merged as RuleOptions);\n }\n\n // Parse the (already-validated) raw excludePaths block into the typed ExcludePaths.\n // webpieces-disable no-any-unknown -- `raw` is opaque consumer JSON until narrowed here\n private parseExcludePaths(raw: unknown): ExcludePaths {\n if (typeof raw !== 'object' || raw === null || Array.isArray(raw)) return new ExcludePaths([], []);\n // webpieces-disable no-any-unknown -- validateExcludePaths already proved both are string[]\n const s = raw as Record<string, string[]>;\n const rules = Array.isArray(s['rules']) ? s['rules'].filter(p => typeof p === 'string') : [];\n const guards = Array.isArray(s['guards']) ? s['guards'].filter(p => typeof p === 'string') : [];\n return new ExcludePaths(rules, guards);\n }\n\n // Parse the (already-validated) raw match-rules array into typed MatchRuleConfig[].\n // webpieces-disable no-any-unknown -- validated array; each entry cast to the typed MatchRuleConfig\n private parseMatchRules(raw: unknown): MatchRuleConfig[] {\n if (!Array.isArray(raw)) return [];\n return raw as MatchRuleConfig[];\n }\n\n private buildWebpiecesRulesConfig(\n // webpieces-disable no-any-unknown -- JSON values are opaque until assigned to typed fields\n rawRules: Record<string, Record<string, unknown>>,\n rulesDir: string[],\n ): WebpiecesRulesConfig {\n const typed = new WebpiecesRulesConfig();\n for (const key of Object.keys(rawRules)) {\n // webpieces-disable no-any-unknown -- dynamic key assignment to typed class\n (typed as Record<string, unknown>)[key] = rawRules[key];\n }\n typed.rulesDir = rulesDir;\n return typed;\n }\n\n private normalizeDeprecatedKeys(section: RuleSectionMap): RuleSectionMap {\n const out: RuleSectionMap = {};\n for (const key of Object.keys(section)) {\n out[DEPRECATED_RULE_ALIASES[key] ?? key] = section[key];\n }\n return out;\n }\n\n // Assemble the validation-failure banner. Most of these errors are version skew, not bad config.\n private formatConfigErrorsBanner(errors: string[]): string {\n return (\n `webpieces.config.json has ${errors.length} validation error(s) — fix ALL, then retry:\\n\\n` +\n errors.map(e => ` • ${e}`).join('\\n') +\n `\\n\\n👉 FIX ORDER (do NOT start by deleting keys — that usually deletes VALID config):\\n` +\n ` 1. Run \\`pnpm install\\`. It is ALWAYS allowed through the guard (installer bypass), even ` +\n `while this config is invalid. This is the #1 cause: your installed @webpieces guard is a ` +\n `release BEHIND webpieces.config.json (a dep bump updated the config + lockfile, but ` +\n `node_modules here was never re-installed), so the running validator doesn't know the newer ` +\n `rule names/values yet. \\`pnpm install\\` syncs node_modules to the pinned version.\\n` +\n ` 2. Retry your command. If the errors are gone, you're DONE — do not touch webpieces.config.json.\\n` +\n ` 3. ONLY if an error survives a fresh install is it a genuine typo / removed / renamed rule. ` +\n `Then edit webpieces.config.json (edits to it are ALWAYS allowed) to fix each • above.`\n );\n }\n}\n\n// Temporary migration delegator — consumers migrate to injecting ConfigLoader over follow-up PRs,\n// then this free function is removed. The logic now lives in the injected ConfigLoader class.\nconst configLoaderSvc = new ConfigLoader(new ConfigFile());\n\n// webpieces-disable no-function-outside-class -- temporary back-compat delegator to ConfigLoader; removed once all 118 consumers inject it\nexport function loadAndValidate(cwd: string): LoadedConfig {\n return configLoaderSvc.loadAndValidate(cwd);\n}\n"]}
|
package/src/load-template.js
CHANGED
|
@@ -7,7 +7,7 @@ exports.writeTemplate = writeTemplate;
|
|
|
7
7
|
const tslib_1 = require("tslib");
|
|
8
8
|
const fs = tslib_1.__importStar(require("fs"));
|
|
9
9
|
const path = tslib_1.__importStar(require("path"));
|
|
10
|
-
const
|
|
10
|
+
const di_1 = require("./di");
|
|
11
11
|
const inversify_1 = require("inversify");
|
|
12
12
|
const TEMPLATES_DIR = path.join(__dirname, '..', 'templates');
|
|
13
13
|
const DEFAULT_INSTRUCT_DIR = '.webpieces/instruct-ai';
|
|
@@ -35,7 +35,7 @@ let TemplateWriter = class TemplateWriter {
|
|
|
35
35
|
};
|
|
36
36
|
exports.TemplateWriter = TemplateWriter;
|
|
37
37
|
exports.TemplateWriter = TemplateWriter = tslib_1.__decorate([
|
|
38
|
-
(0,
|
|
38
|
+
(0, di_1.provideSingleton)(),
|
|
39
39
|
(0, inversify_1.injectable)()
|
|
40
40
|
], TemplateWriter);
|
|
41
41
|
// Temporary migration delegators — consumers migrate to injecting TemplateWriter over follow-up PRs.
|
package/src/load-template.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"load-template.js","sourceRoot":"","sources":["../../../../../packages/tooling/rules-config/src/load-template.ts"],"names":[],"mappings":";;;AAqCA,oCAEC;AAED,wDAMC;AAED,sCAMC;;AAvDD,+CAAyB;AACzB,mDAA6B;AAC7B,
|
|
1
|
+
{"version":3,"file":"load-template.js","sourceRoot":"","sources":["../../../../../packages/tooling/rules-config/src/load-template.ts"],"names":[],"mappings":";;;AAqCA,oCAEC;AAED,wDAMC;AAED,sCAMC;;AAvDD,+CAAyB;AACzB,mDAA6B;AAC7B,6BAAwC;AACxC,yCAAuC;AAEvC,MAAM,aAAa,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,EAAE,WAAW,CAAC,CAAC;AAC9D,MAAM,oBAAoB,GAAG,wBAAwB,CAAC;AAEtD;;;GAGG;AAGI,IAAM,cAAc,GAApB,MAAM,cAAc;IACvB,YAAY,CAAC,IAAY;QACrB,OAAO,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,IAAI,CAAC,EAAE,OAAO,CAAC,CAAC;IACpE,CAAC;IAED,sBAAsB,CAAC,aAAqB,EAAE,IAAY,EAAE,cAAsB,oBAAoB;QAClG,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,WAAW,EAAE,IAAI,CAAC,CAAC;QACzD,IAAI,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC;YAAE,OAAO;QAChC,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACtD,EAAE,CAAC,aAAa,CAAC,IAAI,EAAE,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,EAAE,OAAO,CAAC,CAAC;IAC7D,CAAC;IAED,aAAa,CAAC,aAAqB,EAAE,IAAY,EAAE,cAAsB,oBAAoB;QACzF,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,WAAW,EAAE,IAAI,CAAC,CAAC;QACzD,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACtD,EAAE,CAAC,aAAa,CAAC,IAAI,EAAE,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,EAAE,OAAO,CAAC,CAAC;QACzD,OAAO,IAAI,CAAC;IAChB,CAAC;CACJ,CAAA;AAlBY,wCAAc;yBAAd,cAAc;IAF1B,IAAA,qBAAgB,GAAE;IAClB,IAAA,sBAAU,GAAE;GACA,cAAc,CAkB1B;AAED,qGAAqG;AACrG,MAAM,iBAAiB,GAAG,IAAI,cAAc,EAAE,CAAC;AAE/C,SAAgB,YAAY,CAAC,IAAY;IACrC,OAAO,iBAAiB,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC;AAChD,CAAC;AAED,SAAgB,sBAAsB,CAClC,aAAqB,EACrB,IAAY,EACZ,cAAsB,oBAAoB;IAE1C,iBAAiB,CAAC,sBAAsB,CAAC,aAAa,EAAE,IAAI,EAAE,WAAW,CAAC,CAAC;AAC/E,CAAC;AAED,SAAgB,aAAa,CACzB,aAAqB,EACrB,IAAY,EACZ,cAAsB,oBAAoB;IAE1C,OAAO,iBAAiB,CAAC,aAAa,CAAC,aAAa,EAAE,IAAI,EAAE,WAAW,CAAC,CAAC;AAC7E,CAAC","sourcesContent":["import * as fs from 'fs';\nimport * as path from 'path';\nimport { provideSingleton } from './di';\nimport { injectable } from 'inversify';\n\nconst TEMPLATES_DIR = path.join(__dirname, '..', 'templates');\nconst DEFAULT_INSTRUCT_DIR = '.webpieces/instruct-ai';\n\n/**\n * Writes the AI-facing instruct-ai template docs under `<workspaceRoot>/.webpieces/instruct-ai/`.\n * `@provideSingleton` so it can be injected and appear in the rules-config DI design.\n */\n@provideSingleton()\n@injectable()\nexport class TemplateWriter {\n loadTemplate(name: string): string {\n return fs.readFileSync(path.join(TEMPLATES_DIR, name), 'utf-8');\n }\n\n writeTemplateIfMissing(workspaceRoot: string, name: string, instructDir: string = DEFAULT_INSTRUCT_DIR): void {\n const dest = path.join(workspaceRoot, instructDir, name);\n if (fs.existsSync(dest)) return;\n fs.mkdirSync(path.dirname(dest), { recursive: true });\n fs.writeFileSync(dest, this.loadTemplate(name), 'utf-8');\n }\n\n writeTemplate(workspaceRoot: string, name: string, instructDir: string = DEFAULT_INSTRUCT_DIR): string {\n const dest = path.join(workspaceRoot, instructDir, name);\n fs.mkdirSync(path.dirname(dest), { recursive: true });\n fs.writeFileSync(dest, this.loadTemplate(name), 'utf-8');\n return dest;\n }\n}\n\n// Temporary migration delegators — consumers migrate to injecting TemplateWriter over follow-up PRs.\nconst templateWriterSvc = new TemplateWriter();\n\nexport function loadTemplate(name: string): string {\n return templateWriterSvc.loadTemplate(name);\n}\n\nexport function writeTemplateIfMissing(\n workspaceRoot: string,\n name: string,\n instructDir: string = DEFAULT_INSTRUCT_DIR,\n): void {\n templateWriterSvc.writeTemplateIfMissing(workspaceRoot, name, instructDir);\n}\n\nexport function writeTemplate(\n workspaceRoot: string,\n name: string,\n instructDir: string = DEFAULT_INSTRUCT_DIR,\n): string {\n return templateWriterSvc.writeTemplate(workspaceRoot, name, instructDir);\n}\n"]}
|
package/src/main-sync-status.js
CHANGED
|
@@ -18,7 +18,7 @@ const tslib_1 = require("tslib");
|
|
|
18
18
|
const child_process_1 = require("child_process");
|
|
19
19
|
const fs = tslib_1.__importStar(require("fs"));
|
|
20
20
|
const path = tslib_1.__importStar(require("path"));
|
|
21
|
-
const
|
|
21
|
+
const di_1 = require("./di");
|
|
22
22
|
const inversify_1 = require("inversify");
|
|
23
23
|
const constants_1 = require("./constants");
|
|
24
24
|
const to_error_1 = require("./to-error");
|
|
@@ -286,7 +286,7 @@ let MainSyncStatusService = class MainSyncStatusService {
|
|
|
286
286
|
};
|
|
287
287
|
exports.MainSyncStatusService = MainSyncStatusService;
|
|
288
288
|
exports.MainSyncStatusService = MainSyncStatusService = tslib_1.__decorate([
|
|
289
|
-
(0,
|
|
289
|
+
(0, di_1.provideSingleton)(),
|
|
290
290
|
(0, inversify_1.injectable)()
|
|
291
291
|
], MainSyncStatusService);
|
|
292
292
|
// Temporary migration delegators to MainSyncStatusService — removed once consumers inject it.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"main-sync-status.js","sourceRoot":"","sources":["../../../../../packages/tooling/rules-config/src/main-sync-status.ts"],"names":[],"mappings":"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","sourcesContent":["import { spawnSync } from 'child_process';\nimport * as fs from 'fs';\nimport * as path from 'path';\nimport { provideSingleton } from '@webpieces/core-context';\nimport { injectable } from 'inversify';\n\nimport { WEBPIECES_TMP_DIR } from './constants';\nimport { toError } from './to-error';\n\n// Shared \"is my feature branch healthy relative to origin/main?\" state. The SLOW signals (git fetch +\n// merge-base + same-file-overlap + a merged-PR lookup) are computed by the ai-hook-rules refresher in a\n// DETACHED background process; the feature-branch-guard only READS this cached file. pr-gate's merge\n// flow also writes it synchronously after a merge. Lives here (the shared dep of both).\n\n// How long an `inprocess` refresher lock may sit before a new refresher assumes the prior run hung.\nexport const DEFAULT_HANG_TIMEOUT_MINUTES = 5;\n\nconst MAIN_SYNC_STATUS_FILE = 'main-sync-status.json';\nconst MAIN_SYNC_LOCK_FILE = 'main-sync.lock.json';\n\nconst LOCK_STATE_INPROCESS = 'inprocess';\nconst LOCK_STATE_FINISHED = 'finished';\n\n// Data-only (per CLAUDE.md, classes for data).\nexport class MainSyncStatus {\n branch: string;\n branchAlreadyMerged: boolean;\n mergedPr: string;\n hasForkPoint: boolean;\n forkPoint: string | null;\n originMain: string;\n featureHead: string;\n conflict: boolean;\n conflictFiles: string[];\n timestamp: string;\n // An OPEN (not merged) PR tracking this branch, if any — '' = none or not-yet-known. Advisory.\n // Kept OUT of the positional constructor (a defaulted field) so existing call sites don't churn.\n openPr: string = '';\n\n constructor(\n branch: string,\n branchAlreadyMerged: boolean,\n mergedPr: string,\n hasForkPoint: boolean,\n forkPoint: string | null,\n originMain: string,\n featureHead: string,\n conflict: boolean,\n conflictFiles: string[],\n timestamp: string,\n ) {\n this.branch = branch;\n this.branchAlreadyMerged = branchAlreadyMerged;\n this.mergedPr = mergedPr;\n this.hasForkPoint = hasForkPoint;\n this.forkPoint = forkPoint;\n this.originMain = originMain;\n this.featureHead = featureHead;\n this.conflict = conflict;\n this.conflictFiles = conflictFiles;\n this.timestamp = timestamp;\n }\n}\n\n// Concurrency state machine for the detached refresher. `started` is epoch ms. `pid` is the refresher\n// process's pid (0 = unknown) — used so a KILLED refresher doesn't wedge `inprocess` for the timeout.\nexport class MainSyncLock {\n state: string;\n started: number;\n pid: number;\n\n constructor(state: string, started: number, pid: number = 0) {\n this.state = state;\n this.started = started;\n this.pid = pid;\n }\n}\n\n// Raw JSON shapes for the cast at the parse boundary.\ninterface RawStatus {\n branch?: string;\n branchAlreadyMerged?: boolean;\n mergedPr?: string;\n hasForkPoint?: boolean;\n forkPoint?: string | null;\n originMain?: string;\n featureHead?: string;\n conflict?: boolean;\n conflictFiles?: string[];\n timestamp?: string;\n openPr?: string;\n}\n\ninterface RawLock {\n state?: string;\n started?: number;\n pid?: number;\n}\n\n// Result of a captured git/gh invocation: ok=false on spawn failure or non-zero exit.\ninterface CmdCapture {\n ok: boolean;\n out: string;\n}\n\n/**\n * Reads/writes the main-sync cache + lock and computes the slow \"is my branch healthy vs origin/main?\"\n * status. `@provideSingleton` so it's injectable and drawn in the rules-config DI design.\n */\n@provideSingleton()\n@injectable()\nexport class MainSyncStatusService {\n mainSyncStatusPath(repoRoot: string): string {\n return path.join(repoRoot, WEBPIECES_TMP_DIR, MAIN_SYNC_STATUS_FILE);\n }\n\n mainSyncLockPath(repoRoot: string): string {\n return path.join(repoRoot, WEBPIECES_TMP_DIR, MAIN_SYNC_LOCK_FILE);\n }\n\n // Pure read — any error (missing file, malformed JSON) returns null so the guard fails OPEN.\n readMainSyncStatus(repoRoot: string): MainSyncStatus | null {\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions\n try {\n const statusPath = this.mainSyncStatusPath(repoRoot);\n if (!fs.existsSync(statusPath)) return null;\n const raw = JSON.parse(fs.readFileSync(statusPath, 'utf8')) as RawStatus;\n const status = new MainSyncStatus(\n raw.branch ?? '',\n raw.branchAlreadyMerged ?? false,\n raw.mergedPr ?? '',\n raw.hasForkPoint ?? true,\n raw.forkPoint ?? null,\n raw.originMain ?? '',\n raw.featureHead ?? '',\n raw.conflict ?? false,\n raw.conflictFiles ?? [],\n raw.timestamp ?? '',\n );\n status.openPr = raw.openPr ?? '';\n return status;\n } catch (err: unknown) {\n const error = toError(err);\n void error;\n return null;\n }\n }\n\n writeMainSyncStatus(repoRoot: string, status: MainSyncStatus): void {\n const statusPath = this.mainSyncStatusPath(repoRoot);\n this.ensureDir(statusPath);\n fs.writeFileSync(statusPath, JSON.stringify(status, null, 2) + '\\n');\n }\n\n readMainSyncLock(repoRoot: string): MainSyncLock | null {\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions\n try {\n const lockPath = this.mainSyncLockPath(repoRoot);\n if (!fs.existsSync(lockPath)) return null;\n const raw = JSON.parse(fs.readFileSync(lockPath, 'utf8')) as RawLock;\n return new MainSyncLock(raw.state ?? LOCK_STATE_FINISHED, raw.started ?? 0, raw.pid ?? 0);\n } catch (err: unknown) {\n const error = toError(err);\n void error;\n return null;\n }\n }\n\n writeMainSyncLock(repoRoot: string, lock: MainSyncLock): void {\n const lockPath = this.mainSyncLockPath(repoRoot);\n this.ensureDir(lockPath);\n fs.writeFileSync(lockPath, JSON.stringify(lock, null, 2) + '\\n');\n }\n\n // A lock is stale (prior refresher assumed hung) once `inprocess` longer than hangTimeoutMinutes.\n isLockStale(lock: MainSyncLock, hangTimeoutMinutes: number, now: number = Date.now()): boolean {\n return now - lock.started > hangTimeoutMinutes * 60 * 1000;\n }\n\n // True when another refresher is actively running and we should NOT start a second one.\n isRefreshInProgress(repoRoot: string, hangTimeoutMinutes: number, now: number = Date.now()): boolean {\n const lock = this.readMainSyncLock(repoRoot);\n if (!lock) return false;\n if (lock.state !== LOCK_STATE_INPROCESS) return false;\n if (this.isLockStale(lock, hangTimeoutMinutes, now)) return false;\n return this.isProcessAlive(lock.pid);\n }\n\n inProcessLock(now: number = Date.now(), pid: number = process.pid): MainSyncLock {\n return new MainSyncLock(LOCK_STATE_INPROCESS, now, pid);\n }\n\n finishedLock(started: number): MainSyncLock {\n return new MainSyncLock(LOCK_STATE_FINISHED, started, 0);\n }\n\n /**\n * The SLOW path, run only inside the detached refresher. Computes every cached signal the\n * feature-branch-guard needs. Never run on the hook's blocking path.\n */\n // webpieces-disable max-lines-new-methods -- one cohesive slow-path computation\n computeMainSyncStatus(repoRoot: string): MainSyncStatus {\n const branch = this.gitBranch(repoRoot);\n const mergedPr = this.detectMergedPr(repoRoot, branch);\n const openPr = this.detectOpenPr(repoRoot, branch);\n\n // Best-effort network refresh; offline just means we evaluate against the last-fetched ref.\n spawnSync('git', ['fetch', 'origin', 'main'], { cwd: repoRoot, stdio: 'ignore' });\n\n const head = this.capture(repoRoot, 'git', ['rev-parse', 'HEAD']);\n const originMain = this.capture(repoRoot, 'git', ['rev-parse', 'origin/main']);\n const featureHead = head.ok ? head.out : '';\n if (!head.ok || !originMain.ok) {\n const status = this.benignStatus(branch, featureHead);\n status.branchAlreadyMerged = mergedPr !== '';\n status.mergedPr = mergedPr;\n status.openPr = openPr;\n return status;\n }\n\n const forkPoint = this.capture(repoRoot, 'git', ['merge-base', 'origin/main', 'HEAD']);\n if (!forkPoint.ok || forkPoint.out === '') {\n const noFork = new MainSyncStatus(branch, mergedPr !== '', mergedPr, false, null, originMain.out, featureHead, false, [], new Date().toISOString());\n noFork.openPr = openPr;\n return noFork;\n }\n\n const featureFiles = new Set(this.featureChangedFiles(repoRoot, forkPoint.out));\n const mainFiles = this.changedFiles(repoRoot, forkPoint.out, 'origin/main');\n const conflictFiles = mainFiles.filter((file: string): boolean => featureFiles.has(file));\n\n const status = new MainSyncStatus(\n branch,\n mergedPr !== '',\n mergedPr,\n true,\n forkPoint.out,\n originMain.out,\n featureHead,\n conflictFiles.length > 0,\n conflictFiles,\n new Date().toISOString(),\n );\n status.openPr = openPr;\n return status;\n }\n\n // The recovery steps when there is no fork point with origin/main (a bad merge of main into branch).\n squashRecoverySteps(currentBranch: string): string[] {\n return [\n '1. Fetch latest main: git fetch origin main',\n `2. New branch off origin/main: git checkout -b ${currentBranch}-v2 origin/main`,\n `3. Squash-merge old branch: git merge --squash ${currentBranch}`,\n `4. Commit the squash: git add -A && git commit -m \"Squashed from ${currentBranch}\"`,\n '5. If a PR exists: open a NEW PR for the -v2 branch and close the old one.',\n ];\n }\n\n // Synchronously stamp a clean \"up to date with main\" status — call right after a successful merge.\n stampCleanMainSyncStatus(repoRoot: string): void {\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions\n try {\n const branch = this.gitBranch(repoRoot);\n const originMain = this.capture(repoRoot, 'git', ['rev-parse', 'origin/main']);\n const featureHead = this.capture(repoRoot, 'git', ['rev-parse', 'HEAD']);\n if (!originMain.ok || !featureHead.ok) return;\n const status = new MainSyncStatus(\n branch, false, '', true, originMain.out, originMain.out, featureHead.out, false, [], new Date().toISOString(),\n );\n this.writeMainSyncStatus(repoRoot, status);\n } catch (err: unknown) {\n const error = toError(err);\n void error;\n }\n }\n\n private ensureDir(filePath: string): void {\n fs.mkdirSync(path.dirname(filePath), { recursive: true });\n }\n\n // Liveness probe: is `pid` still a running process? pid <= 0 (unknown) → assume alive.\n private isProcessAlive(pid: number): boolean {\n if (pid <= 0) return true;\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions\n try {\n process.kill(pid, 0);\n return true;\n } catch (err: unknown) {\n const error = toError(err);\n return !error.message.includes('ESRCH');\n }\n }\n\n // Run a command capturing trimmed stdout; ok=false on spawn failure or non-zero exit.\n private capture(repoRoot: string, cmd: string, args: string[]): CmdCapture {\n const result = spawnSync(cmd, args, { cwd: repoRoot, encoding: 'utf8' });\n if (result.status !== 0 || typeof result.stdout !== 'string') return { ok: false, out: '' };\n return { ok: true, out: result.stdout.trim() };\n }\n\n // The actual checked-out branch in repoRoot — cwd-correct so the cache's `branch` label matches.\n private gitBranch(repoRoot: string): string {\n const result = this.capture(repoRoot, 'git', ['rev-parse', '--abbrev-ref', 'HEAD']);\n return result.ok ? result.out : '';\n }\n\n private changedFiles(repoRoot: string, base: string, head: string): string[] {\n const result = this.capture(repoRoot, 'git', ['diff', '--name-only', base, head]);\n if (!result.ok || result.out === '') return [];\n return result.out.split('\\n').map((line: string): string => line.trim()).filter((line: string): boolean => line.length > 0);\n }\n\n // Every file this feature branch has touched since the fork point — committed AND still in the\n // working tree (staged / unstaged / untracked), so a conflict is visible WHILE editing.\n private featureChangedFiles(repoRoot: string, forkPoint: string): string[] {\n const out = new Set<string>();\n const add = (args: string[]): void => {\n const r = this.capture(repoRoot, 'git', args);\n if (!r.ok || r.out === '') return;\n for (const line of r.out.split('\\n')) {\n const f = line.trim();\n if (f.length > 0) out.add(f);\n }\n };\n add(['diff', '--name-only', forkPoint, 'HEAD']); // committed since the fork point\n add(['diff', '--name-only', 'HEAD']); // unstaged working-tree edits\n add(['diff', '--name-only', '--cached', 'HEAD']); // staged edits\n add(['ls-files', '--others', '--exclude-standard']); // untracked new files (respects .gitignore)\n return [...out];\n }\n\n // Has this feature branch already been merged into main? Reliable signal: a MERGED PR exists.\n private detectMergedPr(repoRoot: string, branch: string): string {\n if (!branch || branch === 'main') return '';\n const result = this.capture(repoRoot, 'gh', ['pr', 'list', '--head', branch, '--state', 'merged', '--json', 'number', '--jq', '.[0].number']);\n return result.ok ? result.out : '';\n }\n\n // An OPEN PR tracking this branch, if any. Best-effort/advisory.\n private detectOpenPr(repoRoot: string, branch: string): string {\n if (!branch || branch === 'main') return '';\n const result = this.capture(repoRoot, 'gh', ['pr', 'list', '--head', branch, '--state', 'open', '--json', 'number', '--jq', '.[0].number']);\n return result.ok ? result.out : '';\n }\n\n // A benign status that never blocks — used when origin/main can't be resolved.\n private benignStatus(branch: string, featureHead: string): MainSyncStatus {\n return new MainSyncStatus(branch, false, '', true, null, '', featureHead, false, [], new Date().toISOString());\n }\n}\n\n// Temporary migration delegators to MainSyncStatusService — removed once consumers inject it.\nconst mainSyncSvc = new MainSyncStatusService();\n\n// webpieces-disable no-function-outside-class -- temporary back-compat delegator to MainSyncStatusService; removed once consumers inject it\nexport function mainSyncStatusPath(repoRoot: string): string { return mainSyncSvc.mainSyncStatusPath(repoRoot); }\n// webpieces-disable no-function-outside-class -- temporary back-compat delegator to MainSyncStatusService; removed once consumers inject it\nexport function mainSyncLockPath(repoRoot: string): string { return mainSyncSvc.mainSyncLockPath(repoRoot); }\n// webpieces-disable no-function-outside-class -- temporary back-compat delegator to MainSyncStatusService; removed once consumers inject it\nexport function readMainSyncStatus(repoRoot: string): MainSyncStatus | null { return mainSyncSvc.readMainSyncStatus(repoRoot); }\n// webpieces-disable no-function-outside-class -- temporary back-compat delegator to MainSyncStatusService; removed once consumers inject it\nexport function writeMainSyncStatus(repoRoot: string, status: MainSyncStatus): void { mainSyncSvc.writeMainSyncStatus(repoRoot, status); }\n// webpieces-disable no-function-outside-class -- temporary back-compat delegator to MainSyncStatusService; removed once consumers inject it\nexport function readMainSyncLock(repoRoot: string): MainSyncLock | null { return mainSyncSvc.readMainSyncLock(repoRoot); }\n// webpieces-disable no-function-outside-class -- temporary back-compat delegator to MainSyncStatusService; removed once consumers inject it\nexport function writeMainSyncLock(repoRoot: string, lock: MainSyncLock): void { mainSyncSvc.writeMainSyncLock(repoRoot, lock); }\n// webpieces-disable no-function-outside-class -- temporary back-compat delegator to MainSyncStatusService; removed once consumers inject it\nexport function isLockStale(lock: MainSyncLock, hangTimeoutMinutes: number, now: number = Date.now()): boolean { return mainSyncSvc.isLockStale(lock, hangTimeoutMinutes, now); }\n// webpieces-disable no-function-outside-class -- temporary back-compat delegator to MainSyncStatusService; removed once consumers inject it\nexport function isRefreshInProgress(repoRoot: string, hangTimeoutMinutes: number, now: number = Date.now()): boolean { return mainSyncSvc.isRefreshInProgress(repoRoot, hangTimeoutMinutes, now); }\n// webpieces-disable no-function-outside-class -- temporary back-compat delegator to MainSyncStatusService; removed once consumers inject it\nexport function inProcessLock(now: number = Date.now(), pid: number = process.pid): MainSyncLock { return mainSyncSvc.inProcessLock(now, pid); }\n// webpieces-disable no-function-outside-class -- temporary back-compat delegator to MainSyncStatusService; removed once consumers inject it\nexport function finishedLock(started: number): MainSyncLock { return mainSyncSvc.finishedLock(started); }\n// webpieces-disable no-function-outside-class -- temporary back-compat delegator to MainSyncStatusService; removed once consumers inject it\nexport function computeMainSyncStatus(repoRoot: string): MainSyncStatus { return mainSyncSvc.computeMainSyncStatus(repoRoot); }\n// webpieces-disable no-function-outside-class -- temporary back-compat delegator to MainSyncStatusService; removed once consumers inject it\nexport function squashRecoverySteps(currentBranch: string): string[] { return mainSyncSvc.squashRecoverySteps(currentBranch); }\n// webpieces-disable no-function-outside-class -- temporary back-compat delegator to MainSyncStatusService; removed once consumers inject it\nexport function stampCleanMainSyncStatus(repoRoot: string): void { mainSyncSvc.stampCleanMainSyncStatus(repoRoot); }\n"]}
|
|
1
|
+
{"version":3,"file":"main-sync-status.js","sourceRoot":"","sources":["../../../../../packages/tooling/rules-config/src/main-sync-status.ts"],"names":[],"mappings":"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","sourcesContent":["import { spawnSync } from 'child_process';\nimport * as fs from 'fs';\nimport * as path from 'path';\nimport { provideSingleton } from './di';\nimport { injectable } from 'inversify';\n\nimport { WEBPIECES_TMP_DIR } from './constants';\nimport { toError } from './to-error';\n\n// Shared \"is my feature branch healthy relative to origin/main?\" state. The SLOW signals (git fetch +\n// merge-base + same-file-overlap + a merged-PR lookup) are computed by the ai-hook-rules refresher in a\n// DETACHED background process; the feature-branch-guard only READS this cached file. pr-gate's merge\n// flow also writes it synchronously after a merge. Lives here (the shared dep of both).\n\n// How long an `inprocess` refresher lock may sit before a new refresher assumes the prior run hung.\nexport const DEFAULT_HANG_TIMEOUT_MINUTES = 5;\n\nconst MAIN_SYNC_STATUS_FILE = 'main-sync-status.json';\nconst MAIN_SYNC_LOCK_FILE = 'main-sync.lock.json';\n\nconst LOCK_STATE_INPROCESS = 'inprocess';\nconst LOCK_STATE_FINISHED = 'finished';\n\n// Data-only (per CLAUDE.md, classes for data).\nexport class MainSyncStatus {\n branch: string;\n branchAlreadyMerged: boolean;\n mergedPr: string;\n hasForkPoint: boolean;\n forkPoint: string | null;\n originMain: string;\n featureHead: string;\n conflict: boolean;\n conflictFiles: string[];\n timestamp: string;\n // An OPEN (not merged) PR tracking this branch, if any — '' = none or not-yet-known. Advisory.\n // Kept OUT of the positional constructor (a defaulted field) so existing call sites don't churn.\n openPr: string = '';\n\n constructor(\n branch: string,\n branchAlreadyMerged: boolean,\n mergedPr: string,\n hasForkPoint: boolean,\n forkPoint: string | null,\n originMain: string,\n featureHead: string,\n conflict: boolean,\n conflictFiles: string[],\n timestamp: string,\n ) {\n this.branch = branch;\n this.branchAlreadyMerged = branchAlreadyMerged;\n this.mergedPr = mergedPr;\n this.hasForkPoint = hasForkPoint;\n this.forkPoint = forkPoint;\n this.originMain = originMain;\n this.featureHead = featureHead;\n this.conflict = conflict;\n this.conflictFiles = conflictFiles;\n this.timestamp = timestamp;\n }\n}\n\n// Concurrency state machine for the detached refresher. `started` is epoch ms. `pid` is the refresher\n// process's pid (0 = unknown) — used so a KILLED refresher doesn't wedge `inprocess` for the timeout.\nexport class MainSyncLock {\n state: string;\n started: number;\n pid: number;\n\n constructor(state: string, started: number, pid: number = 0) {\n this.state = state;\n this.started = started;\n this.pid = pid;\n }\n}\n\n// Raw JSON shapes for the cast at the parse boundary.\ninterface RawStatus {\n branch?: string;\n branchAlreadyMerged?: boolean;\n mergedPr?: string;\n hasForkPoint?: boolean;\n forkPoint?: string | null;\n originMain?: string;\n featureHead?: string;\n conflict?: boolean;\n conflictFiles?: string[];\n timestamp?: string;\n openPr?: string;\n}\n\ninterface RawLock {\n state?: string;\n started?: number;\n pid?: number;\n}\n\n// Result of a captured git/gh invocation: ok=false on spawn failure or non-zero exit.\ninterface CmdCapture {\n ok: boolean;\n out: string;\n}\n\n/**\n * Reads/writes the main-sync cache + lock and computes the slow \"is my branch healthy vs origin/main?\"\n * status. `@provideSingleton` so it's injectable and drawn in the rules-config DI design.\n */\n@provideSingleton()\n@injectable()\nexport class MainSyncStatusService {\n mainSyncStatusPath(repoRoot: string): string {\n return path.join(repoRoot, WEBPIECES_TMP_DIR, MAIN_SYNC_STATUS_FILE);\n }\n\n mainSyncLockPath(repoRoot: string): string {\n return path.join(repoRoot, WEBPIECES_TMP_DIR, MAIN_SYNC_LOCK_FILE);\n }\n\n // Pure read — any error (missing file, malformed JSON) returns null so the guard fails OPEN.\n readMainSyncStatus(repoRoot: string): MainSyncStatus | null {\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions\n try {\n const statusPath = this.mainSyncStatusPath(repoRoot);\n if (!fs.existsSync(statusPath)) return null;\n const raw = JSON.parse(fs.readFileSync(statusPath, 'utf8')) as RawStatus;\n const status = new MainSyncStatus(\n raw.branch ?? '',\n raw.branchAlreadyMerged ?? false,\n raw.mergedPr ?? '',\n raw.hasForkPoint ?? true,\n raw.forkPoint ?? null,\n raw.originMain ?? '',\n raw.featureHead ?? '',\n raw.conflict ?? false,\n raw.conflictFiles ?? [],\n raw.timestamp ?? '',\n );\n status.openPr = raw.openPr ?? '';\n return status;\n } catch (err: unknown) {\n const error = toError(err);\n void error;\n return null;\n }\n }\n\n writeMainSyncStatus(repoRoot: string, status: MainSyncStatus): void {\n const statusPath = this.mainSyncStatusPath(repoRoot);\n this.ensureDir(statusPath);\n fs.writeFileSync(statusPath, JSON.stringify(status, null, 2) + '\\n');\n }\n\n readMainSyncLock(repoRoot: string): MainSyncLock | null {\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions\n try {\n const lockPath = this.mainSyncLockPath(repoRoot);\n if (!fs.existsSync(lockPath)) return null;\n const raw = JSON.parse(fs.readFileSync(lockPath, 'utf8')) as RawLock;\n return new MainSyncLock(raw.state ?? LOCK_STATE_FINISHED, raw.started ?? 0, raw.pid ?? 0);\n } catch (err: unknown) {\n const error = toError(err);\n void error;\n return null;\n }\n }\n\n writeMainSyncLock(repoRoot: string, lock: MainSyncLock): void {\n const lockPath = this.mainSyncLockPath(repoRoot);\n this.ensureDir(lockPath);\n fs.writeFileSync(lockPath, JSON.stringify(lock, null, 2) + '\\n');\n }\n\n // A lock is stale (prior refresher assumed hung) once `inprocess` longer than hangTimeoutMinutes.\n isLockStale(lock: MainSyncLock, hangTimeoutMinutes: number, now: number = Date.now()): boolean {\n return now - lock.started > hangTimeoutMinutes * 60 * 1000;\n }\n\n // True when another refresher is actively running and we should NOT start a second one.\n isRefreshInProgress(repoRoot: string, hangTimeoutMinutes: number, now: number = Date.now()): boolean {\n const lock = this.readMainSyncLock(repoRoot);\n if (!lock) return false;\n if (lock.state !== LOCK_STATE_INPROCESS) return false;\n if (this.isLockStale(lock, hangTimeoutMinutes, now)) return false;\n return this.isProcessAlive(lock.pid);\n }\n\n inProcessLock(now: number = Date.now(), pid: number = process.pid): MainSyncLock {\n return new MainSyncLock(LOCK_STATE_INPROCESS, now, pid);\n }\n\n finishedLock(started: number): MainSyncLock {\n return new MainSyncLock(LOCK_STATE_FINISHED, started, 0);\n }\n\n /**\n * The SLOW path, run only inside the detached refresher. Computes every cached signal the\n * feature-branch-guard needs. Never run on the hook's blocking path.\n */\n // webpieces-disable max-lines-new-methods -- one cohesive slow-path computation\n computeMainSyncStatus(repoRoot: string): MainSyncStatus {\n const branch = this.gitBranch(repoRoot);\n const mergedPr = this.detectMergedPr(repoRoot, branch);\n const openPr = this.detectOpenPr(repoRoot, branch);\n\n // Best-effort network refresh; offline just means we evaluate against the last-fetched ref.\n spawnSync('git', ['fetch', 'origin', 'main'], { cwd: repoRoot, stdio: 'ignore' });\n\n const head = this.capture(repoRoot, 'git', ['rev-parse', 'HEAD']);\n const originMain = this.capture(repoRoot, 'git', ['rev-parse', 'origin/main']);\n const featureHead = head.ok ? head.out : '';\n if (!head.ok || !originMain.ok) {\n const status = this.benignStatus(branch, featureHead);\n status.branchAlreadyMerged = mergedPr !== '';\n status.mergedPr = mergedPr;\n status.openPr = openPr;\n return status;\n }\n\n const forkPoint = this.capture(repoRoot, 'git', ['merge-base', 'origin/main', 'HEAD']);\n if (!forkPoint.ok || forkPoint.out === '') {\n const noFork = new MainSyncStatus(branch, mergedPr !== '', mergedPr, false, null, originMain.out, featureHead, false, [], new Date().toISOString());\n noFork.openPr = openPr;\n return noFork;\n }\n\n const featureFiles = new Set(this.featureChangedFiles(repoRoot, forkPoint.out));\n const mainFiles = this.changedFiles(repoRoot, forkPoint.out, 'origin/main');\n const conflictFiles = mainFiles.filter((file: string): boolean => featureFiles.has(file));\n\n const status = new MainSyncStatus(\n branch,\n mergedPr !== '',\n mergedPr,\n true,\n forkPoint.out,\n originMain.out,\n featureHead,\n conflictFiles.length > 0,\n conflictFiles,\n new Date().toISOString(),\n );\n status.openPr = openPr;\n return status;\n }\n\n // The recovery steps when there is no fork point with origin/main (a bad merge of main into branch).\n squashRecoverySteps(currentBranch: string): string[] {\n return [\n '1. Fetch latest main: git fetch origin main',\n `2. New branch off origin/main: git checkout -b ${currentBranch}-v2 origin/main`,\n `3. Squash-merge old branch: git merge --squash ${currentBranch}`,\n `4. Commit the squash: git add -A && git commit -m \"Squashed from ${currentBranch}\"`,\n '5. If a PR exists: open a NEW PR for the -v2 branch and close the old one.',\n ];\n }\n\n // Synchronously stamp a clean \"up to date with main\" status — call right after a successful merge.\n stampCleanMainSyncStatus(repoRoot: string): void {\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions\n try {\n const branch = this.gitBranch(repoRoot);\n const originMain = this.capture(repoRoot, 'git', ['rev-parse', 'origin/main']);\n const featureHead = this.capture(repoRoot, 'git', ['rev-parse', 'HEAD']);\n if (!originMain.ok || !featureHead.ok) return;\n const status = new MainSyncStatus(\n branch, false, '', true, originMain.out, originMain.out, featureHead.out, false, [], new Date().toISOString(),\n );\n this.writeMainSyncStatus(repoRoot, status);\n } catch (err: unknown) {\n const error = toError(err);\n void error;\n }\n }\n\n private ensureDir(filePath: string): void {\n fs.mkdirSync(path.dirname(filePath), { recursive: true });\n }\n\n // Liveness probe: is `pid` still a running process? pid <= 0 (unknown) → assume alive.\n private isProcessAlive(pid: number): boolean {\n if (pid <= 0) return true;\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions\n try {\n process.kill(pid, 0);\n return true;\n } catch (err: unknown) {\n const error = toError(err);\n return !error.message.includes('ESRCH');\n }\n }\n\n // Run a command capturing trimmed stdout; ok=false on spawn failure or non-zero exit.\n private capture(repoRoot: string, cmd: string, args: string[]): CmdCapture {\n const result = spawnSync(cmd, args, { cwd: repoRoot, encoding: 'utf8' });\n if (result.status !== 0 || typeof result.stdout !== 'string') return { ok: false, out: '' };\n return { ok: true, out: result.stdout.trim() };\n }\n\n // The actual checked-out branch in repoRoot — cwd-correct so the cache's `branch` label matches.\n private gitBranch(repoRoot: string): string {\n const result = this.capture(repoRoot, 'git', ['rev-parse', '--abbrev-ref', 'HEAD']);\n return result.ok ? result.out : '';\n }\n\n private changedFiles(repoRoot: string, base: string, head: string): string[] {\n const result = this.capture(repoRoot, 'git', ['diff', '--name-only', base, head]);\n if (!result.ok || result.out === '') return [];\n return result.out.split('\\n').map((line: string): string => line.trim()).filter((line: string): boolean => line.length > 0);\n }\n\n // Every file this feature branch has touched since the fork point — committed AND still in the\n // working tree (staged / unstaged / untracked), so a conflict is visible WHILE editing.\n private featureChangedFiles(repoRoot: string, forkPoint: string): string[] {\n const out = new Set<string>();\n const add = (args: string[]): void => {\n const r = this.capture(repoRoot, 'git', args);\n if (!r.ok || r.out === '') return;\n for (const line of r.out.split('\\n')) {\n const f = line.trim();\n if (f.length > 0) out.add(f);\n }\n };\n add(['diff', '--name-only', forkPoint, 'HEAD']); // committed since the fork point\n add(['diff', '--name-only', 'HEAD']); // unstaged working-tree edits\n add(['diff', '--name-only', '--cached', 'HEAD']); // staged edits\n add(['ls-files', '--others', '--exclude-standard']); // untracked new files (respects .gitignore)\n return [...out];\n }\n\n // Has this feature branch already been merged into main? Reliable signal: a MERGED PR exists.\n private detectMergedPr(repoRoot: string, branch: string): string {\n if (!branch || branch === 'main') return '';\n const result = this.capture(repoRoot, 'gh', ['pr', 'list', '--head', branch, '--state', 'merged', '--json', 'number', '--jq', '.[0].number']);\n return result.ok ? result.out : '';\n }\n\n // An OPEN PR tracking this branch, if any. Best-effort/advisory.\n private detectOpenPr(repoRoot: string, branch: string): string {\n if (!branch || branch === 'main') return '';\n const result = this.capture(repoRoot, 'gh', ['pr', 'list', '--head', branch, '--state', 'open', '--json', 'number', '--jq', '.[0].number']);\n return result.ok ? result.out : '';\n }\n\n // A benign status that never blocks — used when origin/main can't be resolved.\n private benignStatus(branch: string, featureHead: string): MainSyncStatus {\n return new MainSyncStatus(branch, false, '', true, null, '', featureHead, false, [], new Date().toISOString());\n }\n}\n\n// Temporary migration delegators to MainSyncStatusService — removed once consumers inject it.\nconst mainSyncSvc = new MainSyncStatusService();\n\n// webpieces-disable no-function-outside-class -- temporary back-compat delegator to MainSyncStatusService; removed once consumers inject it\nexport function mainSyncStatusPath(repoRoot: string): string { return mainSyncSvc.mainSyncStatusPath(repoRoot); }\n// webpieces-disable no-function-outside-class -- temporary back-compat delegator to MainSyncStatusService; removed once consumers inject it\nexport function mainSyncLockPath(repoRoot: string): string { return mainSyncSvc.mainSyncLockPath(repoRoot); }\n// webpieces-disable no-function-outside-class -- temporary back-compat delegator to MainSyncStatusService; removed once consumers inject it\nexport function readMainSyncStatus(repoRoot: string): MainSyncStatus | null { return mainSyncSvc.readMainSyncStatus(repoRoot); }\n// webpieces-disable no-function-outside-class -- temporary back-compat delegator to MainSyncStatusService; removed once consumers inject it\nexport function writeMainSyncStatus(repoRoot: string, status: MainSyncStatus): void { mainSyncSvc.writeMainSyncStatus(repoRoot, status); }\n// webpieces-disable no-function-outside-class -- temporary back-compat delegator to MainSyncStatusService; removed once consumers inject it\nexport function readMainSyncLock(repoRoot: string): MainSyncLock | null { return mainSyncSvc.readMainSyncLock(repoRoot); }\n// webpieces-disable no-function-outside-class -- temporary back-compat delegator to MainSyncStatusService; removed once consumers inject it\nexport function writeMainSyncLock(repoRoot: string, lock: MainSyncLock): void { mainSyncSvc.writeMainSyncLock(repoRoot, lock); }\n// webpieces-disable no-function-outside-class -- temporary back-compat delegator to MainSyncStatusService; removed once consumers inject it\nexport function isLockStale(lock: MainSyncLock, hangTimeoutMinutes: number, now: number = Date.now()): boolean { return mainSyncSvc.isLockStale(lock, hangTimeoutMinutes, now); }\n// webpieces-disable no-function-outside-class -- temporary back-compat delegator to MainSyncStatusService; removed once consumers inject it\nexport function isRefreshInProgress(repoRoot: string, hangTimeoutMinutes: number, now: number = Date.now()): boolean { return mainSyncSvc.isRefreshInProgress(repoRoot, hangTimeoutMinutes, now); }\n// webpieces-disable no-function-outside-class -- temporary back-compat delegator to MainSyncStatusService; removed once consumers inject it\nexport function inProcessLock(now: number = Date.now(), pid: number = process.pid): MainSyncLock { return mainSyncSvc.inProcessLock(now, pid); }\n// webpieces-disable no-function-outside-class -- temporary back-compat delegator to MainSyncStatusService; removed once consumers inject it\nexport function finishedLock(started: number): MainSyncLock { return mainSyncSvc.finishedLock(started); }\n// webpieces-disable no-function-outside-class -- temporary back-compat delegator to MainSyncStatusService; removed once consumers inject it\nexport function computeMainSyncStatus(repoRoot: string): MainSyncStatus { return mainSyncSvc.computeMainSyncStatus(repoRoot); }\n// webpieces-disable no-function-outside-class -- temporary back-compat delegator to MainSyncStatusService; removed once consumers inject it\nexport function squashRecoverySteps(currentBranch: string): string[] { return mainSyncSvc.squashRecoverySteps(currentBranch); }\n// webpieces-disable no-function-outside-class -- temporary back-compat delegator to MainSyncStatusService; removed once consumers inject it\nexport function stampCleanMainSyncStatus(repoRoot: string): void { mainSyncSvc.stampCleanMainSyncStatus(repoRoot); }\n"]}
|
package/src/repo-root.js
CHANGED
|
@@ -4,7 +4,7 @@ exports.RepoRootFinder = exports.INSTRUCT_AI_DIR = void 0;
|
|
|
4
4
|
const tslib_1 = require("tslib");
|
|
5
5
|
const child_process_1 = require("child_process");
|
|
6
6
|
const path = tslib_1.__importStar(require("path"));
|
|
7
|
-
const
|
|
7
|
+
const di_1 = require("./di");
|
|
8
8
|
const inversify_1 = require("inversify");
|
|
9
9
|
const config_file_1 = require("./config-file");
|
|
10
10
|
const constants_1 = require("./constants");
|
|
@@ -64,7 +64,7 @@ let RepoRootFinder = class RepoRootFinder {
|
|
|
64
64
|
};
|
|
65
65
|
exports.RepoRootFinder = RepoRootFinder;
|
|
66
66
|
exports.RepoRootFinder = RepoRootFinder = tslib_1.__decorate([
|
|
67
|
-
(0,
|
|
67
|
+
(0, di_1.provideSingleton)(),
|
|
68
68
|
(0, inversify_1.injectable)()
|
|
69
69
|
], RepoRootFinder);
|
|
70
70
|
//# sourceMappingURL=repo-root.js.map
|
package/src/repo-root.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"repo-root.js","sourceRoot":"","sources":["../../../../../packages/tooling/rules-config/src/repo-root.ts"],"names":[],"mappings":";;;;AAAA,iDAA0C;AAC1C,mDAA6B;AAC7B,
|
|
1
|
+
{"version":3,"file":"repo-root.js","sourceRoot":"","sources":["../../../../../packages/tooling/rules-config/src/repo-root.ts"],"names":[],"mappings":";;;;AAAA,iDAA0C;AAC1C,mDAA6B;AAC7B,6BAAwC;AACxC,yCAAuC;AAEvC,+CAA+C;AAC/C,2CAAgD;AAEhD,oGAAoG;AACpG,0FAA0F;AAC7E,QAAA,eAAe,GAAG,GAAG,6BAAiB,cAAc,CAAC;AAElE;;;;;;;;;GASG;AAGI,IAAM,cAAc,GAApB,MAAM,cAAc;IACvB;;;;;;;OAOG;IACH,eAAe,CAAC,QAAgB;QAC5B,MAAM,UAAU,GAAG,IAAA,4BAAc,EAAC,QAAQ,CAAC,CAAC;QAC5C,IAAI,UAAU,KAAK,IAAI;YAAE,OAAO,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;QACzD,MAAM,OAAO,GAAG,IAAI,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAC;QAC3C,IAAI,OAAO,KAAK,IAAI;YAAE,OAAO,OAAO,CAAC;QACrC,OAAO,QAAQ,CAAC;IACpB,CAAC;IAED;;;;OAIG;IACH,iBAAiB,CAAC,QAAgB,EAAE,OAAe;QAC/C,OAAO,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,uBAAe,EAAE,OAAO,CAAC,CAAC;IACzD,CAAC;IAED,gGAAgG;IAChG,WAAW,CAAC,QAAgB,EAAE,OAAe;QACzC,OAAO,IAAI,CAAC,iBAAiB,CAAC,IAAI,CAAC,eAAe,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC,CAAC;IAC3E,CAAC;IAED,gGAAgG;IAChG,6FAA6F;IAC7F,8FAA8F;IACtF,WAAW,CAAC,GAAW;QAC3B,MAAM,CAAC,GAAG,IAAA,yBAAS,EAAC,KAAK,EAAE,CAAC,IAAI,EAAE,GAAG,EAAE,WAAW,EAAE,iBAAiB,CAAC,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC,CAAC;QAC9F,IAAI,CAAC,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,IAAI,CAAC;QAChC,MAAM,IAAI,GAAG,CAAC,CAAC,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;QACrC,OAAO,IAAI,KAAK,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC;IACrC,CAAC;CACJ,CAAA;AAxCY,wCAAc;yBAAd,cAAc;IAF1B,IAAA,qBAAgB,GAAE;IAClB,IAAA,sBAAU,GAAE;GACA,cAAc,CAwC1B","sourcesContent":["import { spawnSync } from 'child_process';\nimport * as path from 'path';\nimport { provideSingleton } from './di';\nimport { injectable } from 'inversify';\n\nimport { findConfigFile } from './config-file';\nimport { WEBPIECES_TMP_DIR } from './constants';\n\n// The single instruct-ai home under `.webpieces/`. Kept here (not just in load-template) so callers\n// building AI-facing messages can render the ABSOLUTE doc path from a resolved repo root.\nexport const INSTRUCT_AI_DIR = `${WEBPIECES_TMP_DIR}/instruct-ai`;\n\n/**\n * Resolves the single repo root that owns the `.webpieces/` tree, and renders absolute paths beneath\n * it.\n *\n * `.webpieces/` MUST live at the repo root — never in a CWD-dependent subdirectory. A tool invoked\n * from a subdir (a nested package, a `services/*` app) that naively joined `.webpieces` onto its own\n * cwd would scatter stray `.webpieces` trees across the tree — the exact bug this prevents. Every\n * writer of `.webpieces/...` (logs, instruct-ai docs, sync cache, merge/pr state) MUST anchor its\n * path here rather than at `process.cwd()`.\n */\n@provideSingleton()\n@injectable()\nexport class RepoRootFinder {\n /**\n * The repo root for `startDir`. Resolution order (first hit wins):\n * 1. Directory holding webpieces.config.json — the webpieces workspace root, and the exact\n * anchor the hook runner already uses. Walks UP from startDir, so a subdir resolves to root.\n * 2. git toplevel (`git rev-parse --show-toplevel`) — the repo root when no config is present\n * yet (e.g. the installer runs before webpieces.config.json exists).\n * 3. `startDir` — last resort (git unavailable / not a repo / no config). Best-effort only.\n */\n resolveRepoRoot(startDir: string): string {\n const configPath = findConfigFile(startDir);\n if (configPath !== null) return path.dirname(configPath);\n const gitRoot = this.gitToplevel(startDir);\n if (gitRoot !== null) return gitRoot;\n return startDir;\n }\n\n /**\n * Absolute path to an instruct-ai doc under `repoRoot`. Hand THIS to the AI in a violation/fix\n * message — never a bare `.webpieces/instruct-ai/...` relative path, which an AI whose cwd is a\n * subdirectory would resolve against the wrong directory and fail to open.\n */\n instructAiDocPath(repoRoot: string, docName: string): string {\n return path.join(repoRoot, INSTRUCT_AI_DIR, docName);\n }\n\n /** Absolute instruct-ai doc path resolved directly from `startDir` (resolveRepoRoot + join). */\n docPathFrom(startDir: string, docName: string): string {\n return this.instructAiDocPath(this.resolveRepoRoot(startDir), docName);\n }\n\n // git repo root of `cwd`, or null when cwd is not in a git repo. `status !== 0` is the EXPECTED\n // \"not a repo\" value (spawnSync does not throw on non-zero exit), so we never swallow a real\n // failure with try/catch — a genuine git crash still surfaces. Mirrors runner.ts:gitToplevel.\n private gitToplevel(cwd: string): string | null {\n const r = spawnSync('git', ['-C', cwd, 'rev-parse', '--show-toplevel'], { encoding: 'utf8' });\n if (r.status !== 0) return null;\n const root = (r.stdout ?? '').trim();\n return root !== '' ? root : null;\n }\n}\n"]}
|
package/src/review-json.js
CHANGED
|
@@ -8,7 +8,7 @@ exports.loadReviewJson = loadReviewJson;
|
|
|
8
8
|
const tslib_1 = require("tslib");
|
|
9
9
|
const fs = tslib_1.__importStar(require("fs"));
|
|
10
10
|
const path = tslib_1.__importStar(require("path"));
|
|
11
|
-
const
|
|
11
|
+
const di_1 = require("./di");
|
|
12
12
|
const inversify_1 = require("inversify");
|
|
13
13
|
const constants_1 = require("./constants");
|
|
14
14
|
const inform_ai_error_1 = require("./inform-ai-error");
|
|
@@ -126,7 +126,7 @@ let ReviewJsonService = class ReviewJsonService {
|
|
|
126
126
|
};
|
|
127
127
|
exports.ReviewJsonService = ReviewJsonService;
|
|
128
128
|
exports.ReviewJsonService = ReviewJsonService = tslib_1.__decorate([
|
|
129
|
-
(0,
|
|
129
|
+
(0, di_1.provideSingleton)(),
|
|
130
130
|
(0, inversify_1.injectable)()
|
|
131
131
|
], ReviewJsonService);
|
|
132
132
|
// Temporary migration delegators to ReviewJsonService — removed once consumers inject it.
|
package/src/review-json.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"review-json.js","sourceRoot":"","sources":["../../../../../packages/tooling/rules-config/src/review-json.ts"],"names":[],"mappings":"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","sourcesContent":["import * as fs from 'fs';\nimport * as path from 'path';\nimport { provideSingleton } from '@webpieces/core-context';\nimport { injectable } from 'inversify';\nimport { WEBPIECES_TMP_DIR, PR_REVIEW_DIR } from './constants';\nimport { InformAiError } from './inform-ai-error';\nimport { toError } from './to-error';\n\n// The AI-authored review for a PR. The AI writes this file itself between `wp-start-upsert-pr` (which\n// prints the schema) and `wp-finish-upsert-pr` (which reads it). Data-only (per CLAUDE.md).\nexport class ReviewJson {\n title: string; // human PR title describing the change; used as the `gh pr` title (empty → caller falls back)\n riskScore: number; // 0–100, drives the risk bar\n riskLevel: string; // 'green' | 'yellow' | 'red'\n riskEmoji: string; // '🟢' | '🟡' | '🔴' — derived from riskLevel when omitted\n summary: string; // rendered in the dashboard Summary section\n violations: string[]; // pattern/architecture violations; length = the Pattern Violations count\n risks: string[];\n filesToReview: string[];\n\n constructor(\n title: string,\n riskScore: number,\n riskLevel: string,\n riskEmoji: string,\n summary: string,\n violations: string[],\n risks: string[],\n filesToReview: string[],\n ) {\n this.title = title;\n this.riskScore = riskScore;\n this.riskLevel = riskLevel;\n this.riskEmoji = riskEmoji;\n this.summary = summary;\n this.violations = violations;\n this.risks = risks;\n this.filesToReview = filesToReview;\n }\n}\n\nconst RISK_LEVELS = ['green', 'yellow', 'red'] as const;\nconst EMOJI_FOR_LEVEL: Record<string, string> = { green: '🟢', yellow: '🟡', red: '🔴' };\n\n/** Locates + loads/validates the AI-authored review.json. `@provideSingleton` so it's drawn in the design. */\n@provideSingleton()\n@injectable()\nexport class ReviewJsonService {\n // The per-feature PR working dir: `.webpieces/pr-review/<feature>`.\n prDirFor(repoRoot: string, featureName: string): string {\n return path.join(repoRoot, WEBPIECES_TMP_DIR, PR_REVIEW_DIR, featureName);\n }\n\n // Absolute path of the review.json for a feature — beside pr-body.md, keyed by branch name.\n reviewJsonPath(repoRoot: string, featureName: string): string {\n return path.join(this.prDirFor(repoRoot, featureName), 'review.json');\n }\n\n // Copy-paste schema both commands print (write it / fix it).\n reviewJsonSchemaHint(filePath: string): string {\n return (\n `Write your PR review to:\\n ${filePath}\\n\\n` +\n `with this exact JSON shape (riskEmoji optional — derived from riskLevel):\\n\\n` +\n `{\\n` +\n ` \"title\": \"concise PR title describing the change (imperative, no branch names)\",\\n` +\n ` \"riskScore\": 0, // integer 0–100 (higher = riskier)\\n` +\n ` \"riskLevel\": \"green | yellow | red\",\\n` +\n ` \"summary\": \"5–10 sentence review summary\",\\n` +\n ` \"violations\": [\"pattern/architecture violations you found (empty array if none)\"],\\n` +\n ` \"risks\": [\"notable risks (empty array if none)\"],\\n` +\n ` \"filesToReview\": [\"paths a human should look at (empty array if none)\"]\\n` +\n `}`\n );\n }\n\n /**\n * Load + validate the AI-authored review.json. Throws InformAiError (with the schema) when missing,\n * unparseable, or structurally wrong. Returns a fully-populated ReviewJson on success.\n */\n // webpieces-disable max-lines-new-methods -- one cohesive load+validate pass over the review fields\n loadReviewJson(filePath: string): ReviewJson {\n if (!fs.existsSync(filePath)) {\n throw new InformAiError(\n `Required review.json not found.\\n\\n${this.reviewJsonSchemaHint(filePath)}\\n\\n` +\n `Then re-run: pnpm wp-finish-upsert-pr`,\n );\n }\n\n const raw = this.parseReviewJson(fs.readFileSync(filePath, 'utf8'), filePath);\n if (typeof raw !== 'object' || raw === null || Array.isArray(raw)) {\n throw new InformAiError(`review.json must be a JSON object.\\n\\n${this.reviewJsonSchemaHint(filePath)}`);\n }\n\n const errors: string[] = [];\n\n const riskScore = raw['riskScore'];\n if (typeof riskScore !== 'number' || !Number.isFinite(riskScore) || riskScore < 0 || riskScore > 100) {\n errors.push(`\"riskScore\" must be a number 0–100, got ${JSON.stringify(riskScore)}.`);\n }\n\n const riskLevel = raw['riskLevel'];\n if (typeof riskLevel !== 'string' || !RISK_LEVELS.includes(riskLevel as typeof RISK_LEVELS[number])) {\n errors.push(`\"riskLevel\" must be one of: ${RISK_LEVELS.join(', ')}.`);\n }\n\n const title = typeof raw['title'] === 'string' ? (raw['title'] as string).trim() : '';\n if (title === '') {\n errors.push('\"title\" must be a non-empty, imperative PR title describing the change (no branch names).');\n }\n\n if (errors.length > 0) {\n throw new InformAiError(\n `review.json has ${errors.length} error(s) — fix ALL, then re-run pnpm wp-finish-upsert-pr:\\n\\n` +\n errors.map((e: string): string => ` • ${e}`).join('\\n') +\n `\\n\\n${this.reviewJsonSchemaHint(filePath)}`,\n );\n }\n\n const level = riskLevel as string;\n const emoji = typeof raw['riskEmoji'] === 'string' && raw['riskEmoji'] !== ''\n ? (raw['riskEmoji'] as string)\n : (EMOJI_FOR_LEVEL[level] ?? '🟡');\n const summary = typeof raw['summary'] === 'string' ? (raw['summary'] as string) : '';\n\n return new ReviewJson(\n title,\n riskScore as number,\n level,\n emoji,\n summary,\n this.asStringArray(raw['violations']),\n this.asStringArray(raw['risks']),\n this.asStringArray(raw['filesToReview']),\n );\n }\n\n // webpieces-disable no-any-unknown -- opaque parsed JSON value, narrowed to string[] here\n private asStringArray(value: unknown): string[] {\n if (!Array.isArray(value)) return [];\n // webpieces-disable no-any-unknown -- element of an opaque JSON array, narrowed by the type guard\n return value.filter((v: unknown): v is string => typeof v === 'string');\n }\n\n // Parse opaque AI-authored JSON, converting a SyntaxError into a readable InformAiError.\n // webpieces-disable no-any-unknown -- returns the opaque parsed object; loadReviewJson narrows each field\n private parseReviewJson(raw: string, filePath: string): Record<string, unknown> {\n // webpieces-disable no-unmanaged-exceptions -- chokepoint: convert JSON.parse SyntaxError to an InformAiError for the AI\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions\n try {\n // webpieces-disable no-any-unknown -- parsed JSON is opaque until narrowed by the caller\n return JSON.parse(raw) as Record<string, unknown>;\n } catch (err: unknown) {\n const error = toError(err);\n throw new InformAiError(\n `review.json is not valid JSON (${error.message}).\\n\\n${this.reviewJsonSchemaHint(filePath)}\\n\\n` +\n `Then re-run: pnpm wp-finish-upsert-pr`,\n );\n }\n }\n}\n\n// Temporary migration delegators to ReviewJsonService — removed once consumers inject it.\nconst reviewJsonSvc = new ReviewJsonService();\n\n// webpieces-disable no-function-outside-class -- temporary back-compat delegator to ReviewJsonService; removed once consumers inject it\nexport function prDirFor(repoRoot: string, featureName: string): string {\n return reviewJsonSvc.prDirFor(repoRoot, featureName);\n}\n\n// webpieces-disable no-function-outside-class -- temporary back-compat delegator to ReviewJsonService; removed once consumers inject it\nexport function reviewJsonPath(repoRoot: string, featureName: string): string {\n return reviewJsonSvc.reviewJsonPath(repoRoot, featureName);\n}\n\n// webpieces-disable no-function-outside-class -- temporary back-compat delegator to ReviewJsonService; removed once consumers inject it\nexport function reviewJsonSchemaHint(filePath: string): string {\n return reviewJsonSvc.reviewJsonSchemaHint(filePath);\n}\n\n// webpieces-disable no-function-outside-class -- temporary back-compat delegator to ReviewJsonService; removed once consumers inject it\nexport function loadReviewJson(filePath: string): ReviewJson {\n return reviewJsonSvc.loadReviewJson(filePath);\n}\n"]}
|
|
1
|
+
{"version":3,"file":"review-json.js","sourceRoot":"","sources":["../../../../../packages/tooling/rules-config/src/review-json.ts"],"names":[],"mappings":";;;AAqKA,4BAEC;AAGD,wCAEC;AAGD,oDAEC;AAGD,wCAEC;;AAtLD,+CAAyB;AACzB,mDAA6B;AAC7B,6BAAwC;AACxC,yCAAuC;AACvC,2CAA+D;AAC/D,uDAAkD;AAClD,yCAAqC;AAErC,sGAAsG;AACtG,4FAA4F;AAC5F,MAAa,UAAU;IACnB,KAAK,CAAS,CAAC,8FAA8F;IAC7G,SAAS,CAAS,CAAC,6BAA6B;IAChD,SAAS,CAAS,CAAC,6BAA6B;IAChD,SAAS,CAAS,CAAC,2DAA2D;IAC9E,OAAO,CAAS,CAAC,4CAA4C;IAC7D,UAAU,CAAW,CAAC,yEAAyE;IAC/F,KAAK,CAAW;IAChB,aAAa,CAAW;IAExB,YACI,KAAa,EACb,SAAiB,EACjB,SAAiB,EACjB,SAAiB,EACjB,OAAe,EACf,UAAoB,EACpB,KAAe,EACf,aAAuB;QAEvB,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;QACnB,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC;QAC3B,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC;QAC3B,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC;QAC3B,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;QACvB,IAAI,CAAC,UAAU,GAAG,UAAU,CAAC;QAC7B,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;QACnB,IAAI,CAAC,aAAa,GAAG,aAAa,CAAC;IACvC,CAAC;CACJ;AA7BD,gCA6BC;AAED,MAAM,WAAW,GAAG,CAAC,OAAO,EAAE,QAAQ,EAAE,KAAK,CAAU,CAAC;AACxD,MAAM,eAAe,GAA2B,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC;AAEzF,8GAA8G;AAGvG,IAAM,iBAAiB,GAAvB,MAAM,iBAAiB;IAC1B,oEAAoE;IACpE,QAAQ,CAAC,QAAgB,EAAE,WAAmB;QAC1C,OAAO,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,6BAAiB,EAAE,yBAAa,EAAE,WAAW,CAAC,CAAC;IAC9E,CAAC;IAED,4FAA4F;IAC5F,cAAc,CAAC,QAAgB,EAAE,WAAmB;QAChD,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,QAAQ,EAAE,WAAW,CAAC,EAAE,aAAa,CAAC,CAAC;IAC1E,CAAC;IAED,6DAA6D;IAC7D,oBAAoB,CAAC,QAAgB;QACjC,OAAO,CACH,+BAA+B,QAAQ,MAAM;YAC7C,+EAA+E;YAC/E,KAAK;YACL,sFAAsF;YACtF,+EAA+E;YAC/E,0CAA0C;YAC1C,gDAAgD;YAChD,wFAAwF;YACxF,uDAAuD;YACvD,6EAA6E;YAC7E,GAAG,CACN,CAAC;IACN,CAAC;IAED;;;OAGG;IACH,oGAAoG;IACpG,cAAc,CAAC,QAAgB;QAC3B,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC3B,MAAM,IAAI,+BAAa,CACnB,sCAAsC,IAAI,CAAC,oBAAoB,CAAC,QAAQ,CAAC,MAAM;gBAC/E,uCAAuC,CAC1C,CAAC;QACN,CAAC;QAED,MAAM,GAAG,GAAG,IAAI,CAAC,eAAe,CAAC,EAAE,CAAC,YAAY,CAAC,QAAQ,EAAE,MAAM,CAAC,EAAE,QAAQ,CAAC,CAAC;QAC9E,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,KAAK,IAAI,IAAI,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;YAChE,MAAM,IAAI,+BAAa,CAAC,yCAAyC,IAAI,CAAC,oBAAoB,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;QAC5G,CAAC;QAED,MAAM,MAAM,GAAa,EAAE,CAAC;QAE5B,MAAM,SAAS,GAAG,GAAG,CAAC,WAAW,CAAC,CAAC;QACnC,IAAI,OAAO,SAAS,KAAK,QAAQ,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,SAAS,CAAC,IAAI,SAAS,GAAG,CAAC,IAAI,SAAS,GAAG,GAAG,EAAE,CAAC;YACnG,MAAM,CAAC,IAAI,CAAC,2CAA2C,IAAI,CAAC,SAAS,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;QACzF,CAAC;QAED,MAAM,SAAS,GAAG,GAAG,CAAC,WAAW,CAAC,CAAC;QACnC,IAAI,OAAO,SAAS,KAAK,QAAQ,IAAI,CAAC,WAAW,CAAC,QAAQ,CAAC,SAAuC,CAAC,EAAE,CAAC;YAClG,MAAM,CAAC,IAAI,CAAC,+BAA+B,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAC1E,CAAC;QAED,MAAM,KAAK,GAAG,OAAO,GAAG,CAAC,OAAO,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAE,GAAG,CAAC,OAAO,CAAY,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACtF,IAAI,KAAK,KAAK,EAAE,EAAE,CAAC;YACf,MAAM,CAAC,IAAI,CAAC,2FAA2F,CAAC,CAAC;QAC7G,CAAC;QAED,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACpB,MAAM,IAAI,+BAAa,CACnB,mBAAmB,MAAM,CAAC,MAAM,gEAAgE;gBAChG,MAAM,CAAC,GAAG,CAAC,CAAC,CAAS,EAAU,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC;gBACxD,OAAO,IAAI,CAAC,oBAAoB,CAAC,QAAQ,CAAC,EAAE,CAC/C,CAAC;QACN,CAAC;QAED,MAAM,KAAK,GAAG,SAAmB,CAAC;QAClC,MAAM,KAAK,GAAG,OAAO,GAAG,CAAC,WAAW,CAAC,KAAK,QAAQ,IAAI,GAAG,CAAC,WAAW,CAAC,KAAK,EAAE;YACzE,CAAC,CAAE,GAAG,CAAC,WAAW,CAAY;YAC9B,CAAC,CAAC,CAAC,eAAe,CAAC,KAAK,CAAC,IAAI,IAAI,CAAC,CAAC;QACvC,MAAM,OAAO,GAAG,OAAO,GAAG,CAAC,SAAS,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAE,GAAG,CAAC,SAAS,CAAY,CAAC,CAAC,CAAC,EAAE,CAAC;QAErF,OAAO,IAAI,UAAU,CACjB,KAAK,EACL,SAAmB,EACnB,KAAK,EACL,KAAK,EACL,OAAO,EACP,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC,EACrC,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,EAChC,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC,CAC3C,CAAC;IACN,CAAC;IAED,0FAA0F;IAClF,aAAa,CAAC,KAAc;QAChC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;YAAE,OAAO,EAAE,CAAC;QACrC,kGAAkG;QAClG,OAAO,KAAK,CAAC,MAAM,CAAC,CAAC,CAAU,EAAe,EAAE,CAAC,OAAO,CAAC,KAAK,QAAQ,CAAC,CAAC;IAC5E,CAAC;IAED,yFAAyF;IACzF,0GAA0G;IAClG,eAAe,CAAC,GAAW,EAAE,QAAgB;QACjD,yHAAyH;QACzH,8DAA8D;QAC9D,IAAI,CAAC;YACD,yFAAyF;YACzF,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAA4B,CAAC;QACtD,CAAC;QAAC,OAAO,GAAY,EAAE,CAAC;YACpB,MAAM,KAAK,GAAG,IAAA,kBAAO,EAAC,GAAG,CAAC,CAAC;YAC3B,MAAM,IAAI,+BAAa,CACnB,kCAAkC,KAAK,CAAC,OAAO,SAAS,IAAI,CAAC,oBAAoB,CAAC,QAAQ,CAAC,MAAM;gBACjG,uCAAuC,CAC1C,CAAC;QACN,CAAC;IACL,CAAC;CACJ,CAAA;AAhHY,8CAAiB;4BAAjB,iBAAiB;IAF7B,IAAA,qBAAgB,GAAE;IAClB,IAAA,sBAAU,GAAE;GACA,iBAAiB,CAgH7B;AAED,0FAA0F;AAC1F,MAAM,aAAa,GAAG,IAAI,iBAAiB,EAAE,CAAC;AAE9C,wIAAwI;AACxI,SAAgB,QAAQ,CAAC,QAAgB,EAAE,WAAmB;IAC1D,OAAO,aAAa,CAAC,QAAQ,CAAC,QAAQ,EAAE,WAAW,CAAC,CAAC;AACzD,CAAC;AAED,wIAAwI;AACxI,SAAgB,cAAc,CAAC,QAAgB,EAAE,WAAmB;IAChE,OAAO,aAAa,CAAC,cAAc,CAAC,QAAQ,EAAE,WAAW,CAAC,CAAC;AAC/D,CAAC;AAED,wIAAwI;AACxI,SAAgB,oBAAoB,CAAC,QAAgB;IACjD,OAAO,aAAa,CAAC,oBAAoB,CAAC,QAAQ,CAAC,CAAC;AACxD,CAAC;AAED,wIAAwI;AACxI,SAAgB,cAAc,CAAC,QAAgB;IAC3C,OAAO,aAAa,CAAC,cAAc,CAAC,QAAQ,CAAC,CAAC;AAClD,CAAC","sourcesContent":["import * as fs from 'fs';\nimport * as path from 'path';\nimport { provideSingleton } from './di';\nimport { injectable } from 'inversify';\nimport { WEBPIECES_TMP_DIR, PR_REVIEW_DIR } from './constants';\nimport { InformAiError } from './inform-ai-error';\nimport { toError } from './to-error';\n\n// The AI-authored review for a PR. The AI writes this file itself between `wp-start-upsert-pr` (which\n// prints the schema) and `wp-finish-upsert-pr` (which reads it). Data-only (per CLAUDE.md).\nexport class ReviewJson {\n title: string; // human PR title describing the change; used as the `gh pr` title (empty → caller falls back)\n riskScore: number; // 0–100, drives the risk bar\n riskLevel: string; // 'green' | 'yellow' | 'red'\n riskEmoji: string; // '🟢' | '🟡' | '🔴' — derived from riskLevel when omitted\n summary: string; // rendered in the dashboard Summary section\n violations: string[]; // pattern/architecture violations; length = the Pattern Violations count\n risks: string[];\n filesToReview: string[];\n\n constructor(\n title: string,\n riskScore: number,\n riskLevel: string,\n riskEmoji: string,\n summary: string,\n violations: string[],\n risks: string[],\n filesToReview: string[],\n ) {\n this.title = title;\n this.riskScore = riskScore;\n this.riskLevel = riskLevel;\n this.riskEmoji = riskEmoji;\n this.summary = summary;\n this.violations = violations;\n this.risks = risks;\n this.filesToReview = filesToReview;\n }\n}\n\nconst RISK_LEVELS = ['green', 'yellow', 'red'] as const;\nconst EMOJI_FOR_LEVEL: Record<string, string> = { green: '🟢', yellow: '🟡', red: '🔴' };\n\n/** Locates + loads/validates the AI-authored review.json. `@provideSingleton` so it's drawn in the design. */\n@provideSingleton()\n@injectable()\nexport class ReviewJsonService {\n // The per-feature PR working dir: `.webpieces/pr-review/<feature>`.\n prDirFor(repoRoot: string, featureName: string): string {\n return path.join(repoRoot, WEBPIECES_TMP_DIR, PR_REVIEW_DIR, featureName);\n }\n\n // Absolute path of the review.json for a feature — beside pr-body.md, keyed by branch name.\n reviewJsonPath(repoRoot: string, featureName: string): string {\n return path.join(this.prDirFor(repoRoot, featureName), 'review.json');\n }\n\n // Copy-paste schema both commands print (write it / fix it).\n reviewJsonSchemaHint(filePath: string): string {\n return (\n `Write your PR review to:\\n ${filePath}\\n\\n` +\n `with this exact JSON shape (riskEmoji optional — derived from riskLevel):\\n\\n` +\n `{\\n` +\n ` \"title\": \"concise PR title describing the change (imperative, no branch names)\",\\n` +\n ` \"riskScore\": 0, // integer 0–100 (higher = riskier)\\n` +\n ` \"riskLevel\": \"green | yellow | red\",\\n` +\n ` \"summary\": \"5–10 sentence review summary\",\\n` +\n ` \"violations\": [\"pattern/architecture violations you found (empty array if none)\"],\\n` +\n ` \"risks\": [\"notable risks (empty array if none)\"],\\n` +\n ` \"filesToReview\": [\"paths a human should look at (empty array if none)\"]\\n` +\n `}`\n );\n }\n\n /**\n * Load + validate the AI-authored review.json. Throws InformAiError (with the schema) when missing,\n * unparseable, or structurally wrong. Returns a fully-populated ReviewJson on success.\n */\n // webpieces-disable max-lines-new-methods -- one cohesive load+validate pass over the review fields\n loadReviewJson(filePath: string): ReviewJson {\n if (!fs.existsSync(filePath)) {\n throw new InformAiError(\n `Required review.json not found.\\n\\n${this.reviewJsonSchemaHint(filePath)}\\n\\n` +\n `Then re-run: pnpm wp-finish-upsert-pr`,\n );\n }\n\n const raw = this.parseReviewJson(fs.readFileSync(filePath, 'utf8'), filePath);\n if (typeof raw !== 'object' || raw === null || Array.isArray(raw)) {\n throw new InformAiError(`review.json must be a JSON object.\\n\\n${this.reviewJsonSchemaHint(filePath)}`);\n }\n\n const errors: string[] = [];\n\n const riskScore = raw['riskScore'];\n if (typeof riskScore !== 'number' || !Number.isFinite(riskScore) || riskScore < 0 || riskScore > 100) {\n errors.push(`\"riskScore\" must be a number 0–100, got ${JSON.stringify(riskScore)}.`);\n }\n\n const riskLevel = raw['riskLevel'];\n if (typeof riskLevel !== 'string' || !RISK_LEVELS.includes(riskLevel as typeof RISK_LEVELS[number])) {\n errors.push(`\"riskLevel\" must be one of: ${RISK_LEVELS.join(', ')}.`);\n }\n\n const title = typeof raw['title'] === 'string' ? (raw['title'] as string).trim() : '';\n if (title === '') {\n errors.push('\"title\" must be a non-empty, imperative PR title describing the change (no branch names).');\n }\n\n if (errors.length > 0) {\n throw new InformAiError(\n `review.json has ${errors.length} error(s) — fix ALL, then re-run pnpm wp-finish-upsert-pr:\\n\\n` +\n errors.map((e: string): string => ` • ${e}`).join('\\n') +\n `\\n\\n${this.reviewJsonSchemaHint(filePath)}`,\n );\n }\n\n const level = riskLevel as string;\n const emoji = typeof raw['riskEmoji'] === 'string' && raw['riskEmoji'] !== ''\n ? (raw['riskEmoji'] as string)\n : (EMOJI_FOR_LEVEL[level] ?? '🟡');\n const summary = typeof raw['summary'] === 'string' ? (raw['summary'] as string) : '';\n\n return new ReviewJson(\n title,\n riskScore as number,\n level,\n emoji,\n summary,\n this.asStringArray(raw['violations']),\n this.asStringArray(raw['risks']),\n this.asStringArray(raw['filesToReview']),\n );\n }\n\n // webpieces-disable no-any-unknown -- opaque parsed JSON value, narrowed to string[] here\n private asStringArray(value: unknown): string[] {\n if (!Array.isArray(value)) return [];\n // webpieces-disable no-any-unknown -- element of an opaque JSON array, narrowed by the type guard\n return value.filter((v: unknown): v is string => typeof v === 'string');\n }\n\n // Parse opaque AI-authored JSON, converting a SyntaxError into a readable InformAiError.\n // webpieces-disable no-any-unknown -- returns the opaque parsed object; loadReviewJson narrows each field\n private parseReviewJson(raw: string, filePath: string): Record<string, unknown> {\n // webpieces-disable no-unmanaged-exceptions -- chokepoint: convert JSON.parse SyntaxError to an InformAiError for the AI\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions\n try {\n // webpieces-disable no-any-unknown -- parsed JSON is opaque until narrowed by the caller\n return JSON.parse(raw) as Record<string, unknown>;\n } catch (err: unknown) {\n const error = toError(err);\n throw new InformAiError(\n `review.json is not valid JSON (${error.message}).\\n\\n${this.reviewJsonSchemaHint(filePath)}\\n\\n` +\n `Then re-run: pnpm wp-finish-upsert-pr`,\n );\n }\n }\n}\n\n// Temporary migration delegators to ReviewJsonService — removed once consumers inject it.\nconst reviewJsonSvc = new ReviewJsonService();\n\n// webpieces-disable no-function-outside-class -- temporary back-compat delegator to ReviewJsonService; removed once consumers inject it\nexport function prDirFor(repoRoot: string, featureName: string): string {\n return reviewJsonSvc.prDirFor(repoRoot, featureName);\n}\n\n// webpieces-disable no-function-outside-class -- temporary back-compat delegator to ReviewJsonService; removed once consumers inject it\nexport function reviewJsonPath(repoRoot: string, featureName: string): string {\n return reviewJsonSvc.reviewJsonPath(repoRoot, featureName);\n}\n\n// webpieces-disable no-function-outside-class -- temporary back-compat delegator to ReviewJsonService; removed once consumers inject it\nexport function reviewJsonSchemaHint(filePath: string): string {\n return reviewJsonSvc.reviewJsonSchemaHint(filePath);\n}\n\n// webpieces-disable no-function-outside-class -- temporary back-compat delegator to ReviewJsonService; removed once consumers inject it\nexport function loadReviewJson(filePath: string): ReviewJson {\n return reviewJsonSvc.loadReviewJson(filePath);\n}\n"]}
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.RulesConfigDesign = void 0;
|
|
4
4
|
const tslib_1 = require("tslib");
|
|
5
|
-
const
|
|
6
|
-
const
|
|
5
|
+
const di_1 = require("./di");
|
|
6
|
+
const di_2 = require("./di");
|
|
7
7
|
const inversify_1 = require("inversify");
|
|
8
8
|
const repo_root_1 = require("./repo-root");
|
|
9
9
|
const load_config_1 = require("./load-config");
|
|
@@ -41,8 +41,8 @@ let RulesConfigDesign = class RulesConfigDesign {
|
|
|
41
41
|
};
|
|
42
42
|
exports.RulesConfigDesign = RulesConfigDesign;
|
|
43
43
|
exports.RulesConfigDesign = RulesConfigDesign = tslib_1.__decorate([
|
|
44
|
-
(0,
|
|
45
|
-
(0,
|
|
44
|
+
(0, di_1.DocumentDesign)(),
|
|
45
|
+
(0, di_2.provideSingleton)(),
|
|
46
46
|
(0, inversify_1.injectable)(),
|
|
47
47
|
tslib_1.__metadata("design:paramtypes", [repo_root_1.RepoRootFinder,
|
|
48
48
|
load_config_1.ConfigLoader,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rules-config-design.js","sourceRoot":"","sources":["../../../../../packages/tooling/rules-config/src/rules-config-design.ts"],"names":[],"mappings":";;;;AAAA,
|
|
1
|
+
{"version":3,"file":"rules-config-design.js","sourceRoot":"","sources":["../../../../../packages/tooling/rules-config/src/rules-config-design.ts"],"names":[],"mappings":";;;;AAAA,6BAAsC;AACtC,6BAAwC;AACxC,yCAAuC;AAEvC,2CAA6C;AAC7C,+CAA6C;AAC7C,mDAAiD;AACjD,6CAAyC;AACzC,+DAA0D;AAC1D,+CAAkD;AAClD,yDAA2D;AAE3D;;;;;;;;GAQG;AAII,IAAM,iBAAiB,GAAvB,MAAM,iBAAiB;IAEL;IACA;IACA;IACA;IACA;IACA;IACA;IAPrB,YACqB,cAA8B,EAC9B,YAA0B,EAC1B,cAA8B,EAC9B,SAAoB,EACpB,iBAAoC,EACpC,UAA6B,EAC7B,cAAqC;QANrC,mBAAc,GAAd,cAAc,CAAgB;QAC9B,iBAAY,GAAZ,YAAY,CAAc;QAC1B,mBAAc,GAAd,cAAc,CAAgB;QAC9B,cAAS,GAAT,SAAS,CAAW;QACpB,sBAAiB,GAAjB,iBAAiB,CAAmB;QACpC,eAAU,GAAV,UAAU,CAAmB;QAC7B,mBAAc,GAAd,cAAc,CAAuB;IACvD,CAAC;CACP,CAAA;AAVY,8CAAiB;4BAAjB,iBAAiB;IAH7B,IAAA,mBAAc,GAAE;IAChB,IAAA,qBAAgB,GAAE;IAClB,IAAA,sBAAU,GAAE;6CAG4B,0BAAc;QAChB,0BAAY;QACV,8BAAc;QACnB,sBAAS;QACD,uCAAiB;QACxB,+BAAiB;QACb,wCAAqB;GARjD,iBAAiB,CAU7B","sourcesContent":["import { DocumentDesign } from './di';\nimport { provideSingleton } from './di';\nimport { injectable } from 'inversify';\n\nimport { RepoRootFinder } from './repo-root';\nimport { ConfigLoader } from './load-config';\nimport { TemplateWriter } from './load-template';\nimport { DiffScope } from './diff-scope';\nimport { BranchMutationLog } from './branch-mutation-log';\nimport { ReviewJsonService } from './review-json';\nimport { MainSyncStatusService } from './main-sync-status';\n\n/**\n * DI-design root for @webpieces/rules-config (role:designed-lib).\n *\n * `@DocumentDesign` marks the top of the DAG the DI-design analyzer roots on, so the library's design\n * (design.json / design.md / design.html) is generated. rules-config is the shared foundation whose\n * utilities are being migrated from free functions to injected `@provideSingleton` service classes; as\n * each service class lands (config loader, template writer, diff/git services, …) it is injected HERE\n * so it appears in the drawn design.\n */\n@DocumentDesign()\n@provideSingleton()\n@injectable()\nexport class RulesConfigDesign {\n constructor(\n private readonly repoRootFinder: RepoRootFinder,\n private readonly configLoader: ConfigLoader,\n private readonly templateWriter: TemplateWriter,\n private readonly diffScope: DiffScope,\n private readonly branchMutationLog: BranchMutationLog,\n private readonly reviewJson: ReviewJsonService,\n private readonly mainSyncStatus: MainSyncStatusService,\n ) {}\n}\n"]}
|