@goodbones/core 0.1.0-beta.10 → 0.1.0-beta.12
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/build/dts/core/structure.d.ts.map +1 -1
- package/build/dts/domain/architecture-config.d.ts +3 -310
- package/build/dts/domain/architecture-config.d.ts.map +1 -1
- package/build/dts/domain/snapshot.d.ts +150 -18
- package/build/dts/domain/snapshot.d.ts.map +1 -1
- package/build/dts/index.d.ts +11 -14
- package/build/dts/index.d.ts.map +1 -1
- package/build/dts/infrastructure/file-system-fake.d.ts.map +1 -1
- package/build/dts/infrastructure/file-system-live.d.ts.map +1 -1
- package/build/dts/infrastructure/walk.d.ts +6 -1
- package/build/dts/infrastructure/walk.d.ts.map +1 -1
- package/build/dts/load/extension.d.ts +33 -0
- package/build/dts/load/extension.d.ts.map +1 -0
- package/build/dts/load/policy.d.ts +4 -10
- package/build/dts/load/policy.d.ts.map +1 -1
- package/build/dts/manifest/compile.d.ts +7 -2
- package/build/dts/manifest/compile.d.ts.map +1 -1
- package/build/dts/manifest/extension.d.ts +8 -0
- package/build/dts/manifest/extension.d.ts.map +1 -0
- package/build/dts/manifest/glob.d.ts +1 -0
- package/build/dts/manifest/glob.d.ts.map +1 -1
- package/build/dts/manifest/json-schema.d.ts +6 -2
- package/build/dts/manifest/json-schema.d.ts.map +1 -1
- package/build/dts/manifest/manifest.d.ts +7 -362
- package/build/dts/manifest/manifest.d.ts.map +1 -1
- package/build/dts/ports/file-system.d.ts +1 -0
- package/build/dts/ports/file-system.d.ts.map +1 -1
- package/build/dts/testing.d.ts +0 -1
- package/build/dts/testing.d.ts.map +1 -1
- package/build/esm/core/structure.js +1 -1
- package/build/esm/core/structure.js.map +1 -1
- package/build/esm/domain/architecture-config.js +3 -136
- package/build/esm/domain/architecture-config.js.map +1 -1
- package/build/esm/domain/snapshot.js +55 -15
- package/build/esm/domain/snapshot.js.map +1 -1
- package/build/esm/index.js +16 -10
- package/build/esm/index.js.map +1 -1
- package/build/esm/infrastructure/file-system-fake.js +10 -0
- package/build/esm/infrastructure/file-system-fake.js.map +1 -1
- package/build/esm/infrastructure/file-system-live.js +9 -1
- package/build/esm/infrastructure/file-system-live.js.map +1 -1
- package/build/esm/infrastructure/walk.js +50 -4
- package/build/esm/infrastructure/walk.js.map +1 -1
- package/build/esm/load/extension.js +2 -0
- package/build/esm/load/extension.js.map +1 -0
- package/build/esm/load/policy.js +43 -116
- package/build/esm/load/policy.js.map +1 -1
- package/build/esm/manifest/compile.js +3 -182
- package/build/esm/manifest/compile.js.map +1 -1
- package/build/esm/manifest/extension.js +2 -0
- package/build/esm/manifest/extension.js.map +1 -0
- package/build/esm/manifest/glob.js +3 -0
- package/build/esm/manifest/glob.js.map +1 -1
- package/build/esm/manifest/json-schema.js +45 -11
- package/build/esm/manifest/json-schema.js.map +1 -1
- package/build/esm/manifest/manifest.js +56 -172
- package/build/esm/manifest/manifest.js.map +1 -1
- package/build/esm/testing.js +0 -1
- package/build/esm/testing.js.map +1 -1
- package/package.json +1 -1
- package/schema/architecture-node.schema.json +0 -887
- package/schema/architecture.schema.json +3264 -1591
- package/schema/conformance.schema.json +291 -47
- package/src/core/structure.ts +5 -3
- package/src/domain/architecture-config.ts +3 -180
- package/src/domain/snapshot.ts +125 -25
- package/src/index.ts +51 -78
- package/src/infrastructure/file-system-fake.ts +12 -0
- package/src/infrastructure/file-system-live.ts +8 -1
- package/src/infrastructure/walk.ts +58 -3
- package/src/load/extension.ts +66 -0
- package/src/load/policy.ts +60 -175
- package/src/manifest/compile.ts +2 -213
- package/src/manifest/extension.ts +41 -0
- package/src/manifest/glob.ts +5 -0
- package/src/manifest/json-schema.ts +53 -11
- package/src/manifest/manifest.ts +82 -214
- package/src/ports/file-system.ts +3 -0
- package/src/testing.ts +0 -1
- package/build/dts/core/campaigns.d.ts +0 -141
- package/build/dts/core/campaigns.d.ts.map +0 -1
- package/build/dts/core/ledger.d.ts +0 -61
- package/build/dts/core/ledger.d.ts.map +0 -1
- package/build/dts/domain/report.d.ts +0 -16
- package/build/dts/domain/report.d.ts.map +0 -1
- package/build/dts/infrastructure/campaign-functions.d.ts +0 -7
- package/build/dts/infrastructure/campaign-functions.d.ts.map +0 -1
- package/build/dts/infrastructure/report-source-fake.d.ts +0 -4
- package/build/dts/infrastructure/report-source-fake.d.ts.map +0 -1
- package/build/dts/infrastructure/report-source-live.d.ts +0 -3
- package/build/dts/infrastructure/report-source-live.d.ts.map +0 -1
- package/build/dts/ports/campaign-predicate.d.ts +0 -24
- package/build/dts/ports/campaign-predicate.d.ts.map +0 -1
- package/build/dts/ports/report-source.d.ts +0 -14
- package/build/dts/ports/report-source.d.ts.map +0 -1
- package/build/esm/core/campaigns.js +0 -778
- package/build/esm/core/campaigns.js.map +0 -1
- package/build/esm/core/ledger.js +0 -171
- package/build/esm/core/ledger.js.map +0 -1
- package/build/esm/domain/report.js +0 -168
- package/build/esm/domain/report.js.map +0 -1
- package/build/esm/infrastructure/campaign-functions.js +0 -67
- package/build/esm/infrastructure/campaign-functions.js.map +0 -1
- package/build/esm/infrastructure/report-source-fake.js +0 -6
- package/build/esm/infrastructure/report-source-fake.js.map +0 -1
- package/build/esm/infrastructure/report-source-live.js +0 -165
- package/build/esm/infrastructure/report-source-live.js.map +0 -1
- package/build/esm/ports/campaign-predicate.js +0 -2
- package/build/esm/ports/campaign-predicate.js.map +0 -1
- package/build/esm/ports/report-source.js +0 -7
- package/build/esm/ports/report-source.js.map +0 -1
- package/src/core/campaigns.ts +0 -1056
- package/src/core/ledger.ts +0 -242
- package/src/domain/report.ts +0 -203
- package/src/infrastructure/campaign-functions.ts +0 -98
- package/src/infrastructure/report-source-fake.ts +0 -10
- package/src/infrastructure/report-source-live.ts +0 -192
- package/src/ports/campaign-predicate.ts +0 -30
- package/src/ports/report-source.ts +0 -35
|
@@ -31,14 +31,21 @@ const isObject = (value: JsonValue): value is JsonObject =>
|
|
|
31
31
|
|
|
32
32
|
const isList = (value: JsonValue): value is ReadonlyArray<JsonValue> => Array.isArray(value);
|
|
33
33
|
|
|
34
|
-
// The generator names the recursive schemas after its own internal wrappers
|
|
35
|
-
// A stable name is what a `$ref` in an
|
|
36
|
-
// point at: the tree's node, and
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
34
|
+
// The generator names the recursive schemas after its own internal wrappers,
|
|
35
|
+
// numbered in the order it meets them. A stable name is what a `$ref` in an
|
|
36
|
+
// error message or a docs page can point at: the tree's node, and an
|
|
37
|
+
// objective's detector — told apart by what each declares, since the order
|
|
38
|
+
// moves with the manifest's shape.
|
|
39
|
+
const definitionNameOf = (name: string, definition: JsonValue): string => {
|
|
40
|
+
if (!/^(Suspend|Union)_\d*$/.test(name)) return name;
|
|
41
|
+
const text = JSON.stringify(definition);
|
|
42
|
+
if (text.includes('"children"')) return "ManifestNode";
|
|
43
|
+
if (text.includes('"all"')) return "Detector";
|
|
44
|
+
return name;
|
|
40
45
|
};
|
|
41
46
|
|
|
47
|
+
const DEFINITION_NAMES: Record<string, string> = {};
|
|
48
|
+
|
|
42
49
|
const USE_REFERENCE = "#/$defs/Use";
|
|
43
50
|
const INCLUDE_REFERENCE = "#/$defs/Include";
|
|
44
51
|
|
|
@@ -98,8 +105,40 @@ const SCHEMA_PROPERTY: JsonObject = {
|
|
|
98
105
|
description: "For editors. Ignored by the loader.",
|
|
99
106
|
};
|
|
100
107
|
|
|
101
|
-
|
|
102
|
-
|
|
108
|
+
// A `Record` whose keys are checked — the campaign and objective ids —
|
|
109
|
+
// generates as `patternProperties`, which alone admits any other key beside
|
|
110
|
+
// the matching ones. Closed here, so an editor flags an id in the wrong
|
|
111
|
+
// shape as the loader would.
|
|
112
|
+
const withKeyPatterns = (value: JsonValue): JsonValue => {
|
|
113
|
+
if (Array.isArray(value)) return value.map(withKeyPatterns);
|
|
114
|
+
if (!isObject(value)) return value;
|
|
115
|
+
const rebuilt: Record<string, JsonValue> = {};
|
|
116
|
+
for (const [childKey, entry] of entriesOf(value)) rebuilt[childKey] = withKeyPatterns(entry);
|
|
117
|
+
if (
|
|
118
|
+
rebuilt.type === "object" &&
|
|
119
|
+
"patternProperties" in rebuilt &&
|
|
120
|
+
!("additionalProperties" in rebuilt)
|
|
121
|
+
) {
|
|
122
|
+
rebuilt.additionalProperties = false;
|
|
123
|
+
}
|
|
124
|
+
return rebuilt;
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
export type ManifestJsonSchemaOptions = {
|
|
128
|
+
// The manifest keys of families the core does not own, as the fields of
|
|
129
|
+
// their own codecs — `CampaignsManifest.fields` from @goodbones/campaigns.
|
|
130
|
+
// Generated together with the core's, so the published schema describes the
|
|
131
|
+
// manifest a host with those families loaded will actually decode, and a
|
|
132
|
+
// host without them publishes a schema without those keys.
|
|
133
|
+
readonly extensions?: ReadonlyArray<Schema.Struct.Fields> | undefined;
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
export const manifestJsonSchema = (options: ManifestJsonSchemaOptions = {}): JsonObject => {
|
|
137
|
+
const whole =
|
|
138
|
+
options.extensions === undefined || options.extensions.length === 0
|
|
139
|
+
? Manifest
|
|
140
|
+
: Schema.Struct(Object.assign({}, Manifest.fields, ...options.extensions));
|
|
141
|
+
const generated = Schema.toJsonSchemaDocument(whole) as unknown as {
|
|
103
142
|
readonly schema: JsonObject;
|
|
104
143
|
readonly definitions: JsonObject;
|
|
105
144
|
};
|
|
@@ -108,6 +147,9 @@ export const manifestJsonSchema = (): JsonObject => {
|
|
|
108
147
|
throw new Error("the manifest schema generated with no properties");
|
|
109
148
|
}
|
|
110
149
|
|
|
150
|
+
for (const [name, definition] of entriesOf(generated.definitions)) {
|
|
151
|
+
DEFINITION_NAMES[name] = definitionNameOf(name, definition);
|
|
152
|
+
}
|
|
111
153
|
const definitions: Record<string, JsonValue> = {};
|
|
112
154
|
for (const [name, definition] of entriesOf(generated.definitions)) {
|
|
113
155
|
definitions[DEFINITION_NAMES[name] ?? name] = admitReferences(definition);
|
|
@@ -124,7 +166,7 @@ export const manifestJsonSchema = (): JsonObject => {
|
|
|
124
166
|
$schema: SCHEMA_PROPERTY,
|
|
125
167
|
defs: DEFS_PROPERTY,
|
|
126
168
|
...Object.fromEntries(
|
|
127
|
-
entriesOf(properties).map(([key, value]) => [key, admitReferences(value)]),
|
|
169
|
+
entriesOf(properties).map(([key, value]) => [key, withKeyPatterns(admitReferences(value))]),
|
|
128
170
|
),
|
|
129
171
|
},
|
|
130
172
|
$defs: { ...definitions, Use, Include },
|
|
@@ -135,8 +177,8 @@ export const manifestJsonSchema = (): JsonObject => {
|
|
|
135
177
|
// `architecture.yaml` that the root manifest `include`s is shaped like. The
|
|
136
178
|
// node's object form, with the `$schema` and `defs` keys such a file may carry
|
|
137
179
|
// at the top, over the same definitions as the whole manifest.
|
|
138
|
-
export const manifestNodeJsonSchema = (): JsonObject => {
|
|
139
|
-
const whole = manifestJsonSchema();
|
|
180
|
+
export const manifestNodeJsonSchema = (options: ManifestJsonSchemaOptions = {}): JsonObject => {
|
|
181
|
+
const whole = manifestJsonSchema(options);
|
|
140
182
|
const definitions = whole.$defs;
|
|
141
183
|
if (definitions === undefined || !isObject(definitions)) {
|
|
142
184
|
throw new Error("the manifest schema generated with no definitions");
|
package/src/manifest/manifest.ts
CHANGED
|
@@ -2,14 +2,7 @@ import * as Result from "effect/Result";
|
|
|
2
2
|
import * as Schema from "effect/Schema";
|
|
3
3
|
import * as SchemaIssue from "effect/SchemaIssue";
|
|
4
4
|
|
|
5
|
-
import {
|
|
6
|
-
CampaignUnit,
|
|
7
|
-
DeclarationKind,
|
|
8
|
-
ImportProbeTarget,
|
|
9
|
-
ProbeDiagnostic,
|
|
10
|
-
ReportFormat,
|
|
11
|
-
ResolveConfig,
|
|
12
|
-
} from "../domain/architecture-config.js";
|
|
5
|
+
import { DeclarationKind, ResolveConfig } from "../domain/architecture-config.js";
|
|
13
6
|
import { ConfigInvalid } from "../domain/architecture-error.js";
|
|
14
7
|
import {
|
|
15
8
|
type ManifestLocator,
|
|
@@ -17,6 +10,7 @@ import {
|
|
|
17
10
|
renderManifestPath,
|
|
18
11
|
} from "../domain/manifest-location.js";
|
|
19
12
|
import { expandManifest, originOf, type Substitution } from "./expand.js";
|
|
13
|
+
import type { ManifestExtension } from "./extension.js";
|
|
20
14
|
|
|
21
15
|
// A manifest is a tree of nodes keyed by path pattern, where everything the
|
|
22
16
|
// architecture says about a part of the tree is written at that part of the tree.
|
|
@@ -112,7 +106,7 @@ const Members = Schema.Struct({
|
|
|
112
106
|
// is about; exactly one demand says what is required of them, and none means
|
|
113
107
|
// `forbid` — a selected site is the violation. Stated on a folder it covers
|
|
114
108
|
// the subtree, like `members`.
|
|
115
|
-
const SurfaceConvention = Schema.Union([
|
|
109
|
+
export const SurfaceConvention = Schema.Union([
|
|
116
110
|
Schema.Literals(["kebab-case", "camelCase", "PascalCase", "snake_case"]),
|
|
117
111
|
Schema.Struct({ regex: Schema.String }),
|
|
118
112
|
]);
|
|
@@ -281,201 +275,27 @@ const Limits = Schema.Struct({
|
|
|
281
275
|
conformance: Schema.optionalKey(ConformanceCeilings),
|
|
282
276
|
});
|
|
283
277
|
|
|
284
|
-
// A campaign: a migration the repository is running, tracked as an object —
|
|
285
|
-
// a detector, a rationale, a guide, an owner, a definition of done, and a
|
|
286
|
-
// ledger of every place the pattern still occurs. Where a rule says what may
|
|
287
|
-
// never happen, a campaign names what the code is moving away from.
|
|
288
|
-
//
|
|
289
|
-
// The detector is a predicate algebra: `all`, `any` and `not` over leaf
|
|
290
|
-
// terms, the same three words ast-grep uses. Leaf terms reuse the other
|
|
291
|
-
// families' vocabularies where one exists.
|
|
292
|
-
|
|
293
|
-
// A regular expression over the whole repo-relative path, matched as the
|
|
294
|
-
// `structure` naming rules are: with `subject` and `convention`, the capture
|
|
295
|
-
// group named holds the name being judged, and the term holds when the name
|
|
296
|
-
// has the convention's shape.
|
|
297
|
-
const PathTerm = Schema.Struct({
|
|
298
|
-
file: Globs,
|
|
299
|
-
fileNot: Schema.optionalKey(Globs),
|
|
300
|
-
subject: Schema.optionalKey(Schema.Finite),
|
|
301
|
-
convention: Schema.optionalKey(SurfaceConvention),
|
|
302
|
-
});
|
|
303
278
|
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
const ImportsTerm = Schema.Struct({
|
|
308
|
-
resolves: ImportProbeTarget,
|
|
309
|
-
symbols: Schema.optionalKey(Schema.Array(Schema.String)),
|
|
279
|
+
const decodeTree = Schema.decodeUnknownResult(Schema.Record(Schema.String, ManifestNodeSchema), {
|
|
280
|
+
errors: "all",
|
|
281
|
+
onExcessProperty: "error",
|
|
310
282
|
});
|
|
311
283
|
|
|
312
|
-
//
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
});
|
|
327
|
-
|
|
328
|
-
// A regular expression over the file's text, multiline.
|
|
329
|
-
const ContentTerm = Schema.Struct({ regex: Schema.String });
|
|
330
|
-
|
|
331
|
-
// How a metavariable is narrowed: by the text it captured, or by what the
|
|
332
|
-
// identifier at its root is bound to — the module it resolves to and the
|
|
333
|
-
// name it was imported as (`member`), which is how `class $N extends $BASE`
|
|
334
|
-
// says "a React component" rather than "any class with a base".
|
|
335
|
-
const CaptureNarrowing = Schema.Struct({
|
|
336
|
-
regex: Schema.optionalKey(Schema.String),
|
|
337
|
-
binding: Schema.optionalKey(
|
|
338
|
-
Schema.Struct({
|
|
339
|
-
resolves: ImportProbeTarget,
|
|
340
|
-
member: Schema.optionalKey(Schema.Array(Schema.String)),
|
|
341
|
-
}),
|
|
342
|
-
),
|
|
343
|
-
});
|
|
344
|
-
|
|
345
|
-
// An ast-grep rule object — `pattern`, `kind`, `regex`, `has`, `inside`,
|
|
346
|
-
// `precedes`, `follows`, `nthChild`, `all`, `any`, `not` — with `where`
|
|
347
|
-
// beside it. The rule is the engine's to validate; the manifest only knows
|
|
348
|
-
// which keys are a rule's and which is the narrowing. `kind` names are the
|
|
349
|
-
// engine's node kinds, which today are tree-sitter's.
|
|
350
|
-
const SyntaxTerm = Schema.Struct({
|
|
351
|
-
pattern: Schema.optionalKey(Schema.Unknown),
|
|
352
|
-
kind: Schema.optionalKey(Schema.Unknown),
|
|
353
|
-
regex: Schema.optionalKey(Schema.Unknown),
|
|
354
|
-
nthChild: Schema.optionalKey(Schema.Unknown),
|
|
355
|
-
inside: Schema.optionalKey(Schema.Unknown),
|
|
356
|
-
has: Schema.optionalKey(Schema.Unknown),
|
|
357
|
-
precedes: Schema.optionalKey(Schema.Unknown),
|
|
358
|
-
follows: Schema.optionalKey(Schema.Unknown),
|
|
359
|
-
all: Schema.optionalKey(Schema.Unknown),
|
|
360
|
-
any: Schema.optionalKey(Schema.Unknown),
|
|
361
|
-
not: Schema.optionalKey(Schema.Unknown),
|
|
362
|
-
where: Schema.optionalKey(Schema.Record(Schema.String, CaptureNarrowing)),
|
|
363
|
-
});
|
|
364
|
-
|
|
365
|
-
// One program to run or file to read, or several: a tool that takes one
|
|
366
|
-
// project at a time is run once per project, and the outputs are read as
|
|
367
|
-
// one report — a diagnostic two of them print is one diagnostic.
|
|
368
|
-
const ReportSources = Schema.Union([
|
|
369
|
-
Schema.String,
|
|
370
|
-
Schema.Array(Schema.String).check(Schema.isMinLength(1)),
|
|
371
|
-
]);
|
|
372
|
-
|
|
373
|
-
// A finding of another program, read from the output of `command` (run
|
|
374
|
-
// from the repository root, once per `check`) or from `file` (written by
|
|
375
|
-
// an earlier step) in one of the known formats — `tsc`, `eslint --format
|
|
376
|
-
// json`, `oxlint --format json` — or by a `regex` with named groups. Holds
|
|
377
|
-
// for each diagnostic on the file whose code the term speaks to.
|
|
378
|
-
const ReportTerm = Schema.Struct({
|
|
379
|
-
command: Schema.optionalKey(ReportSources),
|
|
380
|
-
file: Schema.optionalKey(ReportSources),
|
|
381
|
-
format: ReportFormat,
|
|
382
|
-
pattern: Schema.optionalKey(Schema.String),
|
|
383
|
-
codes: Schema.optionalKey(Schema.Array(Schema.String)),
|
|
384
|
-
codesNot: Schema.optionalKey(Schema.Array(Schema.String)),
|
|
385
|
-
}).check(
|
|
386
|
-
// Refused at decode, where the issue names a line, rather than in the
|
|
387
|
-
// lowering: a term that names both sources or neither, and a `regex`
|
|
388
|
-
// term with nothing to match lines against.
|
|
389
|
-
Schema.makeFilter((term) => {
|
|
390
|
-
const issues: Array<Schema.FilterIssue> = [];
|
|
391
|
-
if ((term.command === undefined) === (term.file === undefined)) {
|
|
392
|
-
issues.push(
|
|
393
|
-
"a report term names exactly one of `command` (a program to run) and `file` (a report already written)",
|
|
394
|
-
);
|
|
395
|
-
}
|
|
396
|
-
if (term.format === "regex" && term.pattern === undefined) {
|
|
397
|
-
issues.push({
|
|
398
|
-
path: ["pattern"],
|
|
399
|
-
issue: "a `regex` report term needs a `pattern` with named groups `file` and `line`",
|
|
400
|
-
});
|
|
401
|
-
}
|
|
402
|
-
return issues;
|
|
403
|
-
}),
|
|
404
|
-
);
|
|
405
|
-
|
|
406
|
-
export type DetectorSpec =
|
|
407
|
-
| { readonly all: ReadonlyArray<DetectorSpec> }
|
|
408
|
-
| { readonly any: ReadonlyArray<DetectorSpec> }
|
|
409
|
-
| { readonly not: DetectorSpec }
|
|
410
|
-
| { readonly path: typeof PathTerm.Type }
|
|
411
|
-
| { readonly imports: typeof ImportsTerm.Type }
|
|
412
|
-
| { readonly exports: typeof ExportsTerm.Type }
|
|
413
|
-
| { readonly members: typeof MembersTerm.Type }
|
|
414
|
-
// The `structure` parity strings: holds when every named sibling exists.
|
|
415
|
-
| { readonly requires: ReadonlyArray<string> }
|
|
416
|
-
| { readonly content: typeof ContentTerm.Type }
|
|
417
|
-
| { readonly syntax: typeof SyntaxTerm.Type }
|
|
418
|
-
| { readonly report: typeof ReportTerm.Type }
|
|
419
|
-
// `module#export`: a predicate function the host imports before loading.
|
|
420
|
-
| { readonly fn: string };
|
|
421
|
-
|
|
422
|
-
// Each object carries exactly one term key, so a misspelled one is a decode
|
|
423
|
-
// error that names the line rather than a term quietly dropped.
|
|
424
|
-
const DetectorRef = Schema.suspend((): Schema.Codec<DetectorSpec> => DetectorSpec);
|
|
425
|
-
|
|
426
|
-
const DetectorSpec = Schema.Union([
|
|
427
|
-
Schema.Struct({ all: Schema.Array(DetectorRef) }),
|
|
428
|
-
Schema.Struct({ any: Schema.Array(DetectorRef) }),
|
|
429
|
-
Schema.Struct({ not: DetectorRef }),
|
|
430
|
-
Schema.Struct({ path: PathTerm }),
|
|
431
|
-
Schema.Struct({ imports: ImportsTerm }),
|
|
432
|
-
Schema.Struct({ exports: ExportsTerm }),
|
|
433
|
-
Schema.Struct({ members: MembersTerm }),
|
|
434
|
-
Schema.Struct({ requires: Schema.Array(Schema.String) }),
|
|
435
|
-
Schema.Struct({ content: ContentTerm }),
|
|
436
|
-
Schema.Struct({ syntax: SyntaxTerm }),
|
|
437
|
-
Schema.Struct({ report: ReportTerm }),
|
|
438
|
-
Schema.Struct({ fn: Schema.String }),
|
|
439
|
-
]);
|
|
440
|
-
|
|
441
|
-
// A source the campaign is proven against at load. `path` alone proves a
|
|
442
|
-
// path-shaped detector; `source` is parsed, `edges` answers the `imports`
|
|
443
|
-
// term and a binding narrowing in place of the live resolver, `files`
|
|
444
|
-
// answers `requires` in place of the file system, and `report` answers a
|
|
445
|
-
// `report` term in place of running anything — one-based positions, as a
|
|
446
|
-
// tool prints them.
|
|
447
|
-
const CampaignProbe = Schema.Struct({
|
|
448
|
-
path: Schema.String,
|
|
449
|
-
source: Schema.optionalKey(Schema.String),
|
|
450
|
-
edges: Schema.optionalKey(Schema.Record(Schema.String, ImportProbeTarget)),
|
|
451
|
-
files: Schema.optionalKey(Schema.Array(Schema.String)),
|
|
452
|
-
report: Schema.optionalKey(Schema.Array(ProbeDiagnostic)),
|
|
453
|
-
});
|
|
284
|
+
// A tree of nodes, as an `endState` is once rebased: decoded like the
|
|
285
|
+
// manifest's own, every issue listed.
|
|
286
|
+
export const decodeManifestTree = (
|
|
287
|
+
raw: unknown,
|
|
288
|
+
): Result.Result<Readonly<Record<string, ManifestNode>>, string> => {
|
|
289
|
+
const decoded = decodeTree(raw);
|
|
290
|
+
return Result.isFailure(decoded)
|
|
291
|
+
? Result.fail(
|
|
292
|
+
flatten(decoded.failure.issue)
|
|
293
|
+
.issues.map((issue) => `${renderManifestPath(pathOf(issue))}: ${issue.message}`)
|
|
294
|
+
.join("\n"),
|
|
295
|
+
)
|
|
296
|
+
: Result.succeed(decoded.success);
|
|
297
|
+
};
|
|
454
298
|
|
|
455
|
-
// `30d`, `12h`: how long a campaign may go without progress before the
|
|
456
|
-
// conformance report calls it stalled.
|
|
457
|
-
const Duration = Schema.String.check(Schema.isPattern(/^\d+[dh]$/));
|
|
458
|
-
|
|
459
|
-
const Campaign = Schema.Struct({
|
|
460
|
-
// The ledger file's name, and the rule name's tail: `campaign/<id>`.
|
|
461
|
-
id: Schema.String.check(Schema.isPattern(/^[a-z0-9]+(?:-[a-z0-9]+)*$/)),
|
|
462
|
-
title: Schema.optionalKey(Schema.String),
|
|
463
|
-
why: Schema.String,
|
|
464
|
-
// What a reader at a hit does about it — the message every hit carries.
|
|
465
|
-
how: Schema.String,
|
|
466
|
-
owner: Schema.optionalKey(Schema.String),
|
|
467
|
-
// Which files the campaign selects. Alias-aware globs, as the graph rules
|
|
468
|
-
// take; a campaign's reach is its scope, so it joins no coverage row.
|
|
469
|
-
scope: Globs,
|
|
470
|
-
unit: CampaignUnit,
|
|
471
|
-
detect: DetectorSpec,
|
|
472
|
-
probes: Schema.Struct({
|
|
473
|
-
fires: Schema.Array(CampaignProbe),
|
|
474
|
-
ignores: Schema.optionalKey(Schema.Array(CampaignProbe)),
|
|
475
|
-
}),
|
|
476
|
-
staleAfter: Duration,
|
|
477
|
-
onComplete: Schema.optionalKey(Schema.Literals(["keep", "remove"])),
|
|
478
|
-
});
|
|
479
299
|
|
|
480
300
|
export const Manifest = Schema.Struct({
|
|
481
301
|
// How an import specifier becomes a file. Every pattern below is matched
|
|
@@ -491,10 +311,6 @@ export const Manifest = Schema.Struct({
|
|
|
491
311
|
exports: Schema.optionalKey(Schema.Array(ExportRestriction)),
|
|
492
312
|
graph: Schema.optionalKey(Graph),
|
|
493
313
|
limits: Schema.optionalKey(Limits),
|
|
494
|
-
campaigns: Schema.optionalKey(Schema.Array(Campaign)),
|
|
495
|
-
// Where each campaign's ledger is written: `<ledger>/<id>.json`, relative
|
|
496
|
-
// to the manifest. Defaults to `.architecture-campaigns`.
|
|
497
|
-
ledger: Schema.optionalKey(Schema.String),
|
|
498
314
|
// Shorthands expanded in every glob, so a pattern reads the way the repo's own
|
|
499
315
|
// imports do rather than repeating `packages/server/src` on every line.
|
|
500
316
|
aliases: Schema.optionalKey(Schema.Record(Schema.String, Schema.String)),
|
|
@@ -510,17 +326,8 @@ export type GraphSpec = typeof Graph.Type;
|
|
|
510
326
|
export type LimitsSpec = typeof Limits.Type;
|
|
511
327
|
export type NamingSpec = typeof Naming.Type;
|
|
512
328
|
export type ExportRestriction = typeof ExportRestriction.Type;
|
|
513
|
-
export type CampaignSpec = typeof Campaign.Type;
|
|
514
|
-
export type CampaignProbeSpec = typeof CampaignProbe.Type;
|
|
515
|
-
export type SyntaxTermSpec = typeof SyntaxTerm.Type;
|
|
516
329
|
|
|
517
|
-
export const DEFAULT_LEDGER_DIR = ".architecture-campaigns";
|
|
518
330
|
|
|
519
|
-
// `30d` → milliseconds. The schema has already refused any other shape.
|
|
520
|
-
export const durationMs = (duration: string): number => {
|
|
521
|
-
const amount = Number(duration.slice(0, -1));
|
|
522
|
-
return amount * (duration.endsWith("h") ? 3_600_000 : 86_400_000);
|
|
523
|
-
};
|
|
524
331
|
|
|
525
332
|
export const globsOf = (globs: string | ReadonlyArray<string>): ReadonlyArray<string> =>
|
|
526
333
|
typeof globs === "string" ? [globs] : globs;
|
|
@@ -534,6 +341,10 @@ const flatten = SchemaIssue.makeFormatterStandardSchemaV1();
|
|
|
534
341
|
|
|
535
342
|
export type DecodedManifest = {
|
|
536
343
|
readonly manifest: Manifest;
|
|
344
|
+
// What each family the core does not own decoded out of the keys it claims,
|
|
345
|
+
// keyed by the extension's id. The core carries these and never looks
|
|
346
|
+
// inside them.
|
|
347
|
+
readonly extensions: ReadonlyMap<string, unknown>;
|
|
537
348
|
// Things the manifest said in a form that still loads but is on its way out.
|
|
538
349
|
// The host prints them; nothing else acts on them.
|
|
539
350
|
readonly notices: ReadonlyArray<string>;
|
|
@@ -639,10 +450,15 @@ const normalizeLegacyMembers = (
|
|
|
639
450
|
return { input: { ...input, tree }, notices };
|
|
640
451
|
};
|
|
641
452
|
|
|
453
|
+
|
|
642
454
|
export type DecodeManifestOptions = {
|
|
643
455
|
// Turns a path in the file into a line and column. The YAML reader supplies
|
|
644
456
|
// one; a JavaScript module has no positions to give and passes nothing.
|
|
645
457
|
readonly locate?: ManifestLocator | undefined;
|
|
458
|
+
// Families the core does not own. Each claims top-level keys, which are
|
|
459
|
+
// split off before the core decodes what is left and handed to that
|
|
460
|
+
// family's own codec.
|
|
461
|
+
readonly extensions?: ReadonlyArray<ManifestExtension> | undefined;
|
|
646
462
|
};
|
|
647
463
|
|
|
648
464
|
const fileLabelOf = (configPath: string): string => configPath.split(/[\\/]/).at(-1) ?? configPath;
|
|
@@ -714,7 +530,35 @@ export const decodeManifest = (
|
|
|
714
530
|
}
|
|
715
531
|
const { substitutions, value } = expanded.success;
|
|
716
532
|
|
|
717
|
-
const
|
|
533
|
+
const extensions = options.extensions ?? [];
|
|
534
|
+
const describe = (path: ManifestPath, detail: string): string =>
|
|
535
|
+
describeIssue(configPath, options.locate, substitutions, path, detail);
|
|
536
|
+
|
|
537
|
+
// The keys a family the core does not own claims are split off here. What
|
|
538
|
+
// is left is decoded by the core's codec with excess properties refused, so
|
|
539
|
+
// a key nobody claims is still the misspelling it always was.
|
|
540
|
+
const claimed = new Map<string, string>();
|
|
541
|
+
for (const extension of extensions) {
|
|
542
|
+
for (const key of extension.manifestKeys) {
|
|
543
|
+
const already = claimed.get(key);
|
|
544
|
+
if (already !== undefined) {
|
|
545
|
+
return Result.fail(
|
|
546
|
+
new ConfigInvalid({
|
|
547
|
+
configPath,
|
|
548
|
+
detail:
|
|
549
|
+
`two loaded families both claim the manifest key \`${key}\` ` +
|
|
550
|
+
`("${already}" and "${extension.id}"). Load one of them.`,
|
|
551
|
+
}),
|
|
552
|
+
);
|
|
553
|
+
}
|
|
554
|
+
claimed.set(key, extension.id);
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
const core = isRecord(value)
|
|
558
|
+
? Object.fromEntries(Object.entries(value).filter(([key]) => !claimed.has(key)))
|
|
559
|
+
: value;
|
|
560
|
+
|
|
561
|
+
const resolve = normalizeLegacyResolve(core);
|
|
718
562
|
const members = normalizeLegacyMembers(resolve.input);
|
|
719
563
|
const decoded = decode(members.input);
|
|
720
564
|
if (Result.isFailure(decoded)) {
|
|
@@ -728,8 +572,32 @@ export const decodeManifest = (
|
|
|
728
572
|
}),
|
|
729
573
|
);
|
|
730
574
|
}
|
|
575
|
+
|
|
576
|
+
// Each family decodes its own slice, and renders its issues the way the
|
|
577
|
+
// core renders its own. One family's failure is reported on its own: the
|
|
578
|
+
// core's decode already succeeded, so there is nothing to collect it with.
|
|
579
|
+
const decodedExtensions = new Map<string, unknown>();
|
|
580
|
+
for (const extension of extensions) {
|
|
581
|
+
const slice = isRecord(value)
|
|
582
|
+
? Object.fromEntries(
|
|
583
|
+
extension.manifestKeys.flatMap((key) => (key in value ? [[key, value[key]]] : [])),
|
|
584
|
+
)
|
|
585
|
+
: {};
|
|
586
|
+
const result = extension.decode(slice, describe);
|
|
587
|
+
if (Result.isFailure(result)) {
|
|
588
|
+
return Result.fail(
|
|
589
|
+
new ConfigInvalid({
|
|
590
|
+
configPath,
|
|
591
|
+
detail: `the manifest does not decode:\n${result.failure.join("\n")}`,
|
|
592
|
+
}),
|
|
593
|
+
);
|
|
594
|
+
}
|
|
595
|
+
decodedExtensions.set(extension.id, result.success);
|
|
596
|
+
}
|
|
597
|
+
|
|
731
598
|
return Result.succeed({
|
|
732
599
|
manifest: decoded.success,
|
|
600
|
+
extensions: decodedExtensions,
|
|
733
601
|
notices: [...resolve.notices, ...members.notices],
|
|
734
602
|
substitutions,
|
|
735
603
|
});
|
package/src/ports/file-system.ts
CHANGED
|
@@ -5,4 +5,7 @@ export type FileSystem = {
|
|
|
5
5
|
readonly exists: (repoRelativePath: string) => boolean;
|
|
6
6
|
// The file's text, or `null` when it is absent or unreadable.
|
|
7
7
|
readonly readText: (repoRelativePath: string) => string | null;
|
|
8
|
+
// The names in a folder — the per-sector records under a campaign's
|
|
9
|
+
// ledger directory are found this way. Empty when the folder is absent.
|
|
10
|
+
readonly list: (repoRelativeDir: string) => ReadonlyArray<string>;
|
|
8
11
|
};
|
package/src/testing.ts
CHANGED
|
@@ -4,5 +4,4 @@
|
|
|
4
4
|
export { makeFactExtractorFake } from "./infrastructure/fact-extractor-fake.js";
|
|
5
5
|
export { makeFileSystemFake } from "./infrastructure/file-system-fake.js";
|
|
6
6
|
export { makeModuleResolverFake } from "./infrastructure/module-resolver-fake.js";
|
|
7
|
-
export { makeReportSourceFake } from "./infrastructure/report-source-fake.js";
|
|
8
7
|
export { makeSyntaxMatcherFake, type StagedMatch } from "./infrastructure/syntax-matcher-fake.js";
|
|
@@ -1,141 +0,0 @@
|
|
|
1
|
-
import * as Result from "effect/Result";
|
|
2
|
-
import type { BindingKind, CampaignProbe, CampaignRule, CampaignUnit, DeclarationKind, MemberSubject, ReportFormat } from "../domain/architecture-config.js";
|
|
3
|
-
import { PatternInvalid } from "../domain/architecture-error.js";
|
|
4
|
-
import type { SourceFacts } from "../domain/facts.js";
|
|
5
|
-
import type { Violation } from "../domain/violation.js";
|
|
6
|
-
import type { CampaignPredicate, Range } from "../ports/campaign-predicate.js";
|
|
7
|
-
import type { FactExtractor } from "../ports/fact-extractor.js";
|
|
8
|
-
import type { FileSystem } from "../ports/file-system.js";
|
|
9
|
-
import type { ModuleResolver } from "../ports/module-resolver.js";
|
|
10
|
-
import type { ReportSource, ReportSpec } from "../ports/report-source.js";
|
|
11
|
-
import type { SyntaxMatcher, SyntaxTree } from "../ports/syntax-matcher.js";
|
|
12
|
-
export type { CampaignPredicate, CampaignPredicateInput, CampaignSubject, Range, } from "../ports/campaign-predicate.js";
|
|
13
|
-
type Target = {
|
|
14
|
-
readonly kind: "path";
|
|
15
|
-
readonly pattern: RegExp;
|
|
16
|
-
} | {
|
|
17
|
-
readonly kind: "external";
|
|
18
|
-
readonly name: string;
|
|
19
|
-
} | {
|
|
20
|
-
readonly kind: "builtin";
|
|
21
|
-
readonly name: string;
|
|
22
|
-
};
|
|
23
|
-
type CompiledNarrowing = {
|
|
24
|
-
readonly capture: string;
|
|
25
|
-
readonly regex: RegExp | null;
|
|
26
|
-
readonly binding: {
|
|
27
|
-
readonly resolves: Target;
|
|
28
|
-
readonly member: ReadonlyArray<string> | null;
|
|
29
|
-
} | null;
|
|
30
|
-
};
|
|
31
|
-
export type CompiledDetector = {
|
|
32
|
-
readonly kind: "all";
|
|
33
|
-
readonly terms: ReadonlyArray<CompiledDetector>;
|
|
34
|
-
} | {
|
|
35
|
-
readonly kind: "any";
|
|
36
|
-
readonly terms: ReadonlyArray<CompiledDetector>;
|
|
37
|
-
} | {
|
|
38
|
-
readonly kind: "not";
|
|
39
|
-
readonly term: CompiledDetector;
|
|
40
|
-
} | {
|
|
41
|
-
readonly kind: "path";
|
|
42
|
-
readonly file: ReadonlyArray<RegExp>;
|
|
43
|
-
readonly fileNot: ReadonlyArray<RegExp>;
|
|
44
|
-
readonly subject: number | null;
|
|
45
|
-
readonly convention: RegExp | null;
|
|
46
|
-
} | {
|
|
47
|
-
readonly kind: "imports";
|
|
48
|
-
readonly resolves: Target;
|
|
49
|
-
readonly symbols: ReadonlyArray<string> | null;
|
|
50
|
-
} | {
|
|
51
|
-
readonly kind: "exports";
|
|
52
|
-
readonly name: ReadonlyArray<RegExp>;
|
|
53
|
-
readonly kinds: ReadonlyArray<BindingKind> | null;
|
|
54
|
-
readonly declares: ReadonlyArray<DeclarationKind> | null;
|
|
55
|
-
readonly reexport: boolean | null;
|
|
56
|
-
} | {
|
|
57
|
-
readonly kind: "members";
|
|
58
|
-
readonly subject: MemberSubject;
|
|
59
|
-
readonly name: ReadonlyArray<RegExp>;
|
|
60
|
-
readonly in: ReadonlyArray<RegExp>;
|
|
61
|
-
readonly declares: ReadonlyArray<DeclarationKind> | null;
|
|
62
|
-
} | {
|
|
63
|
-
readonly kind: "requires";
|
|
64
|
-
readonly templates: ReadonlyArray<string>;
|
|
65
|
-
} | {
|
|
66
|
-
readonly kind: "content";
|
|
67
|
-
readonly regex: RegExp;
|
|
68
|
-
} | {
|
|
69
|
-
readonly kind: "syntax";
|
|
70
|
-
readonly rule: unknown;
|
|
71
|
-
readonly where: ReadonlyArray<CompiledNarrowing>;
|
|
72
|
-
} | {
|
|
73
|
-
readonly kind: "report";
|
|
74
|
-
readonly command: ReadonlyArray<string> | null;
|
|
75
|
-
readonly file: ReadonlyArray<string> | null;
|
|
76
|
-
readonly format: ReportFormat;
|
|
77
|
-
readonly pattern: string | null;
|
|
78
|
-
readonly codes: ReadonlySet<string> | null;
|
|
79
|
-
readonly codesNot: ReadonlySet<string>;
|
|
80
|
-
} | {
|
|
81
|
-
readonly kind: "fn";
|
|
82
|
-
readonly name: string;
|
|
83
|
-
};
|
|
84
|
-
export type CompiledCampaign = {
|
|
85
|
-
readonly name: string;
|
|
86
|
-
readonly id: string;
|
|
87
|
-
readonly title: string | null;
|
|
88
|
-
readonly message: string;
|
|
89
|
-
readonly why: string;
|
|
90
|
-
readonly owner: string | null;
|
|
91
|
-
readonly scope: ReadonlyArray<RegExp>;
|
|
92
|
-
readonly unit: CampaignUnit;
|
|
93
|
-
readonly detect: CompiledDetector;
|
|
94
|
-
readonly probes: {
|
|
95
|
-
readonly fires: ReadonlyArray<CampaignProbe>;
|
|
96
|
-
readonly ignores: ReadonlyArray<CampaignProbe>;
|
|
97
|
-
};
|
|
98
|
-
readonly staleAfter: number;
|
|
99
|
-
readonly onComplete: "keep" | "remove";
|
|
100
|
-
};
|
|
101
|
-
export declare const compileCampaignRule: (rule: CampaignRule) => Result.Result<CompiledCampaign, PatternInvalid>;
|
|
102
|
-
export declare const compileCampaignRules: (rules: ReadonlyArray<CampaignRule>) => Result.Result<ReadonlyArray<CompiledCampaign>, PatternInvalid>;
|
|
103
|
-
export declare const campaignsSelecting: (rules: ReadonlyArray<CompiledCampaign>, file: string) => ReadonlyArray<CompiledCampaign>;
|
|
104
|
-
declare const LEAF_ORDER: readonly ["path", "imports", "exports", "members", "requires", "content", "syntax", "report", "fn"];
|
|
105
|
-
type LeafKind = (typeof LEAF_ORDER)[number];
|
|
106
|
-
export declare const leafTermsOf: (detector: CompiledDetector) => ReadonlyArray<LeafKind>;
|
|
107
|
-
export type CampaignInput = {
|
|
108
|
-
readonly file: string;
|
|
109
|
-
readonly text: string;
|
|
110
|
-
readonly facts: SourceFacts;
|
|
111
|
-
readonly resolver: ModuleResolver;
|
|
112
|
-
readonly fileSystem: FileSystem;
|
|
113
|
-
readonly syntax: SyntaxTree | null;
|
|
114
|
-
readonly functions: ReadonlyMap<string, CampaignPredicate>;
|
|
115
|
-
readonly reports: ReportSource;
|
|
116
|
-
};
|
|
117
|
-
export type CampaignHit = {
|
|
118
|
-
readonly violation: Violation;
|
|
119
|
-
readonly campaign: string;
|
|
120
|
-
readonly range?: Range;
|
|
121
|
-
};
|
|
122
|
-
export declare const matchKeyOf: (anchor: string | null, text: string) => string;
|
|
123
|
-
export declare const evaluateCampaign: (rule: CompiledCampaign, input: CampaignInput) => ReadonlyArray<CampaignHit>;
|
|
124
|
-
export declare const reportSpecsOf: (rules: ReadonlyArray<CompiledCampaign>) => ReadonlyArray<ReportSpec>;
|
|
125
|
-
export declare const evaluateCampaigns: (selected: ReadonlyArray<CompiledCampaign>, input: CampaignInput) => ReadonlyArray<CampaignHit>;
|
|
126
|
-
export type TermAnswer = {
|
|
127
|
-
readonly term: string;
|
|
128
|
-
readonly answer: boolean;
|
|
129
|
-
readonly count?: number;
|
|
130
|
-
};
|
|
131
|
-
export declare const explainCampaign: (rule: CompiledCampaign, input: CampaignInput) => ReadonlyArray<TermAnswer>;
|
|
132
|
-
export declare const probeInputOf: (probe: CampaignProbe, extractor: FactExtractor, matcher: SyntaxMatcher | null, functions: ReadonlyMap<string, CampaignPredicate>) => CampaignInput;
|
|
133
|
-
export type FailedProbe = {
|
|
134
|
-
readonly name: string;
|
|
135
|
-
readonly probe: CampaignProbe;
|
|
136
|
-
readonly expected: "fires" | "ignores";
|
|
137
|
-
readonly admittedBy?: string;
|
|
138
|
-
readonly outOfScope?: boolean;
|
|
139
|
-
};
|
|
140
|
-
export declare const campaignsFailingTheirProbe: (rules: ReadonlyArray<CompiledCampaign>, extractor: FactExtractor, matcherFor: (file: string) => SyntaxMatcher | null, functions: ReadonlyMap<string, CampaignPredicate>) => ReadonlyArray<FailedProbe>;
|
|
141
|
-
//# sourceMappingURL=campaigns.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"campaigns.d.ts","sourceRoot":"","sources":["../../../src/core/campaigns.ts"],"names":[],"mappings":"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"}
|