archcheck 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +536 -0
- package/dist/archcheck.d.ts +99 -0
- package/dist/archcheck.d.ts.map +1 -0
- package/dist/archcheck.js +339 -0
- package/dist/archcheck.js.map +1 -0
- package/dist/baseline/index.d.ts +29 -0
- package/dist/baseline/index.d.ts.map +1 -0
- package/dist/baseline/index.js +116 -0
- package/dist/baseline/index.js.map +1 -0
- package/dist/cli/args.d.ts +36 -0
- package/dist/cli/args.d.ts.map +1 -0
- package/dist/cli/args.js +233 -0
- package/dist/cli/args.js.map +1 -0
- package/dist/cli/index.d.ts +4 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +219 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/version.d.ts +2 -0
- package/dist/cli/version.d.ts.map +1 -0
- package/dist/cli/version.js +29 -0
- package/dist/cli/version.js.map +1 -0
- package/dist/contracts/banned-syntax.d.ts +48 -0
- package/dist/contracts/banned-syntax.d.ts.map +1 -0
- package/dist/contracts/banned-syntax.js +201 -0
- package/dist/contracts/banned-syntax.js.map +1 -0
- package/dist/contracts/declared-field.d.ts +39 -0
- package/dist/contracts/declared-field.d.ts.map +1 -0
- package/dist/contracts/declared-field.js +142 -0
- package/dist/contracts/declared-field.js.map +1 -0
- package/dist/contracts/encapsulation.d.ts +28 -0
- package/dist/contracts/encapsulation.d.ts.map +1 -0
- package/dist/contracts/encapsulation.js +130 -0
- package/dist/contracts/encapsulation.js.map +1 -0
- package/dist/contracts/index.d.ts +10 -0
- package/dist/contracts/index.d.ts.map +1 -0
- package/dist/contracts/index.js +9 -0
- package/dist/contracts/index.js.map +1 -0
- package/dist/contracts/judgement.d.ts +21 -0
- package/dist/contracts/judgement.d.ts.map +1 -0
- package/dist/contracts/judgement.js +109 -0
- package/dist/contracts/judgement.js.map +1 -0
- package/dist/contracts/layers.d.ts +75 -0
- package/dist/contracts/layers.d.ts.map +1 -0
- package/dist/contracts/layers.js +244 -0
- package/dist/contracts/layers.js.map +1 -0
- package/dist/contracts/naming.d.ts +21 -0
- package/dist/contracts/naming.d.ts.map +1 -0
- package/dist/contracts/naming.js +120 -0
- package/dist/contracts/naming.js.map +1 -0
- package/dist/contracts/registry.d.ts +23 -0
- package/dist/contracts/registry.d.ts.map +1 -0
- package/dist/contracts/registry.js +48 -0
- package/dist/contracts/registry.js.map +1 -0
- package/dist/contracts/required-file.d.ts +29 -0
- package/dist/contracts/required-file.d.ts.map +1 -0
- package/dist/contracts/required-file.js +131 -0
- package/dist/contracts/required-file.js.map +1 -0
- package/dist/contracts/types.d.ts +61 -0
- package/dist/contracts/types.d.ts.map +1 -0
- package/dist/contracts/types.js +2 -0
- package/dist/contracts/types.js.map +1 -0
- package/dist/coverage/index.d.ts +55 -0
- package/dist/coverage/index.d.ts.map +1 -0
- package/dist/coverage/index.js +68 -0
- package/dist/coverage/index.js.map +1 -0
- package/dist/emit/collect.d.ts +20 -0
- package/dist/emit/collect.d.ts.map +1 -0
- package/dist/emit/collect.js +17 -0
- package/dist/emit/collect.js.map +1 -0
- package/dist/emit/header.d.ts +21 -0
- package/dist/emit/header.d.ts.map +1 -0
- package/dist/emit/header.js +20 -0
- package/dist/emit/header.js.map +1 -0
- package/dist/emit/index.d.ts +57 -0
- package/dist/emit/index.d.ts.map +1 -0
- package/dist/emit/index.js +128 -0
- package/dist/emit/index.js.map +1 -0
- package/dist/emit/merge.d.ts +23 -0
- package/dist/emit/merge.d.ts.map +1 -0
- package/dist/emit/merge.js +71 -0
- package/dist/emit/merge.js.map +1 -0
- package/dist/emit/write.d.ts +20 -0
- package/dist/emit/write.d.ts.map +1 -0
- package/dist/emit/write.js +61 -0
- package/dist/emit/write.js.map +1 -0
- package/dist/exceptions/index.d.ts +46 -0
- package/dist/exceptions/index.d.ts.map +1 -0
- package/dist/exceptions/index.js +117 -0
- package/dist/exceptions/index.js.map +1 -0
- package/dist/fs/index.d.ts +12 -0
- package/dist/fs/index.d.ts.map +1 -0
- package/dist/fs/index.js +78 -0
- package/dist/fs/index.js.map +1 -0
- package/dist/graph/cache.d.ts +12 -0
- package/dist/graph/cache.d.ts.map +1 -0
- package/dist/graph/cache.js +76 -0
- package/dist/graph/cache.js.map +1 -0
- package/dist/graph/edges.d.ts +16 -0
- package/dist/graph/edges.d.ts.map +1 -0
- package/dist/graph/edges.js +161 -0
- package/dist/graph/edges.js.map +1 -0
- package/dist/graph/index.d.ts +15 -0
- package/dist/graph/index.d.ts.map +1 -0
- package/dist/graph/index.js +90 -0
- package/dist/graph/index.js.map +1 -0
- package/dist/graph/match.d.ts +12 -0
- package/dist/graph/match.d.ts.map +1 -0
- package/dist/graph/match.js +35 -0
- package/dist/graph/match.js.map +1 -0
- package/dist/graph/program.d.ts +12 -0
- package/dist/graph/program.d.ts.map +1 -0
- package/dist/graph/program.js +54 -0
- package/dist/graph/program.js.map +1 -0
- package/dist/graph/types.d.ts +55 -0
- package/dist/graph/types.d.ts.map +1 -0
- package/dist/graph/types.js +2 -0
- package/dist/graph/types.js.map +1 -0
- package/dist/index.d.ts +21 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +20 -0
- package/dist/index.js.map +1 -0
- package/dist/init/index.d.ts +23 -0
- package/dist/init/index.d.ts.map +1 -0
- package/dist/init/index.js +218 -0
- package/dist/init/index.js.map +1 -0
- package/dist/normalize/identity.d.ts +4 -0
- package/dist/normalize/identity.d.ts.map +1 -0
- package/dist/normalize/identity.js +33 -0
- package/dist/normalize/identity.js.map +1 -0
- package/dist/normalize/index.d.ts +3 -0
- package/dist/normalize/index.d.ts.map +1 -0
- package/dist/normalize/index.js +3 -0
- package/dist/normalize/index.js.map +1 -0
- package/dist/normalize/normalize.d.ts +34 -0
- package/dist/normalize/normalize.d.ts.map +1 -0
- package/dist/normalize/normalize.js +91 -0
- package/dist/normalize/normalize.js.map +1 -0
- package/dist/probes/graph.d.ts +19 -0
- package/dist/probes/graph.d.ts.map +1 -0
- package/dist/probes/graph.js +181 -0
- package/dist/probes/graph.js.map +1 -0
- package/dist/probes/index.d.ts +5 -0
- package/dist/probes/index.d.ts.map +1 -0
- package/dist/probes/index.js +5 -0
- package/dist/probes/index.js.map +1 -0
- package/dist/probes/lint.d.ts +17 -0
- package/dist/probes/lint.d.ts.map +1 -0
- package/dist/probes/lint.js +121 -0
- package/dist/probes/lint.js.map +1 -0
- package/dist/probes/model/anthropic.d.ts +47 -0
- package/dist/probes/model/anthropic.d.ts.map +1 -0
- package/dist/probes/model/anthropic.js +100 -0
- package/dist/probes/model/anthropic.js.map +1 -0
- package/dist/probes/model/budget.d.ts +22 -0
- package/dist/probes/model/budget.d.ts.map +1 -0
- package/dist/probes/model/budget.js +27 -0
- package/dist/probes/model/budget.js.map +1 -0
- package/dist/probes/model/cache.d.ts +13 -0
- package/dist/probes/model/cache.d.ts.map +1 -0
- package/dist/probes/model/cache.js +74 -0
- package/dist/probes/model/cache.js.map +1 -0
- package/dist/probes/model/index.d.ts +20 -0
- package/dist/probes/model/index.d.ts.map +1 -0
- package/dist/probes/model/index.js +160 -0
- package/dist/probes/model/index.js.map +1 -0
- package/dist/probes/model/prompt.d.ts +16 -0
- package/dist/probes/model/prompt.d.ts.map +1 -0
- package/dist/probes/model/prompt.js +57 -0
- package/dist/probes/model/prompt.js.map +1 -0
- package/dist/probes/model/provider.d.ts +88 -0
- package/dist/probes/model/provider.d.ts.map +1 -0
- package/dist/probes/model/provider.js +48 -0
- package/dist/probes/model/provider.js.map +1 -0
- package/dist/probes/structure.d.ts +3 -0
- package/dist/probes/structure.d.ts.map +1 -0
- package/dist/probes/structure.js +95 -0
- package/dist/probes/structure.js.map +1 -0
- package/dist/probes/types.d.ts +46 -0
- package/dist/probes/types.d.ts.map +1 -0
- package/dist/probes/types.js +15 -0
- package/dist/probes/types.js.map +1 -0
- package/dist/registry/define.d.ts +40 -0
- package/dist/registry/define.d.ts.map +1 -0
- package/dist/registry/define.js +4 -0
- package/dist/registry/define.js.map +1 -0
- package/dist/registry/ids.d.ts +19 -0
- package/dist/registry/ids.d.ts.map +1 -0
- package/dist/registry/ids.js +86 -0
- package/dist/registry/ids.js.map +1 -0
- package/dist/registry/index.d.ts +45 -0
- package/dist/registry/index.d.ts.map +1 -0
- package/dist/registry/index.js +71 -0
- package/dist/registry/index.js.map +1 -0
- package/dist/registry/load.d.ts +14 -0
- package/dist/registry/load.d.ts.map +1 -0
- package/dist/registry/load.js +108 -0
- package/dist/registry/load.js.map +1 -0
- package/dist/registry/validate.d.ts +24 -0
- package/dist/registry/validate.d.ts.map +1 -0
- package/dist/registry/validate.js +160 -0
- package/dist/registry/validate.js.map +1 -0
- package/dist/report/index.d.ts +10 -0
- package/dist/report/index.d.ts.map +1 -0
- package/dist/report/index.js +30 -0
- package/dist/report/index.js.map +1 -0
- package/dist/report/json.d.ts +20 -0
- package/dist/report/json.d.ts.map +1 -0
- package/dist/report/json.js +34 -0
- package/dist/report/json.js.map +1 -0
- package/dist/report/sarif.d.ts +21 -0
- package/dist/report/sarif.d.ts.map +1 -0
- package/dist/report/sarif.js +138 -0
- package/dist/report/sarif.js.map +1 -0
- package/dist/report/terminal.d.ts +3 -0
- package/dist/report/terminal.d.ts.map +1 -0
- package/dist/report/terminal.js +117 -0
- package/dist/report/terminal.js.map +1 -0
- package/dist/report/types.d.ts +28 -0
- package/dist/report/types.d.ts.map +1 -0
- package/dist/report/types.js +11 -0
- package/dist/report/types.js.map +1 -0
- package/dist/report/verdict.d.ts +21 -0
- package/dist/report/verdict.d.ts.map +1 -0
- package/dist/report/verdict.js +31 -0
- package/dist/report/verdict.js.map +1 -0
- package/dist/result.d.ts +22 -0
- package/dist/result.d.ts.map +1 -0
- package/dist/result.js +19 -0
- package/dist/result.js.map +1 -0
- package/dist/types.d.ts +178 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +11 -0
- package/dist/types.js.map +1 -0
- package/dist/util/compilerApi.d.ts +2 -0
- package/dist/util/compilerApi.d.ts.map +1 -0
- package/dist/util/compilerApi.js +28 -0
- package/dist/util/compilerApi.js.map +1 -0
- package/dist/util/glob.d.ts +43 -0
- package/dist/util/glob.d.ts.map +1 -0
- package/dist/util/glob.js +195 -0
- package/dist/util/glob.js.map +1 -0
- package/dist/util/paths.d.ts +6 -0
- package/dist/util/paths.d.ts.map +1 -0
- package/dist/util/paths.js +22 -0
- package/dist/util/paths.js.map +1 -0
- package/dist/version.d.ts +13 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +12 -0
- package/dist/version.js.map +1 -0
- package/package.json +72 -0
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The second seam: a probe.
|
|
3
|
+
*
|
|
4
|
+
* What rule this enforces: every probe can either RUN a tool or INGEST the report another gate
|
|
5
|
+
* already produced (LLD §1.3). Without that, archcheck re-runs the type checker, the linter and
|
|
6
|
+
* the graph reader and doubles CI time for no new information. CI builds each artefact once and
|
|
7
|
+
* archcheck normalises; running directly is the local convenience path.
|
|
8
|
+
*
|
|
9
|
+
* What a caller must know: `ctx.graph()` is MEMOISED by the caller. One parse, shared by every
|
|
10
|
+
* probe — a probe that builds its own graph has broken the property the whole design rests on.
|
|
11
|
+
*/
|
|
12
|
+
import type { FileSystem } from '../fs/index.js';
|
|
13
|
+
import type { ModuleGraph } from '../graph/types.js';
|
|
14
|
+
import type { Result } from '../result.js';
|
|
15
|
+
import type { ContractKind, RawFinding, Rule } from '../types.js';
|
|
16
|
+
/** A probe that could not run. `skipped` in the RunResult is built from these. */
|
|
17
|
+
export type Skip = {
|
|
18
|
+
skipped: true;
|
|
19
|
+
reason: string;
|
|
20
|
+
};
|
|
21
|
+
export declare function isSkip(value: unknown): value is Skip;
|
|
22
|
+
export type RunCtx = {
|
|
23
|
+
root: string;
|
|
24
|
+
tsconfigPath: string;
|
|
25
|
+
/** Active rules only — `severity: 'off'` never reaches a probe. */
|
|
26
|
+
rules: readonly Rule[];
|
|
27
|
+
/**
|
|
28
|
+
* Repo-relative POSIX paths of the changed set, or undefined for the whole repository.
|
|
29
|
+
* Diff scoping is where the real saving is: rule evaluation and every model call (LLD §1.8).
|
|
30
|
+
*/
|
|
31
|
+
changed?: readonly string[];
|
|
32
|
+
/** The shared module graph, built at most once per run. */
|
|
33
|
+
graph(): Result<ModuleGraph>;
|
|
34
|
+
/** Every filesystem read goes through here, so a probe can be run with no disk at all. */
|
|
35
|
+
fs: FileSystem;
|
|
36
|
+
/** Repo-relative path of the manifest — where a registry-level finding is reported against. */
|
|
37
|
+
configPath: string;
|
|
38
|
+
};
|
|
39
|
+
export type Probe = {
|
|
40
|
+
name: string;
|
|
41
|
+
handles: readonly ContractKind[];
|
|
42
|
+
run(ctx: RunCtx): Promise<RawFinding[] | Skip>;
|
|
43
|
+
/** Map a report another gate wrote back into findings. */
|
|
44
|
+
ingest?(report: unknown, ctx: RunCtx): RawFinding[];
|
|
45
|
+
};
|
|
46
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/probes/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AACjD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAC3C,OAAO,KAAK,EAAE,YAAY,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC;AAElE,kFAAkF;AAClF,MAAM,MAAM,IAAI,GAAG;IAAE,OAAO,EAAE,IAAI,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAErD,wBAAgB,MAAM,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,IAAI,CAEpD;AAED,MAAM,MAAM,MAAM,GAAG;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,MAAM,CAAC;IACrB,mEAAmE;IACnE,KAAK,EAAE,SAAS,IAAI,EAAE,CAAC;IACvB;;;OAGG;IACH,OAAO,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC5B,2DAA2D;IAC3D,KAAK,IAAI,MAAM,CAAC,WAAW,CAAC,CAAC;IAC7B,0FAA0F;IAC1F,EAAE,EAAE,UAAU,CAAC;IACf,+FAA+F;IAC/F,UAAU,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,MAAM,MAAM,KAAK,GAAG;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,SAAS,YAAY,EAAE,CAAC;IACjC,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,EAAE,GAAG,IAAI,CAAC,CAAC;IAC/C,0DAA0D;IAC1D,MAAM,CAAC,CAAC,MAAM,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,GAAG,UAAU,EAAE,CAAC;CACrD,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The second seam: a probe.
|
|
3
|
+
*
|
|
4
|
+
* What rule this enforces: every probe can either RUN a tool or INGEST the report another gate
|
|
5
|
+
* already produced (LLD §1.3). Without that, archcheck re-runs the type checker, the linter and
|
|
6
|
+
* the graph reader and doubles CI time for no new information. CI builds each artefact once and
|
|
7
|
+
* archcheck normalises; running directly is the local convenience path.
|
|
8
|
+
*
|
|
9
|
+
* What a caller must know: `ctx.graph()` is MEMOISED by the caller. One parse, shared by every
|
|
10
|
+
* probe — a probe that builds its own graph has broken the property the whole design rests on.
|
|
11
|
+
*/
|
|
12
|
+
export function isSkip(value) {
|
|
13
|
+
return typeof value === 'object' && value !== null && value.skipped === true;
|
|
14
|
+
}
|
|
15
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/probes/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAUH,MAAM,UAAU,MAAM,CAAC,KAAc;IACnC,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAK,KAAc,CAAC,OAAO,KAAK,IAAI,CAAC;AACzF,CAAC"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `defineArchitecture` — the one function a manifest calls.
|
|
3
|
+
*
|
|
4
|
+
* What this is for: giving `architecture.config.ts` a type without giving it any runtime
|
|
5
|
+
* behaviour. It is an identity function on purpose. A manifest that ran logic at load time would
|
|
6
|
+
* be a program pretending to be a declaration, and the whole value of the manifest is that it can
|
|
7
|
+
* be read by a person, a generator and a diff.
|
|
8
|
+
*
|
|
9
|
+
* What a caller must know: nothing here validates. Validation is total and lives in
|
|
10
|
+
* `validate.ts`, because it must be able to return every problem at once rather than throwing on
|
|
11
|
+
* the first (a malformed manifest exits 2, not 1).
|
|
12
|
+
*/
|
|
13
|
+
import type { ArchitectureException } from '../exceptions/index.js';
|
|
14
|
+
import type { Rule } from '../types.js';
|
|
15
|
+
export type ArchitectureManifest = {
|
|
16
|
+
/**
|
|
17
|
+
* The repository root every path in the manifest is relative to. Defaults to the directory
|
|
18
|
+
* holding the config, which is what almost everyone wants.
|
|
19
|
+
*/
|
|
20
|
+
root?: string;
|
|
21
|
+
/** The tsconfig the module graph is built from. Defaults to `<root>/tsconfig.json`. */
|
|
22
|
+
tsconfig?: string;
|
|
23
|
+
/** Where generated output is written. Defaults to `generated`. */
|
|
24
|
+
generatedDir?: string;
|
|
25
|
+
/**
|
|
26
|
+
* Documents with an `archcheck:begin rules-table` fence. Only the fenced region is generated —
|
|
27
|
+
* long-form prose stays hand-written, because a manifest holding paragraphs is a config file
|
|
28
|
+
* pretending to be a document (LLD §1.9).
|
|
29
|
+
*/
|
|
30
|
+
docs?: string[];
|
|
31
|
+
/** The only place a rule is defined. */
|
|
32
|
+
rules: Rule[];
|
|
33
|
+
/**
|
|
34
|
+
* The only legitimate way past a rule: owned, reasoned and expiring. Never inline — an inline
|
|
35
|
+
* suppression comment is itself a violation (plan 12, plan 15).
|
|
36
|
+
*/
|
|
37
|
+
exceptions?: ArchitectureException[];
|
|
38
|
+
};
|
|
39
|
+
export declare function defineArchitecture(config: ArchitectureManifest): ArchitectureManifest;
|
|
40
|
+
//# sourceMappingURL=define.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"define.d.ts","sourceRoot":"","sources":["../../src/registry/define.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,wBAAwB,CAAC;AACpE,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC;AAExC,MAAM,MAAM,oBAAoB,GAAG;IACjC;;;OAGG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,uFAAuF;IACvF,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,kEAAkE;IAClE,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;;;OAIG;IACH,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,wCAAwC;IACxC,KAAK,EAAE,IAAI,EAAE,CAAC;IACd;;;OAGG;IACH,UAAU,CAAC,EAAE,qBAAqB,EAAE,CAAC;CACtC,CAAC;AAEF,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,oBAAoB,GAAG,oBAAoB,CAErF"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"define.js","sourceRoot":"","sources":["../../src/registry/define.ts"],"names":[],"mappings":"AAwCA,MAAM,UAAU,kBAAkB,CAAC,MAA4B;IAC7D,OAAO,MAAM,CAAC;AAChB,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { type Result } from '../result.js';
|
|
2
|
+
import type { Rule } from '../types.js';
|
|
3
|
+
export type IdLedger = {
|
|
4
|
+
schemaVersion: 1;
|
|
5
|
+
ids: string[];
|
|
6
|
+
/**
|
|
7
|
+
* Set when a ledger file exists but could not be understood, and holds the whole message to
|
|
8
|
+
* print. `checkIdImmutability` refuses to run against it, and the CLI refuses to let `emit`
|
|
9
|
+
* rebuild it: an empty `ids` list would otherwise read as "no id has ever been committed",
|
|
10
|
+
* which passes every rename. `tests/cli.test.ts` → 'a corrupt id ledger' pins both paths.
|
|
11
|
+
*/
|
|
12
|
+
unreadable?: string;
|
|
13
|
+
};
|
|
14
|
+
export declare const ID_LEDGER_PATH = ".archcheck/ids.json";
|
|
15
|
+
export declare function readIdLedger(path: string): IdLedger;
|
|
16
|
+
/** The ledger `emit` should write: every id ever seen, plus today's, sorted and deduplicated. */
|
|
17
|
+
export declare function nextIdLedger(previous: IdLedger, rules: readonly Rule[]): IdLedger;
|
|
18
|
+
export declare function checkIdImmutability(rules: readonly Rule[], ledger: IdLedger): Result<void>;
|
|
19
|
+
//# sourceMappingURL=ids.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ids.d.ts","sourceRoot":"","sources":["../../src/registry/ids.ts"],"names":[],"mappings":"AAYA,OAAO,EAAW,KAAK,MAAM,EAAE,MAAM,cAAc,CAAC;AACpD,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC;AAExC,MAAM,MAAM,QAAQ,GAAG;IACrB,aAAa,EAAE,CAAC,CAAC;IACjB,GAAG,EAAE,MAAM,EAAE,CAAC;IACd;;;;;OAKG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF,eAAO,MAAM,cAAc,wBAAwB,CAAC;AAcpD,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,QAAQ,CAgCnD;AAED,iGAAiG;AACjG,wBAAgB,YAAY,CAAC,QAAQ,EAAE,QAAQ,EAAE,KAAK,EAAE,SAAS,IAAI,EAAE,GAAG,QAAQ,CAIjF;AAED,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,SAAS,IAAI,EAAE,EAAE,MAAM,EAAE,QAAQ,GAAG,MAAM,CAAC,IAAI,CAAC,CAmB1F"}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rule ids are immutable once committed.
|
|
3
|
+
*
|
|
4
|
+
* What rule this enforces: LLD §1.6. A rename silently orphans every baseline entry and every
|
|
5
|
+
* historical annotation that keyed off the old id — and it fails open, because the renamed rule
|
|
6
|
+
* still passes. `.archcheck/ids.json` is the ledger of every id ever committed, and a load fails
|
|
7
|
+
* if a listed id has vanished without a successor declaring `replaces`.
|
|
8
|
+
*
|
|
9
|
+
* What a caller must know: this reads a file but never writes it. `archcheck emit` owns the write,
|
|
10
|
+
* so the ledger grows as a reviewed diff rather than as a side effect of running a check.
|
|
11
|
+
*/
|
|
12
|
+
import { readFileSync } from 'node:fs';
|
|
13
|
+
import { err, ok } from '../result.js';
|
|
14
|
+
export const ID_LEDGER_PATH = '.archcheck/ids.json';
|
|
15
|
+
function unreadableLedger(path, reason) {
|
|
16
|
+
return {
|
|
17
|
+
schemaVersion: 1,
|
|
18
|
+
ids: [],
|
|
19
|
+
unreadable: [
|
|
20
|
+
`the id ledger ${path} exists but could not be read: ${reason}`,
|
|
21
|
+
' → restore it from version control, or delete it and re-run `npx archcheck emit`',
|
|
22
|
+
' an empty ledger would read as "no id has ever been committed", which passes every rename',
|
|
23
|
+
].join('\n'),
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
export function readIdLedger(path) {
|
|
27
|
+
let source;
|
|
28
|
+
try {
|
|
29
|
+
source = readFileSync(path, 'utf8');
|
|
30
|
+
}
|
|
31
|
+
catch (error) {
|
|
32
|
+
// No ledger yet is the normal first-run state, not a failure. Anything else — a directory in
|
|
33
|
+
// its place, a permission error — is one, and must not borrow the first-run answer.
|
|
34
|
+
if (error.code === 'ENOENT')
|
|
35
|
+
return { schemaVersion: 1, ids: [] };
|
|
36
|
+
return unreadableLedger(path, error instanceof Error ? error.message : String(error));
|
|
37
|
+
}
|
|
38
|
+
let parsed;
|
|
39
|
+
try {
|
|
40
|
+
parsed = JSON.parse(source);
|
|
41
|
+
}
|
|
42
|
+
catch (error) {
|
|
43
|
+
return unreadableLedger(path, error instanceof Error ? error.message : String(error));
|
|
44
|
+
}
|
|
45
|
+
if (typeof parsed !== 'object' || parsed === null) {
|
|
46
|
+
return unreadableLedger(path, 'its top level is not a JSON object');
|
|
47
|
+
}
|
|
48
|
+
const listed = parsed.ids;
|
|
49
|
+
if (!Array.isArray(listed))
|
|
50
|
+
return unreadableLedger(path, 'it has no `ids` array');
|
|
51
|
+
const ids = [];
|
|
52
|
+
for (const id of listed) {
|
|
53
|
+
// Dropping a malformed entry would silently shrink the ledger, which is the same failure as
|
|
54
|
+
// not reading it at all.
|
|
55
|
+
if (typeof id !== 'string')
|
|
56
|
+
return unreadableLedger(path, '`ids` holds an entry that is not a string');
|
|
57
|
+
ids.push(id);
|
|
58
|
+
}
|
|
59
|
+
return { schemaVersion: 1, ids };
|
|
60
|
+
}
|
|
61
|
+
/** The ledger `emit` should write: every id ever seen, plus today's, sorted and deduplicated. */
|
|
62
|
+
export function nextIdLedger(previous, rules) {
|
|
63
|
+
const ids = new Set(previous.ids);
|
|
64
|
+
for (const rule of rules)
|
|
65
|
+
ids.add(rule.id);
|
|
66
|
+
return { schemaVersion: 1, ids: [...ids].sort() };
|
|
67
|
+
}
|
|
68
|
+
export function checkIdImmutability(rules, ledger) {
|
|
69
|
+
// A ledger nobody can read proves nothing, so it fails the load rather than passing it.
|
|
70
|
+
if (ledger.unreadable !== undefined)
|
|
71
|
+
return err([ledger.unreadable]);
|
|
72
|
+
const present = new Set(rules.map((r) => r.id));
|
|
73
|
+
const superseded = new Set(rules.map((r) => r.replaces).filter((r) => Boolean(r)));
|
|
74
|
+
const errors = [];
|
|
75
|
+
for (const id of ledger.ids) {
|
|
76
|
+
if (present.has(id) || superseded.has(id))
|
|
77
|
+
continue;
|
|
78
|
+
errors.push([
|
|
79
|
+
`id disappeared: ${id}`,
|
|
80
|
+
` → either restore it, or add a rule with replaces: '${id}'`,
|
|
81
|
+
' ids are immutable because the baseline and every past annotation key off them',
|
|
82
|
+
].join('\n'));
|
|
83
|
+
}
|
|
84
|
+
return errors.length > 0 ? err(errors) : ok(undefined);
|
|
85
|
+
}
|
|
86
|
+
//# sourceMappingURL=ids.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ids.js","sourceRoot":"","sources":["../../src/registry/ids.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import type { ArchitectureException } from '../exceptions/index.js';
|
|
2
|
+
import { type Result } from '../result.js';
|
|
3
|
+
import type { ContractKind, Coverage, Rule } from '../types.js';
|
|
4
|
+
import type { ArchitectureManifest } from './define.js';
|
|
5
|
+
import { type IdLedger } from './ids.js';
|
|
6
|
+
import { type ParamsValidator } from './validate.js';
|
|
7
|
+
export type Registry = {
|
|
8
|
+
readonly root: string;
|
|
9
|
+
readonly configPath: string;
|
|
10
|
+
readonly manifest: ArchitectureManifest;
|
|
11
|
+
all(): readonly Rule[];
|
|
12
|
+
/** Rules that can actually fire — everything except `severity: 'off'`. */
|
|
13
|
+
active(): readonly Rule[];
|
|
14
|
+
byId(id: string): Rule | undefined;
|
|
15
|
+
byKind(kind: ContractKind): readonly Rule[];
|
|
16
|
+
coverage(): Coverage;
|
|
17
|
+
exceptions(): readonly ArchitectureException[];
|
|
18
|
+
/** Things that load fine but somebody should see. Printed, never silently dropped. */
|
|
19
|
+
warnings(): readonly string[];
|
|
20
|
+
/** The id ledger `emit` should write next. */
|
|
21
|
+
ledger(): IdLedger;
|
|
22
|
+
};
|
|
23
|
+
export type OpenOptions = {
|
|
24
|
+
/** Where to start looking for the manifest. Ignored when `configPath` is given. */
|
|
25
|
+
cwd?: string;
|
|
26
|
+
configPath?: string;
|
|
27
|
+
/** Kind-specific params validation, injected by the composition root. */
|
|
28
|
+
validateParams: ParamsValidator;
|
|
29
|
+
/** Kind-specific load-time warnings, injected the same way. */
|
|
30
|
+
collectWarnings?: (rules: readonly Rule[]) => string[];
|
|
31
|
+
/**
|
|
32
|
+
* Whether archcheck can check this rule's kind at all. Injected, because the registry knows a
|
|
33
|
+
* rule has a kind and must never learn what a kind means. Defaults to "yes" so the registry
|
|
34
|
+
* stays testable with no contract kind present.
|
|
35
|
+
*/
|
|
36
|
+
isCheckable?: (rule: Rule) => boolean;
|
|
37
|
+
/** Skip the id-immutability ledger. Only `emit` and tests have a reason to. */
|
|
38
|
+
skipIdCheck?: boolean;
|
|
39
|
+
};
|
|
40
|
+
export declare function open(options: OpenOptions): Result<Registry>;
|
|
41
|
+
export { type ArchitectureManifest, defineArchitecture } from './define.js';
|
|
42
|
+
export { checkIdImmutability, ID_LEDGER_PATH, type IdLedger, nextIdLedger, readIdLedger, } from './ids.js';
|
|
43
|
+
export { CONFIG_FILENAME, findConfig, type LoadedManifest } from './load.js';
|
|
44
|
+
export { type ParamsValidator, validateManifest } from './validate.js';
|
|
45
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/registry/index.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,wBAAwB,CAAC;AAEpE,OAAO,EAAW,KAAK,MAAM,EAAE,MAAM,cAAc,CAAC;AACpD,OAAO,KAAK,EAAE,YAAY,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC;AAChE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AACxD,OAAO,EAAuC,KAAK,QAAQ,EAAgB,MAAM,UAAU,CAAC;AAE5F,OAAO,EAAE,KAAK,eAAe,EAAoB,MAAM,eAAe,CAAC;AAEvE,MAAM,MAAM,QAAQ,GAAG;IACrB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,QAAQ,EAAE,oBAAoB,CAAC;IACxC,GAAG,IAAI,SAAS,IAAI,EAAE,CAAC;IACvB,0EAA0E;IAC1E,MAAM,IAAI,SAAS,IAAI,EAAE,CAAC;IAC1B,IAAI,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;IACnC,MAAM,CAAC,IAAI,EAAE,YAAY,GAAG,SAAS,IAAI,EAAE,CAAC;IAC5C,QAAQ,IAAI,QAAQ,CAAC;IACrB,UAAU,IAAI,SAAS,qBAAqB,EAAE,CAAC;IAC/C,sFAAsF;IACtF,QAAQ,IAAI,SAAS,MAAM,EAAE,CAAC;IAC9B,8CAA8C;IAC9C,MAAM,IAAI,QAAQ,CAAC;CACpB,CAAC;AAsCF,MAAM,MAAM,WAAW,GAAG;IACxB,mFAAmF;IACnF,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,yEAAyE;IACzE,cAAc,EAAE,eAAe,CAAC;IAChC,+DAA+D;IAC/D,eAAe,CAAC,EAAE,CAAC,KAAK,EAAE,SAAS,IAAI,EAAE,KAAK,MAAM,EAAE,CAAC;IACvD;;;;OAIG;IACH,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,OAAO,CAAC;IACtC,+EAA+E;IAC/E,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB,CAAC;AAEF,wBAAgB,IAAI,CAAC,OAAO,EAAE,WAAW,GAAG,MAAM,CAAC,QAAQ,CAAC,CA2B3D;AAED,OAAO,EAAE,KAAK,oBAAoB,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAC5E,OAAO,EACL,mBAAmB,EACnB,cAAc,EACd,KAAK,QAAQ,EACb,YAAY,EACZ,YAAY,GACb,MAAM,UAAU,CAAC;AAClB,OAAO,EAAE,eAAe,EAAE,UAAU,EAAE,KAAK,cAAc,EAAE,MAAM,WAAW,CAAC;AAC7E,OAAO,EAAE,KAAK,eAAe,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The registry — the only thing that knows a rule exists.
|
|
3
|
+
*
|
|
4
|
+
* What rule this enforces: coverage is computed, never stored (plan 03). The three counts come
|
|
5
|
+
* from counting the manifest at load time, so nobody can claim a number the manifest does not
|
|
6
|
+
* support. If someone adds a rule with no real check, the model count goes up and the percentage
|
|
7
|
+
* drops, in the same commit.
|
|
8
|
+
*
|
|
9
|
+
* What a caller must know: `open()` is the only way in, and it returns a Result. Everything past
|
|
10
|
+
* it can assume every rule is well-formed, which is why no downstream module re-validates.
|
|
11
|
+
*/
|
|
12
|
+
import { join } from 'node:path';
|
|
13
|
+
import { validateExceptions } from '../exceptions/index.js';
|
|
14
|
+
import { err, ok } from '../result.js';
|
|
15
|
+
import { checkIdImmutability, ID_LEDGER_PATH, readIdLedger } from './ids.js';
|
|
16
|
+
import { loadManifest, loadManifestFrom } from './load.js';
|
|
17
|
+
import { validateManifest } from './validate.js';
|
|
18
|
+
function createRegistry(loaded, ledger, warnings, isCheckable) {
|
|
19
|
+
const rules = loaded.manifest.rules;
|
|
20
|
+
const byId = new Map(rules.map((rule) => [rule.id, rule]));
|
|
21
|
+
return {
|
|
22
|
+
root: loaded.root,
|
|
23
|
+
configPath: loaded.configPath,
|
|
24
|
+
manifest: loaded.manifest,
|
|
25
|
+
all: () => rules,
|
|
26
|
+
active: () => rules.filter((rule) => rule.severity !== 'off'),
|
|
27
|
+
byId: (id) => byId.get(id),
|
|
28
|
+
byKind: (kind) => rules.filter((rule) => rule.kind === kind),
|
|
29
|
+
coverage: () => {
|
|
30
|
+
const active = rules.filter((rule) => rule.severity !== 'off');
|
|
31
|
+
const checkable = active.filter(isCheckable);
|
|
32
|
+
const count = (confidence) => checkable.filter((rule) => rule.confidence === confidence).length;
|
|
33
|
+
return {
|
|
34
|
+
total: active.length,
|
|
35
|
+
deterministic: count('deterministic'),
|
|
36
|
+
proxy: count('proxy'),
|
|
37
|
+
model: count('model'),
|
|
38
|
+
unchecked: active.length - checkable.length,
|
|
39
|
+
};
|
|
40
|
+
},
|
|
41
|
+
exceptions: () => loaded.manifest.exceptions ?? [],
|
|
42
|
+
warnings: () => warnings,
|
|
43
|
+
ledger: () => ledger,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
export function open(options) {
|
|
47
|
+
const loadedResult = options.configPath
|
|
48
|
+
? loadManifestFrom(options.configPath)
|
|
49
|
+
: loadManifest(options.cwd ?? process.cwd());
|
|
50
|
+
if (!loadedResult.ok)
|
|
51
|
+
return loadedResult;
|
|
52
|
+
const loaded = loadedResult.value;
|
|
53
|
+
const validated = validateManifest(loaded.manifest, options.validateParams);
|
|
54
|
+
if (!validated.ok)
|
|
55
|
+
return err(validated.error);
|
|
56
|
+
const exceptionsOk = validateExceptions(loaded.manifest.exceptions ?? [], loaded.manifest.rules);
|
|
57
|
+
if (!exceptionsOk.ok)
|
|
58
|
+
return err(exceptionsOk.error);
|
|
59
|
+
const ledger = readIdLedger(join(loaded.root, ID_LEDGER_PATH));
|
|
60
|
+
if (options.skipIdCheck !== true) {
|
|
61
|
+
const immutable = checkIdImmutability(loaded.manifest.rules, ledger);
|
|
62
|
+
if (!immutable.ok)
|
|
63
|
+
return err(immutable.error);
|
|
64
|
+
}
|
|
65
|
+
return ok(createRegistry(loaded, ledger, options.collectWarnings?.(loaded.manifest.rules) ?? [], options.isCheckable ?? (() => true)));
|
|
66
|
+
}
|
|
67
|
+
export { defineArchitecture } from './define.js';
|
|
68
|
+
export { checkIdImmutability, ID_LEDGER_PATH, nextIdLedger, readIdLedger, } from './ids.js';
|
|
69
|
+
export { CONFIG_FILENAME, findConfig } from './load.js';
|
|
70
|
+
export { validateManifest } from './validate.js';
|
|
71
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/registry/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { type Result } from '../result.js';
|
|
2
|
+
import { type ArchitectureManifest } from './define.js';
|
|
3
|
+
export declare const CONFIG_FILENAME = "architecture.config.ts";
|
|
4
|
+
/** Walk up from `start` looking for the manifest. Returns its absolute path, or null. */
|
|
5
|
+
export declare function findConfig(start: string): string | null;
|
|
6
|
+
export type LoadedManifest = {
|
|
7
|
+
manifest: ArchitectureManifest;
|
|
8
|
+
configPath: string;
|
|
9
|
+
/** Absolute, and the base every relative path in the manifest resolves against. */
|
|
10
|
+
root: string;
|
|
11
|
+
};
|
|
12
|
+
export declare function loadManifestFrom(configPath: string): Result<LoadedManifest>;
|
|
13
|
+
export declare function loadManifest(startDir: string): Result<LoadedManifest>;
|
|
14
|
+
//# sourceMappingURL=load.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"load.d.ts","sourceRoot":"","sources":["../../src/registry/load.ts"],"names":[],"mappings":"AAsBA,OAAO,EAAW,KAAK,MAAM,EAAE,MAAM,cAAc,CAAC;AAEpD,OAAO,EAAE,KAAK,oBAAoB,EAAsB,MAAM,aAAa,CAAC;AAE5E,eAAO,MAAM,eAAe,2BAA2B,CAAC;AAExD,yFAAyF;AACzF,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CASvD;AAaD,MAAM,MAAM,cAAc,GAAG;IAC3B,QAAQ,EAAE,oBAAoB,CAAC;IAC/B,UAAU,EAAE,MAAM,CAAC;IACnB,mFAAmF;IACnF,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAEF,wBAAgB,gBAAgB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAAC,cAAc,CAAC,CAiE3E;AAED,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAAC,cAAc,CAAC,CASrE"}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Finding, compiling and evaluating `architecture.config.ts`.
|
|
3
|
+
*
|
|
4
|
+
* What this is for: the manifest is TypeScript, so it has to be compiled to be read. It is
|
|
5
|
+
* compiled with the consumer's own `typescript` (our single peer dependency) — no bundler, no
|
|
6
|
+
* ts-node, and no temporary file on disk, because a tool that scatters temp files into a repo it
|
|
7
|
+
* is auditing has already lost the argument.
|
|
8
|
+
*
|
|
9
|
+
* How: transpile to CommonJS in memory, then evaluate the result with an injected `require`. The
|
|
10
|
+
* one specifier a manifest actually needs at run time is `archcheck` itself, and that resolves to
|
|
11
|
+
* a value we already hold — so the common case needs no module resolution at all, and works
|
|
12
|
+
* identically under Node and Bun. Anything else the manifest imports goes through Node's own
|
|
13
|
+
* resolver, rooted at the config file.
|
|
14
|
+
*
|
|
15
|
+
* What a caller must know: this executes the config. That is what a config file is for, and it is
|
|
16
|
+
* the operator's own committed code — but it does mean `load()` is not a safe place to point at an
|
|
17
|
+
* untrusted file.
|
|
18
|
+
*/
|
|
19
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
20
|
+
import { createRequire } from 'node:module';
|
|
21
|
+
import { dirname, join, resolve } from 'node:path';
|
|
22
|
+
import ts from 'typescript';
|
|
23
|
+
import { err, ok } from '../result.js';
|
|
24
|
+
import { missingCompilerApi } from '../util/compilerApi.js';
|
|
25
|
+
import { defineArchitecture } from './define.js';
|
|
26
|
+
export const CONFIG_FILENAME = 'architecture.config.ts';
|
|
27
|
+
/** Walk up from `start` looking for the manifest. Returns its absolute path, or null. */
|
|
28
|
+
export function findConfig(start) {
|
|
29
|
+
let dir = resolve(start);
|
|
30
|
+
for (;;) {
|
|
31
|
+
const candidate = join(dir, CONFIG_FILENAME);
|
|
32
|
+
if (existsSync(candidate))
|
|
33
|
+
return candidate;
|
|
34
|
+
const parent = dirname(dir);
|
|
35
|
+
if (parent === dir)
|
|
36
|
+
return null;
|
|
37
|
+
dir = parent;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
/** The module we hand back for `import ... from 'archcheck'`, without resolving anything. */
|
|
41
|
+
function archcheckModule() {
|
|
42
|
+
return { defineArchitecture, default: { defineArchitecture } };
|
|
43
|
+
}
|
|
44
|
+
function isArchcheckSpecifier(specifier) {
|
|
45
|
+
if (specifier === 'archcheck' || specifier.startsWith('archcheck/'))
|
|
46
|
+
return true;
|
|
47
|
+
// archcheck's own manifest reaches its source directly; treat that as the same module.
|
|
48
|
+
return /(^|\/)src\/index(\.[jt]s)?$/.test(specifier) || /(^|\/)dist\/index\.js$/.test(specifier);
|
|
49
|
+
}
|
|
50
|
+
export function loadManifestFrom(configPath) {
|
|
51
|
+
const unusable = missingCompilerApi(ts);
|
|
52
|
+
if (unusable)
|
|
53
|
+
return err(unusable);
|
|
54
|
+
let source;
|
|
55
|
+
try {
|
|
56
|
+
source = readFileSync(configPath, 'utf8');
|
|
57
|
+
}
|
|
58
|
+
catch (error) {
|
|
59
|
+
return err([`cannot read ${configPath}: ${error.message}`]);
|
|
60
|
+
}
|
|
61
|
+
const transpiled = ts.transpileModule(source, {
|
|
62
|
+
fileName: configPath,
|
|
63
|
+
reportDiagnostics: true,
|
|
64
|
+
compilerOptions: {
|
|
65
|
+
module: ts.ModuleKind.CommonJS,
|
|
66
|
+
target: ts.ScriptTarget.ES2022,
|
|
67
|
+
esModuleInterop: true,
|
|
68
|
+
isolatedModules: true,
|
|
69
|
+
},
|
|
70
|
+
});
|
|
71
|
+
const syntaxErrors = (transpiled.diagnostics ?? []).filter((d) => d.category === ts.DiagnosticCategory.Error);
|
|
72
|
+
if (syntaxErrors.length > 0) {
|
|
73
|
+
return err(syntaxErrors.map((d) => `${CONFIG_FILENAME}: ${ts.flattenDiagnosticMessageText(d.messageText, ' ')}`));
|
|
74
|
+
}
|
|
75
|
+
const nodeRequire = createRequire(configPath);
|
|
76
|
+
const requireShim = (specifier) => isArchcheckSpecifier(specifier) ? archcheckModule() : nodeRequire(specifier);
|
|
77
|
+
const moduleShim = { exports: {} };
|
|
78
|
+
try {
|
|
79
|
+
// `new Function` rather than `eval`: the config body gets its own scope with exactly the five
|
|
80
|
+
// CommonJS bindings it expects and nothing of ours. Compiling the operator's own committed
|
|
81
|
+
// config is the job of this module, not an escape hatch.
|
|
82
|
+
const evaluate = new Function('exports', 'require', 'module', '__filename', '__dirname', transpiled.outputText);
|
|
83
|
+
evaluate(moduleShim.exports, requireShim, moduleShim, configPath, dirname(configPath));
|
|
84
|
+
}
|
|
85
|
+
catch (error) {
|
|
86
|
+
return err([`${CONFIG_FILENAME} threw while loading: ${error.message}`]);
|
|
87
|
+
}
|
|
88
|
+
const exported = moduleShim.exports.default ?? moduleShim.exports;
|
|
89
|
+
if (typeof exported !== 'object' || exported === null) {
|
|
90
|
+
return err([
|
|
91
|
+
`${CONFIG_FILENAME} must export a default: export default defineArchitecture({ rules: [...] })`,
|
|
92
|
+
]);
|
|
93
|
+
}
|
|
94
|
+
const manifest = exported;
|
|
95
|
+
const root = manifest.root ? resolve(dirname(configPath), manifest.root) : dirname(configPath);
|
|
96
|
+
return ok({ manifest, configPath, root });
|
|
97
|
+
}
|
|
98
|
+
export function loadManifest(startDir) {
|
|
99
|
+
const configPath = findConfig(startDir);
|
|
100
|
+
if (configPath === null) {
|
|
101
|
+
return err([
|
|
102
|
+
`no ${CONFIG_FILENAME} found in ${resolve(startDir)} or any parent directory`,
|
|
103
|
+
' → create one: export default defineArchitecture({ rules: [] })',
|
|
104
|
+
]);
|
|
105
|
+
}
|
|
106
|
+
return loadManifestFrom(configPath);
|
|
107
|
+
}
|
|
108
|
+
//# sourceMappingURL=load.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"load.js","sourceRoot":"","sources":["../../src/registry/load.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Manifest validation. Total, and it never throws.
|
|
3
|
+
*
|
|
4
|
+
* What rule this enforces: a malformed manifest exits 2, never 1 (LLD §6). An agent must be able
|
|
5
|
+
* to tell "the config is broken" from "the code is broken", or it will spend its turn editing the
|
|
6
|
+
* wrong file. That means every problem is returned together — one pass, every field named —
|
|
7
|
+
* rather than thrown one at a time.
|
|
8
|
+
*
|
|
9
|
+
* What a caller must know: the per-kind params check is INJECTED, not imported. The registry knows
|
|
10
|
+
* that a rule has a kind; only the contract type knows what that kind's params mean. Wiring the
|
|
11
|
+
* two together is the facade's job, which keeps this module testable without any kind at all.
|
|
12
|
+
*/
|
|
13
|
+
import { type Result } from '../result.js';
|
|
14
|
+
import { type Rule } from '../types.js';
|
|
15
|
+
import type { ArchitectureManifest } from './define.js';
|
|
16
|
+
/**
|
|
17
|
+
* The seam: kind-specific params validation, supplied by the contracts layer. It receives the
|
|
18
|
+
* whole rule set because one question genuinely needs it — "does this mayImport name a layer that
|
|
19
|
+
* exists?" — and passing it here rather than closing over it keeps the registry's only entry point
|
|
20
|
+
* a single call.
|
|
21
|
+
*/
|
|
22
|
+
export type ParamsValidator = (rule: Rule, rules: readonly Rule[]) => Result<void>;
|
|
23
|
+
export declare function validateManifest(manifest: ArchitectureManifest, validateParams: ParamsValidator): Result<ArchitectureManifest>;
|
|
24
|
+
//# sourceMappingURL=validate.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../../src/registry/validate.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,EAAW,KAAK,MAAM,EAAE,MAAM,cAAc,CAAC;AACpD,OAAO,EAAqC,KAAK,IAAI,EAAE,MAAM,aAAa,CAAC;AAE3E,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAExD;;;;;GAKG;AACH,MAAM,MAAM,eAAe,GAAG,CAAC,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,SAAS,IAAI,EAAE,KAAK,MAAM,CAAC,IAAI,CAAC,CAAC;AAuInF,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,oBAAoB,EAC9B,cAAc,EAAE,eAAe,GAC9B,MAAM,CAAC,oBAAoB,CAAC,CA6C9B"}
|