@invariant-ui/cli 0.0.0-stage → 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 +105 -0
- package/README.md +250 -2
- package/dist/agent-kit/apply.js +232 -0
- package/dist/agent-kit/guidance.js +236 -0
- package/dist/agent-kit/index.js +5 -0
- package/dist/agent-kit/integrations.js +121 -0
- package/dist/agent-kit/plugin.js +115 -0
- package/dist/artifact-generator/index.js +75 -0
- package/dist/browser-verify/driver.js +2 -0
- package/dist/browser-verify/index.js +4 -0
- package/dist/browser-verify/playwright.js +82 -0
- package/dist/browser-verify/runner.js +175 -0
- package/dist/browser-verify/spec.js +103 -0
- package/dist/cli/args.js +77 -0
- package/dist/cli/baseline.js +64 -0
- package/dist/cli/cli.js +121 -0
- package/dist/cli/commands-browser.js +104 -0
- package/dist/cli/commands-discover.js +312 -0
- package/dist/cli/commands-enforcement.js +35 -0
- package/dist/cli/commands-governance.js +179 -0
- package/dist/cli/commands-hook.js +269 -0
- package/dist/cli/commands-patterns.js +324 -0
- package/dist/cli/commands-setup.js +485 -0
- package/dist/cli/commands-validate.js +157 -0
- package/dist/cli/commands.js +517 -0
- package/dist/cli/config.js +197 -0
- package/dist/cli/diff.js +66 -0
- package/dist/cli/enforcement-log.js +83 -0
- package/dist/cli/enforcement-report.js +96 -0
- package/dist/cli/errors.js +10 -0
- package/dist/cli/format.js +22 -0
- package/dist/cli/index.js +5 -0
- package/dist/cli/io.js +2 -0
- package/dist/cli/main.js +31 -0
- package/dist/cli/pipeline.js +262 -0
- package/dist/cli/publish.js +77 -0
- package/dist/cli/report.js +127 -0
- package/dist/cli/starter-rules.js +57 -0
- package/dist/cli/store.js +60 -0
- package/dist/cli/version.js +5 -0
- package/dist/cli/watch.js +68 -0
- package/dist/cli/workflow.js +52 -0
- package/dist/compiler-angular/adapter.js +115 -0
- package/dist/compiler-angular/analyze.js +222 -0
- package/dist/compiler-angular/classes.js +446 -0
- package/dist/compiler-angular/declared.js +285 -0
- package/dist/compiler-angular/emit.js +183 -0
- package/dist/compiler-angular/index.js +4 -0
- package/dist/compiler-angular/template.js +142 -0
- package/dist/compiler-core/adapter.js +2 -0
- package/dist/compiler-core/adapters/inventory.js +312 -0
- package/dist/compiler-core/compile.js +148 -0
- package/dist/compiler-core/component-facts.js +123 -0
- package/dist/compiler-core/discovery/docs.js +66 -0
- package/dist/compiler-core/discovery/entries.js +270 -0
- package/dist/compiler-core/discovery/exports.js +207 -0
- package/dist/compiler-core/discovery/graph.js +59 -0
- package/dist/compiler-core/discovery/index.js +103 -0
- package/dist/compiler-core/discovery/packages.js +148 -0
- package/dist/compiler-core/discovery/schema.js +144 -0
- package/dist/compiler-core/discovery/storybook.js +232 -0
- package/dist/compiler-core/discovery/support.js +82 -0
- package/dist/compiler-core/discovery/tokens.js +186 -0
- package/dist/compiler-core/discovery/tooling.js +146 -0
- package/dist/compiler-core/fs.js +89 -0
- package/dist/compiler-core/glob.js +3 -0
- package/dist/compiler-core/index.js +16 -0
- package/dist/compiler-core/ir-builder.js +68 -0
- package/dist/compiler-core/json-schema.js +11 -0
- package/dist/compiler-core/testing/index.js +81 -0
- package/dist/compiler-core/ts/ast.js +166 -0
- package/dist/compiler-core/ts/members.js +163 -0
- package/dist/compiler-core/ts/program.js +81 -0
- package/dist/compiler-core/ts/static-eval.js +145 -0
- package/dist/compiler-core/ts/tsconfig-paths.js +60 -0
- package/dist/compiler-core/walk.js +46 -0
- package/dist/compiler-ir/index.js +4 -0
- package/dist/compiler-ir/ir.js +67 -0
- package/dist/compiler-ir/json-schema.js +6 -0
- package/dist/compiler-ir/validate.js +116 -0
- package/dist/compiler-react/adapter.js +147 -0
- package/dist/compiler-react/analyze.js +199 -0
- package/dist/compiler-react/behavior.js +75 -0
- package/dist/compiler-react/detect.js +558 -0
- package/dist/compiler-react/emit.js +188 -0
- package/dist/compiler-react/index.js +2 -0
- package/dist/compiler-react/props.js +157 -0
- package/dist/compiler-react/styling.js +138 -0
- package/dist/compiler-react/usage.js +116 -0
- package/dist/compiler-react/variants.js +111 -0
- package/dist/compiler-storybook/adapter.js +253 -0
- package/dist/compiler-storybook/csf.js +521 -0
- package/dist/compiler-storybook/emit.js +202 -0
- package/dist/compiler-storybook/index-json.js +41 -0
- package/dist/compiler-storybook/index.js +5 -0
- package/dist/compiler-storybook/mdx.js +120 -0
- package/dist/compiler-tokens/adapter.js +158 -0
- package/dist/compiler-tokens/css.js +59 -0
- package/dist/compiler-tokens/index.js +5 -0
- package/dist/compiler-tokens/json-lines.js +106 -0
- package/dist/compiler-tokens/json-tokens.js +52 -0
- package/dist/compiler-vue/adapter.js +107 -0
- package/dist/compiler-vue/analyze.js +344 -0
- package/dist/compiler-vue/declared.js +343 -0
- package/dist/compiler-vue/emit.js +129 -0
- package/dist/compiler-vue/index.js +5 -0
- package/dist/compiler-vue/script.js +472 -0
- package/dist/compiler-vue/sfc.js +44 -0
- package/dist/compiler-vue/template.js +73 -0
- package/dist/contract/archetype-checks.js +153 -0
- package/dist/contract/components.js +102 -0
- package/dist/contract/conflicts.js +39 -0
- package/dist/contract/contract.js +46 -0
- package/dist/contract/evidence.js +84 -0
- package/dist/contract/fact-path.js +39 -0
- package/dist/contract/facts.js +62 -0
- package/dist/contract/index.js +16 -0
- package/dist/contract/integrity.js +21 -0
- package/dist/contract/json-schema.js +11 -0
- package/dist/contract/knowledge.js +95 -0
- package/dist/contract/pattern-checks.js +254 -0
- package/dist/contract/patterns.js +225 -0
- package/dist/contract/primitives.js +81 -0
- package/dist/contract/sources.js +30 -0
- package/dist/contract/tokens.js +41 -0
- package/dist/contract/validate.js +253 -0
- package/dist/contract-query/archetype.js +188 -0
- package/dist/contract-query/index.js +6 -0
- package/dist/contract-query/patterns.js +298 -0
- package/dist/contract-query/search.js +132 -0
- package/dist/contract-query/system.js +148 -0
- package/dist/contract-query/views.js +288 -0
- package/dist/governance/decisions.js +229 -0
- package/dist/governance/index.js +5 -0
- package/dist/governance/patterns-file.js +156 -0
- package/dist/governance/patterns.js +510 -0
- package/dist/governance/policies.js +148 -0
- package/dist/mcp/index.js +7 -0
- package/dist/mcp/loader.js +70 -0
- package/dist/mcp/protocol.js +27 -0
- package/dist/mcp/resources.js +138 -0
- package/dist/mcp/server.js +177 -0
- package/dist/mcp/stdio.js +28 -0
- package/dist/mcp/tools.js +267 -0
- package/dist/reconciliation/code.js +62 -0
- package/dist/reconciliation/components.js +401 -0
- package/dist/reconciliation/conflicts.js +126 -0
- package/dist/reconciliation/facts.js +41 -0
- package/dist/reconciliation/index.js +7 -0
- package/dist/reconciliation/reconcile.js +134 -0
- package/dist/reconciliation/resolve.js +158 -0
- package/dist/reconciliation/review.js +81 -0
- package/dist/reconciliation/storybook.js +370 -0
- package/dist/reconciliation/tokens.js +179 -0
- package/dist/reconciliation/util.js +55 -0
- package/dist/semantic-engine/additive.js +51 -0
- package/dist/semantic-engine/compounds.js +91 -0
- package/dist/semantic-engine/documents.js +157 -0
- package/dist/semantic-engine/enrich.js +42 -0
- package/dist/semantic-engine/index.js +7 -0
- package/dist/semantic-engine/infer.js +38 -0
- package/dist/semantic-engine/tokens.js +120 -0
- package/dist/shared/canonical.js +65 -0
- package/dist/shared/diagnostic.js +78 -0
- package/dist/shared/glob.js +98 -0
- package/dist/shared/index.js +4 -0
- package/dist/shared/json.js +2 -0
- package/dist/validation/card.js +50 -0
- package/dist/validation/discovery/arrangements.js +162 -0
- package/dist/validation/discovery/discover.js +70 -0
- package/dist/validation/discovery/index.js +8 -0
- package/dist/validation/discovery/model.js +127 -0
- package/dist/validation/discovery/promote.js +24 -0
- package/dist/validation/discovery/reviews.js +63 -0
- package/dist/validation/discovery/shapes.js +47 -0
- package/dist/validation/discovery/similar-files.js +146 -0
- package/dist/validation/discovery/util.js +47 -0
- package/dist/validation/index.js +11 -0
- package/dist/validation/interaction-facts.js +341 -0
- package/dist/validation/interaction-workflows.js +633 -0
- package/dist/validation/literals.js +251 -0
- package/dist/validation/pattern-rules.js +608 -0
- package/dist/validation/policy.js +241 -0
- package/dist/validation/report.js +163 -0
- package/dist/validation/rules.js +269 -0
- package/dist/validation/scan.js +295 -0
- package/dist/validation/text.js +34 -0
- package/dist/validation/validate-code.js +513 -0
- package/dist/validation/validate-usage.js +196 -0
- package/package.json +38 -4
|
@@ -0,0 +1,510 @@
|
|
|
1
|
+
import { computeEvidenceId, computeFactFingerprint, constraintDetailsSchemas, patternSchema, resolveFactPath } from "../contract/index.js";
|
|
2
|
+
import { DiagnosticCollector, sha256Hex } from "../shared/index.js";
|
|
3
|
+
import { nearest } from "../validation/index.js";
|
|
4
|
+
export const PATTERN_SOURCE_ID = "patterns";
|
|
5
|
+
const EXTRACTOR = { name: "pattern-definitions", version: "1.0.0" };
|
|
6
|
+
const cmp = (a, b) => (a < b ? -1 : a > b ? 1 : 0);
|
|
7
|
+
const byId = (a, b) => cmp(a.id, b.id);
|
|
8
|
+
const norm = (s) => s
|
|
9
|
+
.replace(/^[a-z]+:/, "")
|
|
10
|
+
.toLowerCase()
|
|
11
|
+
.replace(/[^a-z0-9]/g, "");
|
|
12
|
+
const bodyOf = (id) => id.slice(id.indexOf(":") + 1);
|
|
13
|
+
const afterCategory = (id) => {
|
|
14
|
+
const body = bodyOf(id);
|
|
15
|
+
return body.includes(".") ? body.slice(body.indexOf(".") + 1) : body;
|
|
16
|
+
};
|
|
17
|
+
/**
|
|
18
|
+
* Finds the entity a written reference means, or explains why it cannot. An exact id wins; then a name or id
|
|
19
|
+
* (ignoring case and separators); then the part of an id after its category (`data-management`). A looser
|
|
20
|
+
* match never competes with a stricter one, and several matches at the same level are ambiguous, not guessed.
|
|
21
|
+
*/
|
|
22
|
+
function lookup(kind, ref, universe, code) {
|
|
23
|
+
const text = ref.trim();
|
|
24
|
+
const exact = universe.find((e) => e.id === text);
|
|
25
|
+
if (exact)
|
|
26
|
+
return { ok: true, id: exact.id, tier: 0 };
|
|
27
|
+
// An explicit prefix names exactly one id: no fuzzy matching.
|
|
28
|
+
if (text.startsWith(`${kind}:`))
|
|
29
|
+
return unknown(kind, text, universe, code);
|
|
30
|
+
const n = norm(text);
|
|
31
|
+
const tiers = [
|
|
32
|
+
universe.filter((e) => norm(e.name) === n || norm(bodyOf(e.id)) === n),
|
|
33
|
+
universe.filter((e) => norm(afterCategory(e.id)) === n)
|
|
34
|
+
];
|
|
35
|
+
for (const [i, matches] of tiers.entries()) {
|
|
36
|
+
if (matches.length === 1)
|
|
37
|
+
return { ok: true, id: matches[0].id, tier: i + 1 };
|
|
38
|
+
if (matches.length > 1)
|
|
39
|
+
return {
|
|
40
|
+
ok: false,
|
|
41
|
+
code: "governance.pattern-ambiguous-reference",
|
|
42
|
+
message: `"${text}" matches several ${kind}s (${matches.map((m) => m.id).join(", ")}); write the full id`
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
return unknown(kind, text, universe, code);
|
|
46
|
+
}
|
|
47
|
+
function unknown(kind, text, universe, code) {
|
|
48
|
+
const close = nearest(text.replace(/^[a-z]+:/, ""), universe.flatMap((e) => [e.name, e.id.slice(e.id.indexOf(":") + 1)]));
|
|
49
|
+
return {
|
|
50
|
+
ok: false,
|
|
51
|
+
code,
|
|
52
|
+
message: `there is no ${kind} "${text}"${close.length > 0 ? `; did you mean ${close.join(" or ")}?` : ""}`
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
/** Resolves written references to ids. Problems are collected, never thrown, so one run reports them all. */
|
|
56
|
+
class References {
|
|
57
|
+
problems = [];
|
|
58
|
+
universe;
|
|
59
|
+
owner;
|
|
60
|
+
constructor(universe, owner) {
|
|
61
|
+
this.universe = universe;
|
|
62
|
+
this.owner = owner;
|
|
63
|
+
}
|
|
64
|
+
fail(where, r) {
|
|
65
|
+
this.problems.push({ code: r.code, message: `${this.owner} ${where}: ${r.message}` });
|
|
66
|
+
return "";
|
|
67
|
+
}
|
|
68
|
+
component(ref, where) {
|
|
69
|
+
const r = lookup("component", ref, this.universe.components, "governance.pattern-unknown-component");
|
|
70
|
+
return r.ok ? r.id : this.fail(where, r);
|
|
71
|
+
}
|
|
72
|
+
pattern(ref, where) {
|
|
73
|
+
const r = lookup("pattern", ref, this.universe.patterns, "governance.pattern-unknown-pattern");
|
|
74
|
+
return r.ok ? r.id : this.fail(where, r);
|
|
75
|
+
}
|
|
76
|
+
token(ref, where) {
|
|
77
|
+
const r = lookup("token", ref, this.universe.tokens, "governance.pattern-unknown-token");
|
|
78
|
+
return r.ok ? r.id : this.fail(where, r);
|
|
79
|
+
}
|
|
80
|
+
guideline(ref, where) {
|
|
81
|
+
const r = lookup("guideline", ref, this.universe.guidelines, "governance.pattern-unknown-guideline");
|
|
82
|
+
return r.ok ? r.id : this.fail(where, r);
|
|
83
|
+
}
|
|
84
|
+
/** A component or a pattern. An explicit prefix decides; otherwise exactly one must match. */
|
|
85
|
+
target(ref, where) {
|
|
86
|
+
const text = ref.trim();
|
|
87
|
+
if (text.startsWith("component:"))
|
|
88
|
+
return this.component(text, where);
|
|
89
|
+
if (text.startsWith("pattern:"))
|
|
90
|
+
return this.pattern(text, where);
|
|
91
|
+
const c = lookup("component", text, this.universe.components, "");
|
|
92
|
+
const p = lookup("pattern", text, this.universe.patterns, "");
|
|
93
|
+
if (c.ok && p.ok) {
|
|
94
|
+
// A stricter match beats a looser one; only a tie is ambiguous.
|
|
95
|
+
if (c.tier !== p.tier)
|
|
96
|
+
return c.tier < p.tier ? c.id : p.id;
|
|
97
|
+
return this.fail(where, {
|
|
98
|
+
ok: false,
|
|
99
|
+
code: "governance.pattern-ambiguous-reference",
|
|
100
|
+
message: `"${text}" is both a component (${c.id}) and a pattern (${p.id}); write the full id`
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
if (c.ok)
|
|
104
|
+
return c.id;
|
|
105
|
+
if (p.ok)
|
|
106
|
+
return p.id;
|
|
107
|
+
const close = nearest(text, [...this.universe.components, ...this.universe.patterns].map((e) => e.name));
|
|
108
|
+
return this.fail(where, {
|
|
109
|
+
ok: false,
|
|
110
|
+
code: "governance.pattern-unknown-component",
|
|
111
|
+
message: `there is no component or pattern "${text}"${close.length > 0 ? `; did you mean ${close.join(" or ")}?` : ""}`
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
/** A component, a pattern, or a `region:<name>` of the pattern itself. */
|
|
115
|
+
element(ref, where) {
|
|
116
|
+
return ref.startsWith("region:") ? ref : this.target(ref, where);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
function mapStrings(value, map, key = "") {
|
|
120
|
+
if (typeof value === "string")
|
|
121
|
+
return map(value, key);
|
|
122
|
+
if (Array.isArray(value))
|
|
123
|
+
return value.map((v) => mapStrings(v, map, key));
|
|
124
|
+
if (value && typeof value === "object")
|
|
125
|
+
return Object.fromEntries(Object.entries(value).map(([k, v]) => [k, mapStrings(v, map, k)]));
|
|
126
|
+
return value;
|
|
127
|
+
}
|
|
128
|
+
const stringsIn = (value) => typeof value === "string"
|
|
129
|
+
? [value]
|
|
130
|
+
: Array.isArray(value)
|
|
131
|
+
? value.flatMap(stringsIn)
|
|
132
|
+
: value && typeof value === "object"
|
|
133
|
+
? Object.values(value).flatMap(stringsIn)
|
|
134
|
+
: [];
|
|
135
|
+
const localName = (id) => id.slice(id.indexOf(":") + 1);
|
|
136
|
+
/** The pattern with every reference resolved, before facts and constraints are attached. */
|
|
137
|
+
function buildPattern(ctx) {
|
|
138
|
+
const { def, refs } = ctx;
|
|
139
|
+
const parts = def.composition?.parts.map((p) => ({
|
|
140
|
+
...p,
|
|
141
|
+
ref: refs.target(p.ref, `composition.parts.${p.name}.ref`)
|
|
142
|
+
}));
|
|
143
|
+
const regions = def.layout?.regions.map((r) => ({
|
|
144
|
+
...r,
|
|
145
|
+
...(r.accepts
|
|
146
|
+
? { accepts: r.accepts.map((a) => refs.target(a, `layout.regions.${r.name}.accepts`)) }
|
|
147
|
+
: {}),
|
|
148
|
+
...(r.spacing ? { spacing: refs.token(r.spacing, `layout.regions.${r.name}.spacing`) } : {}),
|
|
149
|
+
...(r.grid
|
|
150
|
+
? {
|
|
151
|
+
grid: {
|
|
152
|
+
...r.grid,
|
|
153
|
+
...(r.grid.gap ? { gap: refs.token(r.grid.gap, `layout.regions.${r.name}.grid.gap`) } : {})
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
: {}),
|
|
157
|
+
...(r.responsive
|
|
158
|
+
? {
|
|
159
|
+
responsive: r.responsive.map((x) => ({
|
|
160
|
+
...x,
|
|
161
|
+
breakpoint: refs.token(x.breakpoint, `layout.regions.${r.name}.responsive`)
|
|
162
|
+
}))
|
|
163
|
+
}
|
|
164
|
+
: {})
|
|
165
|
+
}));
|
|
166
|
+
const variations = def.variations?.map((v) => ({
|
|
167
|
+
...v,
|
|
168
|
+
...(v.parts
|
|
169
|
+
? {
|
|
170
|
+
parts: v.parts.map((p) => ({
|
|
171
|
+
...p,
|
|
172
|
+
ref: refs.target(p.ref, `variations.${v.name}.parts.${p.name}.ref`)
|
|
173
|
+
}))
|
|
174
|
+
}
|
|
175
|
+
: {})
|
|
176
|
+
}));
|
|
177
|
+
const relationships = def.relationships?.map((r) => ({
|
|
178
|
+
...r,
|
|
179
|
+
target: r.type === "contains" || r.type === "uses"
|
|
180
|
+
? refs.target(r.target, `relationships.${r.id}.target`)
|
|
181
|
+
: refs.pattern(r.target, `relationships.${r.id}.target`)
|
|
182
|
+
}));
|
|
183
|
+
const guidelines = (def.guidelines ?? []).map((g, i) => refs.guideline(g, `guidelines.${i}`));
|
|
184
|
+
const pattern = {
|
|
185
|
+
id: ctx.id,
|
|
186
|
+
name: def.name,
|
|
187
|
+
category: def.category,
|
|
188
|
+
description: def.description,
|
|
189
|
+
...(def.intent ? { intent: def.intent } : {}),
|
|
190
|
+
components: [],
|
|
191
|
+
guidelines: guidelines.sort(),
|
|
192
|
+
...(parts ? { composition: { parts } } : {}),
|
|
193
|
+
...(variations ? { variations } : {}),
|
|
194
|
+
...(regions ? { layout: { regions } } : {}),
|
|
195
|
+
...(def.behavior ? { behavior: def.behavior } : {}),
|
|
196
|
+
...(def.accessibility ? { accessibility: def.accessibility } : {}),
|
|
197
|
+
...(relationships ? { relationships } : {}),
|
|
198
|
+
...(def.appliesTo ? { appliesTo: def.appliesTo } : {}),
|
|
199
|
+
...(def.origin ? { extensions: { discovery: def.origin } } : {}),
|
|
200
|
+
...(def.inheritsConstraints === undefined ? {} : { inheritsConstraints: def.inheritsConstraints })
|
|
201
|
+
};
|
|
202
|
+
return pattern;
|
|
203
|
+
}
|
|
204
|
+
function buildConstraints(ctx) {
|
|
205
|
+
const { def, refs } = ctx;
|
|
206
|
+
const body = localName(ctx.id);
|
|
207
|
+
return (def.constraints ?? []).map((c) => {
|
|
208
|
+
const where = `constraints.${c.id}.details`;
|
|
209
|
+
// A state requirement names a workflow and a state, not components, so there is nothing to resolve.
|
|
210
|
+
const details = (c.kind === "required-state" ? c.details : mapStrings(c.details, (s) => refs.element(s, where)));
|
|
211
|
+
return {
|
|
212
|
+
id: `constraint:${body}.${c.id}`,
|
|
213
|
+
name: c.name ?? `${def.name}: ${c.id}`,
|
|
214
|
+
kind: c.kind,
|
|
215
|
+
description: c.description,
|
|
216
|
+
subjects: [ctx.id],
|
|
217
|
+
details,
|
|
218
|
+
facts: {}
|
|
219
|
+
};
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
function validateDetails(ctx, constraints, regions) {
|
|
223
|
+
for (const c of constraints) {
|
|
224
|
+
const local = c.id.slice(c.id.lastIndexOf(".") + 1);
|
|
225
|
+
const parsed = constraintDetailsSchemas[c.kind].safeParse(c.details);
|
|
226
|
+
if (!parsed.success) {
|
|
227
|
+
const issue = parsed.error.issues[0];
|
|
228
|
+
ctx.refs.problems.push({
|
|
229
|
+
code: "governance.pattern-constraint-invalid",
|
|
230
|
+
message: `${ctx.id} constraints.${local}: details do not fit a ${c.kind} constraint at ${issue?.path.join(".") || "(root)"}: ${issue?.message ?? "schema error"}`
|
|
231
|
+
});
|
|
232
|
+
continue;
|
|
233
|
+
}
|
|
234
|
+
for (const s of stringsIn(parsed.data))
|
|
235
|
+
if (s.startsWith("region:") && !regions.has(s))
|
|
236
|
+
ctx.refs.problems.push({
|
|
237
|
+
code: "governance.pattern-unknown-region",
|
|
238
|
+
message: `${ctx.id} constraints.${local}: ${s} is not a region of this pattern${regions.size === 0 ? " (it defines no layout)" : `; its regions are ${[...regions].join(", ")}`}`
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
/** One fact per authored section, with the pattern's approval layered on when it has one. */
|
|
243
|
+
function factFor(ctx, entity, path) {
|
|
244
|
+
const value = resolveFactPath(entity, path);
|
|
245
|
+
if (!value.found)
|
|
246
|
+
return undefined;
|
|
247
|
+
const base = { evidenceIds: [ctx.evidence.id] };
|
|
248
|
+
const approval = ctx.def.approval;
|
|
249
|
+
if (!approval)
|
|
250
|
+
return { status: "documented", ...base };
|
|
251
|
+
return {
|
|
252
|
+
status: "approved",
|
|
253
|
+
...base,
|
|
254
|
+
approval: {
|
|
255
|
+
approvedBy: approval.approvedBy,
|
|
256
|
+
approvedAt: approval.approvedAt,
|
|
257
|
+
...(approval.role ? { role: approval.role } : {}),
|
|
258
|
+
...(approval.reference ? { reference: approval.reference } : {}),
|
|
259
|
+
priorStatus: "documented",
|
|
260
|
+
fingerprint: computeFactFingerprint({ entityId: entity.id, path, value: value.value })
|
|
261
|
+
}
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
function withFacts(ctx, entity, paths) {
|
|
265
|
+
const facts = {};
|
|
266
|
+
for (const path of paths) {
|
|
267
|
+
const f = factFor(ctx, entity, path);
|
|
268
|
+
if (f)
|
|
269
|
+
facts[path] = f;
|
|
270
|
+
}
|
|
271
|
+
return { ...entity, facts };
|
|
272
|
+
}
|
|
273
|
+
function build(ctx) {
|
|
274
|
+
const constraints = buildConstraints(ctx);
|
|
275
|
+
const draft = buildPattern(ctx);
|
|
276
|
+
const regionRefs = new Set((draft.layout?.regions ?? []).map((r) => `region:${r.name}`));
|
|
277
|
+
validateDetails(ctx, constraints, regionRefs);
|
|
278
|
+
const components = new Set();
|
|
279
|
+
for (const s of [
|
|
280
|
+
...stringsIn(draft.composition),
|
|
281
|
+
...stringsIn(draft.layout),
|
|
282
|
+
...stringsIn(draft.variations),
|
|
283
|
+
...stringsIn(draft.relationships),
|
|
284
|
+
...constraints.flatMap((c) => stringsIn(c.details))
|
|
285
|
+
])
|
|
286
|
+
if (s.startsWith("component:"))
|
|
287
|
+
components.add(s);
|
|
288
|
+
const entity = {
|
|
289
|
+
...draft,
|
|
290
|
+
components: [...components].sort(),
|
|
291
|
+
constraints: constraints.map((c) => c.id).sort(),
|
|
292
|
+
...(constraints.length === 0 ? { constraints: undefined } : {})
|
|
293
|
+
};
|
|
294
|
+
if (entity.constraints === undefined)
|
|
295
|
+
delete entity.constraints;
|
|
296
|
+
const paths = [
|
|
297
|
+
"description",
|
|
298
|
+
...(entity.intent ? ["intent"] : []),
|
|
299
|
+
...(entity.composition ? ["composition"] : []),
|
|
300
|
+
...(entity.layout ? ["layout"] : []),
|
|
301
|
+
...(entity.behavior ? ["behavior"] : []),
|
|
302
|
+
...(entity.accessibility ? ["accessibility"] : []),
|
|
303
|
+
...(entity.appliesTo ? ["appliesTo"] : []),
|
|
304
|
+
...(entity.inheritsConstraints === undefined ? [] : ["inheritsConstraints"]),
|
|
305
|
+
...(entity.variations ?? []).map((v) => `variations.${v.name}`),
|
|
306
|
+
...(entity.relationships ?? []).map((r) => `relationships.${r.id}`)
|
|
307
|
+
];
|
|
308
|
+
const pattern = withFacts(ctx, entity, paths);
|
|
309
|
+
const withConstraintFacts = constraints.map((c) => withFacts(ctx, c, ["description", "details"]));
|
|
310
|
+
const policies = [];
|
|
311
|
+
const body = localName(ctx.id);
|
|
312
|
+
for (const [i, c] of (ctx.def.constraints ?? []).entries()) {
|
|
313
|
+
if (!c.enforce)
|
|
314
|
+
continue;
|
|
315
|
+
const approval = ctx.def.approval;
|
|
316
|
+
if (!approval)
|
|
317
|
+
continue;
|
|
318
|
+
policies.push({
|
|
319
|
+
id: `policy:${body}.${c.id}`,
|
|
320
|
+
name: withConstraintFacts[i].name,
|
|
321
|
+
statement: c.description,
|
|
322
|
+
severity: c.enforce.severity,
|
|
323
|
+
enforcement: c.enforce.enforcement ?? "block",
|
|
324
|
+
scope: {
|
|
325
|
+
entities: [ctx.id],
|
|
326
|
+
...(ctx.def.appliesTo?.frameworks ? { frameworks: ctx.def.appliesTo.frameworks } : {}),
|
|
327
|
+
...(ctx.def.appliesTo?.paths ? { paths: ctx.def.appliesTo.paths } : {})
|
|
328
|
+
},
|
|
329
|
+
basedOn: [`constraint:${body}.${c.id}`],
|
|
330
|
+
approval: {
|
|
331
|
+
approvedBy: approval.approvedBy,
|
|
332
|
+
approvedAt: approval.approvedAt,
|
|
333
|
+
...(approval.role ? { role: approval.role } : {}),
|
|
334
|
+
...(approval.reference ? { reference: approval.reference } : {})
|
|
335
|
+
}
|
|
336
|
+
});
|
|
337
|
+
}
|
|
338
|
+
return { pattern, constraints: withConstraintFacts, policies };
|
|
339
|
+
}
|
|
340
|
+
const SOURCE_CAPABILITIES = [
|
|
341
|
+
"description",
|
|
342
|
+
"intent",
|
|
343
|
+
"composition",
|
|
344
|
+
"layout",
|
|
345
|
+
"behavior",
|
|
346
|
+
"accessibility",
|
|
347
|
+
"appliesTo",
|
|
348
|
+
"inheritsConstraints",
|
|
349
|
+
"variations.*",
|
|
350
|
+
"relationships.*"
|
|
351
|
+
];
|
|
352
|
+
/**
|
|
353
|
+
* Adds human-written pattern definitions to the contract: each becomes a pattern, its constraints, and (when
|
|
354
|
+
* the pattern is approved and a constraint asks to be enforced) a policy. Facts are `documented`, or `approved`
|
|
355
|
+
* when the definition carries an approval; nothing here infers or grants approval. A definition with an
|
|
356
|
+
* unresolvable reference is left out whole and reported, so the contract never holds a half-resolved pattern.
|
|
357
|
+
* Nothing already in the contract is changed.
|
|
358
|
+
*/
|
|
359
|
+
export function compilePatterns(draft, file, options) {
|
|
360
|
+
const collector = new DiagnosticCollector();
|
|
361
|
+
const summary = { added: 0, skipped: 0, constraints: 0, policies: 0, awaitingApproval: 0 };
|
|
362
|
+
const at = { location: { path: options.file } };
|
|
363
|
+
const idOf = (def) => (def.id.startsWith("pattern:") ? def.id : `pattern:${def.id}`);
|
|
364
|
+
const taken = new Set([
|
|
365
|
+
...draft.patterns.map((p) => p.id),
|
|
366
|
+
...draft.constraints.map((c) => c.id),
|
|
367
|
+
...draft.policies.map((p) => p.id)
|
|
368
|
+
]);
|
|
369
|
+
const seen = new Set();
|
|
370
|
+
const eligible = [];
|
|
371
|
+
for (const def of file.patterns) {
|
|
372
|
+
const id = idOf(def);
|
|
373
|
+
if (seen.has(id)) {
|
|
374
|
+
collector.add({
|
|
375
|
+
code: "governance.pattern-duplicate-id",
|
|
376
|
+
severity: "error",
|
|
377
|
+
message: `${options.file}: ${id} is defined more than once; the first definition was used`,
|
|
378
|
+
...at
|
|
379
|
+
});
|
|
380
|
+
summary.skipped++;
|
|
381
|
+
continue;
|
|
382
|
+
}
|
|
383
|
+
seen.add(id);
|
|
384
|
+
if (draft.patterns.some((p) => p.id === id)) {
|
|
385
|
+
collector.add({
|
|
386
|
+
code: "governance.pattern-id-taken",
|
|
387
|
+
severity: "error",
|
|
388
|
+
message: `${options.file}: ${id} already exists in the contract (from another source) and was not added`,
|
|
389
|
+
...at
|
|
390
|
+
});
|
|
391
|
+
summary.skipped++;
|
|
392
|
+
continue;
|
|
393
|
+
}
|
|
394
|
+
eligible.push(def);
|
|
395
|
+
}
|
|
396
|
+
const universe = {
|
|
397
|
+
components: draft.components.map((c) => ({ id: c.id, name: c.name })),
|
|
398
|
+
patterns: [
|
|
399
|
+
...draft.patterns.map((p) => ({ id: p.id, name: p.name })),
|
|
400
|
+
...eligible.map((d) => ({ id: idOf(d), name: d.name }))
|
|
401
|
+
],
|
|
402
|
+
tokens: draft.tokens.map((t) => ({ id: t.id, name: t.name })),
|
|
403
|
+
guidelines: draft.guidelines.map((g) => ({ id: g.id, name: g.name }))
|
|
404
|
+
};
|
|
405
|
+
const patterns = [];
|
|
406
|
+
const constraints = [];
|
|
407
|
+
const policies = [];
|
|
408
|
+
const evidence = [];
|
|
409
|
+
for (const def of eligible) {
|
|
410
|
+
const id = idOf(def);
|
|
411
|
+
const refs = new References(universe, id);
|
|
412
|
+
const origin = {
|
|
413
|
+
sourceId: PATTERN_SOURCE_ID,
|
|
414
|
+
sourceRef: `${options.file}#${localName(id)}`,
|
|
415
|
+
location: { type: "file", path: options.file },
|
|
416
|
+
method: "manual",
|
|
417
|
+
extractor: EXTRACTOR,
|
|
418
|
+
original: `${def.name}: ${def.intent ?? def.description}`.slice(0, 2000)
|
|
419
|
+
};
|
|
420
|
+
const item = {
|
|
421
|
+
...origin,
|
|
422
|
+
id: computeEvidenceId(origin),
|
|
423
|
+
verificationStatus: "unverified",
|
|
424
|
+
capturedAt: options.clock()
|
|
425
|
+
};
|
|
426
|
+
const ctx = {
|
|
427
|
+
def,
|
|
428
|
+
id,
|
|
429
|
+
refs,
|
|
430
|
+
evidence: item,
|
|
431
|
+
approved: def.approval !== undefined,
|
|
432
|
+
file: options.file
|
|
433
|
+
};
|
|
434
|
+
const built = build(ctx);
|
|
435
|
+
// A reference that did not resolve is already reported; checking the half-built pattern would only repeat it.
|
|
436
|
+
const structural = refs.problems.length === 0 ? patternSchema.safeParse(built.pattern) : undefined;
|
|
437
|
+
if (structural && !structural.success) {
|
|
438
|
+
const issue = structural.error.issues[0];
|
|
439
|
+
refs.problems.push({
|
|
440
|
+
code: "governance.pattern-invalid",
|
|
441
|
+
message: `${id} does not fit the contract at ${issue?.path.join(".") || "(root)"}: ${issue?.message ?? "schema error"}`
|
|
442
|
+
});
|
|
443
|
+
}
|
|
444
|
+
const collisions = [
|
|
445
|
+
...built.constraints.map((c) => c.id),
|
|
446
|
+
...built.policies.map((p) => p.id)
|
|
447
|
+
].filter((x) => taken.has(x));
|
|
448
|
+
for (const c of collisions)
|
|
449
|
+
refs.problems.push({
|
|
450
|
+
code: "governance.pattern-id-taken",
|
|
451
|
+
message: `${id}: ${c} already exists in the contract`
|
|
452
|
+
});
|
|
453
|
+
for (const c of def.constraints ?? [])
|
|
454
|
+
if (c.enforce && !def.approval)
|
|
455
|
+
collector.add({
|
|
456
|
+
code: "governance.pattern-policy-needs-approval",
|
|
457
|
+
severity: "warning",
|
|
458
|
+
message: `${options.file}: ${id} constraint "${c.id}" asks to be enforced, but the pattern has no approval; nothing infers one, so no policy was created. Add an approval block, or approve it and then add one.`,
|
|
459
|
+
...at
|
|
460
|
+
});
|
|
461
|
+
if (refs.problems.length > 0) {
|
|
462
|
+
for (const p of refs.problems)
|
|
463
|
+
collector.add({ code: p.code, severity: "error", message: `${options.file}: ${p.message}`, ...at });
|
|
464
|
+
collector.add({
|
|
465
|
+
code: "governance.pattern-skipped",
|
|
466
|
+
severity: "error",
|
|
467
|
+
message: `${options.file}: ${id} was not added to the contract because of the ${refs.problems.length} problem(s) above`,
|
|
468
|
+
...at
|
|
469
|
+
});
|
|
470
|
+
summary.skipped++;
|
|
471
|
+
continue;
|
|
472
|
+
}
|
|
473
|
+
patterns.push(built.pattern);
|
|
474
|
+
constraints.push(...built.constraints);
|
|
475
|
+
policies.push(...built.policies);
|
|
476
|
+
evidence.push(item);
|
|
477
|
+
summary.added++;
|
|
478
|
+
summary.constraints += built.constraints.length;
|
|
479
|
+
summary.policies += built.policies.length;
|
|
480
|
+
if (!def.approval)
|
|
481
|
+
summary.awaitingApproval++;
|
|
482
|
+
}
|
|
483
|
+
const problems = collector.toArray().filter((d) => d.severity !== "info").length;
|
|
484
|
+
const sources = summary.added + summary.skipped === 0
|
|
485
|
+
? draft.sources
|
|
486
|
+
: [
|
|
487
|
+
...draft.sources.filter((s) => s.id !== PATTERN_SOURCE_ID),
|
|
488
|
+
{
|
|
489
|
+
id: PATTERN_SOURCE_ID,
|
|
490
|
+
type: "patterns",
|
|
491
|
+
location: options.file,
|
|
492
|
+
version: { kind: "content-hash", value: sha256Hex(options.text) },
|
|
493
|
+
capabilities: SOURCE_CAPABILITIES,
|
|
494
|
+
health: { status: problems > 0 ? "degraded" : "ok", diagnosticCount: problems }
|
|
495
|
+
}
|
|
496
|
+
].sort(byId);
|
|
497
|
+
return {
|
|
498
|
+
contract: {
|
|
499
|
+
...draft,
|
|
500
|
+
sources,
|
|
501
|
+
patterns: [...draft.patterns, ...patterns].sort(byId),
|
|
502
|
+
constraints: [...draft.constraints, ...constraints].sort(byId),
|
|
503
|
+
policies: [...draft.policies, ...policies].sort(byId)
|
|
504
|
+
},
|
|
505
|
+
evidence,
|
|
506
|
+
diagnostics: collector.toArray(),
|
|
507
|
+
summary
|
|
508
|
+
};
|
|
509
|
+
}
|
|
510
|
+
//# sourceMappingURL=patterns.js.map
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import { DiagnosticCollector } from "../shared/index.js";
|
|
2
|
+
import { policyRuleSchema } from "../validation/index.js";
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
export const POLICIES_FILE = "invariant.policies.json";
|
|
5
|
+
const slugRe = /^[a-z0-9][a-z0-9._-]*$/;
|
|
6
|
+
/**
|
|
7
|
+
* Organizational rules, written and approved by people and kept in version control. Nothing infers a policy:
|
|
8
|
+
* every entry must carry who approved it and when.
|
|
9
|
+
*/
|
|
10
|
+
export const policiesFileSchema = z.strictObject({
|
|
11
|
+
version: z.literal(1),
|
|
12
|
+
policies: z.array(z.strictObject({
|
|
13
|
+
id: z.string().regex(slugRe).max(64),
|
|
14
|
+
name: z.string().min(1).max(200),
|
|
15
|
+
statement: z.string().min(1).max(2000),
|
|
16
|
+
severity: z.enum(["error", "warning", "info"]),
|
|
17
|
+
enforcement: z.enum(["block", "warn", "report-only"]).default("block"),
|
|
18
|
+
approval: z.strictObject({
|
|
19
|
+
approvedBy: z.string().min(1).max(200),
|
|
20
|
+
approvedAt: z.iso.datetime(),
|
|
21
|
+
role: z.string().min(1).max(200).optional(),
|
|
22
|
+
reference: z.string().min(1).max(500).optional()
|
|
23
|
+
}),
|
|
24
|
+
rule: policyRuleSchema
|
|
25
|
+
}))
|
|
26
|
+
});
|
|
27
|
+
export function parsePolicies(text) {
|
|
28
|
+
let raw;
|
|
29
|
+
try {
|
|
30
|
+
raw = JSON.parse(text);
|
|
31
|
+
}
|
|
32
|
+
catch (error) {
|
|
33
|
+
return { ok: false, message: `${POLICIES_FILE} is not valid JSON: ${error.message}` };
|
|
34
|
+
}
|
|
35
|
+
const parsed = policiesFileSchema.safeParse(raw);
|
|
36
|
+
if (!parsed.success) {
|
|
37
|
+
const i = parsed.error.issues[0];
|
|
38
|
+
return {
|
|
39
|
+
ok: false,
|
|
40
|
+
message: `${POLICIES_FILE} is invalid at ${i?.path.join(".") || "(root)"}: ${i?.message ?? "schema error"}`
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
const seen = new Set();
|
|
44
|
+
for (const p of parsed.data.policies) {
|
|
45
|
+
if (seen.has(p.id))
|
|
46
|
+
return { ok: false, message: `${POLICIES_FILE}: duplicate policy id "${p.id}"` };
|
|
47
|
+
seen.add(p.id);
|
|
48
|
+
}
|
|
49
|
+
return { ok: true, value: parsed.data };
|
|
50
|
+
}
|
|
51
|
+
const referencedComponents = (rule) => {
|
|
52
|
+
switch (rule.kind) {
|
|
53
|
+
case "require-attribute":
|
|
54
|
+
return rule.components;
|
|
55
|
+
case "forbid-prop":
|
|
56
|
+
case "forbid-prop-value":
|
|
57
|
+
case "forbid-component":
|
|
58
|
+
return [rule.component];
|
|
59
|
+
case "allowed-children":
|
|
60
|
+
return [rule.container, ...rule.children];
|
|
61
|
+
case "required-parent":
|
|
62
|
+
return [rule.component, ...rule.parents];
|
|
63
|
+
case "forbid-token":
|
|
64
|
+
return [];
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
const KIND = {
|
|
68
|
+
"require-attribute": "required-prop",
|
|
69
|
+
"forbid-prop": "deprecated-api",
|
|
70
|
+
"forbid-prop-value": "invalid-combination",
|
|
71
|
+
"forbid-component": "deprecated-api",
|
|
72
|
+
"forbid-token": "deprecated-api",
|
|
73
|
+
"allowed-children": "valid-combination",
|
|
74
|
+
"required-parent": "valid-combination"
|
|
75
|
+
};
|
|
76
|
+
const norm = (s) => s
|
|
77
|
+
.replace(/^component:/, "")
|
|
78
|
+
.toLowerCase()
|
|
79
|
+
.replace(/[^a-z0-9]/g, "");
|
|
80
|
+
/**
|
|
81
|
+
* Adds each approved policy to the contract as a policy plus the constraint that carries its rule. Existing
|
|
82
|
+
* entries are never changed. A policy that names a component the contract does not have is still added (it
|
|
83
|
+
* simply never fires) and is reported.
|
|
84
|
+
*/
|
|
85
|
+
export function compilePolicies(draft, file, source = POLICIES_FILE) {
|
|
86
|
+
const collector = new DiagnosticCollector();
|
|
87
|
+
const policies = [];
|
|
88
|
+
const constraints = [];
|
|
89
|
+
const existing = new Set([...draft.policies.map((p) => p.id), ...draft.constraints.map((c) => c.id)]);
|
|
90
|
+
for (const p of file.policies) {
|
|
91
|
+
const policyId = `policy:${p.id}`;
|
|
92
|
+
const constraintId = `constraint:${p.id}`;
|
|
93
|
+
if (existing.has(policyId) || existing.has(constraintId)) {
|
|
94
|
+
collector.error("governance.policy-id-taken", `${source}: ${policyId} already exists in the contract and was not added`);
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
97
|
+
const subjects = new Set();
|
|
98
|
+
for (const ref of referencedComponents(p.rule)) {
|
|
99
|
+
const found = draft.components.find((c) => c.id === ref || norm(c.name) === norm(ref) || norm(c.id) === norm(ref));
|
|
100
|
+
if (found)
|
|
101
|
+
subjects.add(found.id);
|
|
102
|
+
else
|
|
103
|
+
collector.warning("governance.policy-unknown-component", `${source}: policy ${p.id} names "${ref}", which is not a component in the contract; that part of the policy cannot fire`);
|
|
104
|
+
}
|
|
105
|
+
if (p.rule.kind === "forbid-token")
|
|
106
|
+
for (const t of p.rule.tokens) {
|
|
107
|
+
const n = norm(t.replace(/^token:/, "").replace(/^--/, ""));
|
|
108
|
+
const found = draft.tokens.find((x) => norm(x.name.replace(/^--/, "")) === n);
|
|
109
|
+
if (found)
|
|
110
|
+
subjects.add(found.id);
|
|
111
|
+
else
|
|
112
|
+
collector.warning("governance.policy-unknown-token", `${source}: policy ${p.id} names token "${t}", which is not in the contract`);
|
|
113
|
+
}
|
|
114
|
+
const ruleJson = p.rule;
|
|
115
|
+
constraints.push({
|
|
116
|
+
id: constraintId,
|
|
117
|
+
name: p.name,
|
|
118
|
+
kind: KIND[p.rule.kind],
|
|
119
|
+
description: p.statement,
|
|
120
|
+
subjects: subjects.size > 0 ? [...subjects].sort() : [policyId],
|
|
121
|
+
details: ruleJson,
|
|
122
|
+
facts: {},
|
|
123
|
+
extensions: { invariant: { rule: ruleJson, source, policy: p.id } }
|
|
124
|
+
});
|
|
125
|
+
policies.push({
|
|
126
|
+
id: policyId,
|
|
127
|
+
name: p.name,
|
|
128
|
+
statement: p.statement,
|
|
129
|
+
severity: p.severity,
|
|
130
|
+
enforcement: p.enforcement,
|
|
131
|
+
scope: subjects.size > 0
|
|
132
|
+
? { entities: [...subjects].filter((s) => s.startsWith("component:")).sort() }
|
|
133
|
+
: {},
|
|
134
|
+
basedOn: [constraintId],
|
|
135
|
+
approval: p.approval
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
const cmp = (a, b) => (a.id < b.id ? -1 : a.id > b.id ? 1 : 0);
|
|
139
|
+
return {
|
|
140
|
+
contract: {
|
|
141
|
+
...draft,
|
|
142
|
+
policies: [...draft.policies, ...policies].sort(cmp),
|
|
143
|
+
constraints: [...draft.constraints, ...constraints].sort(cmp)
|
|
144
|
+
},
|
|
145
|
+
diagnostics: collector.toArray()
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
//# sourceMappingURL=policies.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { ContractSource } from "./loader.js";
|
|
2
|
+
export { ERROR, INSTRUCTIONS, LEGACY_VERSIONS, MODERN_VERSIONS, PROVENANCE_KEY } from "./protocol.js";
|
|
3
|
+
export { RESOURCE_TEMPLATES, RESOURCES } from "./resources.js";
|
|
4
|
+
export { MAX_RESULT_CHARS, McpServer } from "./server.js";
|
|
5
|
+
export { serveStdio } from "./stdio.js";
|
|
6
|
+
export { TOOLS } from "./tools.js";
|
|
7
|
+
//# sourceMappingURL=index.js.map
|