@intentius/chant 0.0.1
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/README.md +365 -0
- package/package.json +22 -0
- package/src/attrref.test.ts +148 -0
- package/src/attrref.ts +50 -0
- package/src/barrel.test.ts +157 -0
- package/src/barrel.ts +101 -0
- package/src/bench.test.ts +227 -0
- package/src/build.test.ts +437 -0
- package/src/build.ts +425 -0
- package/src/builder.test.ts +312 -0
- package/src/builder.ts +56 -0
- package/src/child-project.ts +44 -0
- package/src/cli/commands/__fixtures__/init-lexicon-output/README.md +26 -0
- package/src/cli/commands/__fixtures__/init-lexicon-output/docs/astro.config.mjs +14 -0
- package/src/cli/commands/__fixtures__/init-lexicon-output/docs/package.json +16 -0
- package/src/cli/commands/__fixtures__/init-lexicon-output/docs/src/content/docs/index.mdx +8 -0
- package/src/cli/commands/__fixtures__/init-lexicon-output/docs/src/content.config.ts +7 -0
- package/src/cli/commands/__fixtures__/init-lexicon-output/docs/tsconfig.json +10 -0
- package/src/cli/commands/__fixtures__/init-lexicon-output/examples/getting-started/.gitkeep +0 -0
- package/src/cli/commands/__fixtures__/init-lexicon-output/justfile +26 -0
- package/src/cli/commands/__fixtures__/init-lexicon-output/package.json +29 -0
- package/src/cli/commands/__fixtures__/init-lexicon-output/src/codegen/docs.ts +25 -0
- package/src/cli/commands/__fixtures__/init-lexicon-output/src/codegen/generate-cli.ts +8 -0
- package/src/cli/commands/__fixtures__/init-lexicon-output/src/codegen/generate.ts +74 -0
- package/src/cli/commands/__fixtures__/init-lexicon-output/src/codegen/naming.ts +33 -0
- package/src/cli/commands/__fixtures__/init-lexicon-output/src/codegen/package.ts +25 -0
- package/src/cli/commands/__fixtures__/init-lexicon-output/src/codegen/rollback.ts +45 -0
- package/src/cli/commands/__fixtures__/init-lexicon-output/src/coverage.ts +11 -0
- package/src/cli/commands/__fixtures__/init-lexicon-output/src/generated/.gitkeep +0 -0
- package/src/cli/commands/__fixtures__/init-lexicon-output/src/import/generator.ts +10 -0
- package/src/cli/commands/__fixtures__/init-lexicon-output/src/import/parser.ts +10 -0
- package/src/cli/commands/__fixtures__/init-lexicon-output/src/index.ts +9 -0
- package/src/cli/commands/__fixtures__/init-lexicon-output/src/lint/rules/index.ts +1 -0
- package/src/cli/commands/__fixtures__/init-lexicon-output/src/lint/rules/sample.ts +18 -0
- package/src/cli/commands/__fixtures__/init-lexicon-output/src/lsp/completions.ts +14 -0
- package/src/cli/commands/__fixtures__/init-lexicon-output/src/lsp/hover.ts +14 -0
- package/src/cli/commands/__fixtures__/init-lexicon-output/src/plugin.ts +110 -0
- package/src/cli/commands/__fixtures__/init-lexicon-output/src/serializer.ts +24 -0
- package/src/cli/commands/__fixtures__/init-lexicon-output/src/spec/fetch.ts +21 -0
- package/src/cli/commands/__fixtures__/init-lexicon-output/src/spec/parse.ts +25 -0
- package/src/cli/commands/__fixtures__/init-lexicon-output/src/validate-cli.ts +4 -0
- package/src/cli/commands/__fixtures__/init-lexicon-output/src/validate.ts +24 -0
- package/src/cli/commands/__fixtures__/init-lexicon-output/tsconfig.json +10 -0
- package/src/cli/commands/__fixtures__/sample-rule.ts +11 -0
- package/src/cli/commands/__snapshots__/init-lexicon.test.ts.snap +222 -0
- package/src/cli/commands/build.test.ts +149 -0
- package/src/cli/commands/build.ts +344 -0
- package/src/cli/commands/diff.test.ts +148 -0
- package/src/cli/commands/diff.ts +221 -0
- package/src/cli/commands/doctor.test.ts +239 -0
- package/src/cli/commands/doctor.ts +224 -0
- package/src/cli/commands/import.test.ts +379 -0
- package/src/cli/commands/import.ts +335 -0
- package/src/cli/commands/init-lexicon.test.ts +297 -0
- package/src/cli/commands/init-lexicon.ts +993 -0
- package/src/cli/commands/init.test.ts +317 -0
- package/src/cli/commands/init.ts +505 -0
- package/src/cli/commands/licenses.ts +165 -0
- package/src/cli/commands/lint.test.ts +332 -0
- package/src/cli/commands/lint.ts +408 -0
- package/src/cli/commands/list.test.ts +100 -0
- package/src/cli/commands/list.ts +108 -0
- package/src/cli/commands/update.test.ts +38 -0
- package/src/cli/commands/update.ts +207 -0
- package/src/cli/conflict-check.test.ts +255 -0
- package/src/cli/conflict-check.ts +89 -0
- package/src/cli/debug.ts +8 -0
- package/src/cli/format.test.ts +140 -0
- package/src/cli/format.ts +133 -0
- package/src/cli/handlers/build.ts +58 -0
- package/src/cli/handlers/dev.ts +38 -0
- package/src/cli/handlers/init.ts +46 -0
- package/src/cli/handlers/lint.ts +36 -0
- package/src/cli/handlers/misc.ts +57 -0
- package/src/cli/handlers/serve.ts +26 -0
- package/src/cli/index.ts +3 -0
- package/src/cli/lsp/capabilities.ts +46 -0
- package/src/cli/lsp/diagnostics.ts +52 -0
- package/src/cli/lsp/server.test.ts +618 -0
- package/src/cli/lsp/server.ts +393 -0
- package/src/cli/main.test.ts +257 -0
- package/src/cli/main.ts +224 -0
- package/src/cli/mcp/resources/context.ts +59 -0
- package/src/cli/mcp/server.test.ts +747 -0
- package/src/cli/mcp/server.ts +402 -0
- package/src/cli/mcp/tools/build.ts +117 -0
- package/src/cli/mcp/tools/import.ts +48 -0
- package/src/cli/mcp/tools/lint.ts +45 -0
- package/src/cli/plugins.test.ts +31 -0
- package/src/cli/plugins.ts +94 -0
- package/src/cli/registry.ts +73 -0
- package/src/cli/reporters/stylish.test.ts +282 -0
- package/src/cli/reporters/stylish.ts +186 -0
- package/src/cli/watch.test.ts +81 -0
- package/src/cli/watch.ts +101 -0
- package/src/codegen/case.test.ts +30 -0
- package/src/codegen/case.ts +11 -0
- package/src/codegen/coverage.ts +167 -0
- package/src/codegen/docs.ts +634 -0
- package/src/codegen/fetch.test.ts +119 -0
- package/src/codegen/fetch.ts +261 -0
- package/src/codegen/generate-registry.test.ts +118 -0
- package/src/codegen/generate-registry.ts +107 -0
- package/src/codegen/generate-runtime-index.test.ts +81 -0
- package/src/codegen/generate-runtime-index.ts +99 -0
- package/src/codegen/generate-typescript.test.ts +146 -0
- package/src/codegen/generate-typescript.ts +161 -0
- package/src/codegen/generate.ts +206 -0
- package/src/codegen/json-patch.test.ts +113 -0
- package/src/codegen/json-patch.ts +151 -0
- package/src/codegen/json-schema.test.ts +196 -0
- package/src/codegen/json-schema.ts +209 -0
- package/src/codegen/naming.ts +201 -0
- package/src/codegen/package.ts +161 -0
- package/src/codegen/rollback.test.ts +92 -0
- package/src/codegen/rollback.ts +115 -0
- package/src/codegen/topo-sort.test.ts +69 -0
- package/src/codegen/topo-sort.ts +46 -0
- package/src/codegen/typecheck.test.ts +37 -0
- package/src/codegen/typecheck.ts +74 -0
- package/src/codegen/validate.test.ts +86 -0
- package/src/codegen/validate.ts +143 -0
- package/src/composite.test.ts +426 -0
- package/src/composite.ts +243 -0
- package/src/config.test.ts +91 -0
- package/src/config.ts +87 -0
- package/src/declarable.test.ts +160 -0
- package/src/declarable.ts +47 -0
- package/src/detectLexicon.test.ts +236 -0
- package/src/detectLexicon.ts +37 -0
- package/src/discovery/cache.test.ts +78 -0
- package/src/discovery/cache.ts +86 -0
- package/src/discovery/collect.test.ts +269 -0
- package/src/discovery/collect.ts +51 -0
- package/src/discovery/cycles.test.ts +238 -0
- package/src/discovery/cycles.ts +107 -0
- package/src/discovery/files.test.ts +154 -0
- package/src/discovery/files.ts +61 -0
- package/src/discovery/graph.test.ts +476 -0
- package/src/discovery/graph.ts +150 -0
- package/src/discovery/import.test.ts +199 -0
- package/src/discovery/import.ts +20 -0
- package/src/discovery/index.test.ts +272 -0
- package/src/discovery/index.ts +132 -0
- package/src/discovery/resolve.test.ts +267 -0
- package/src/discovery/resolve.ts +54 -0
- package/src/errors.test.ts +138 -0
- package/src/errors.ts +86 -0
- package/src/import/base-parser.test.ts +67 -0
- package/src/import/base-parser.ts +48 -0
- package/src/import/generator.ts +21 -0
- package/src/import/ir-utils.test.ts +103 -0
- package/src/import/ir-utils.ts +87 -0
- package/src/import/parser.ts +41 -0
- package/src/index.ts +60 -0
- package/src/intrinsic-interpolation.test.ts +91 -0
- package/src/intrinsic-interpolation.ts +89 -0
- package/src/intrinsic.test.ts +69 -0
- package/src/intrinsic.ts +43 -0
- package/src/lexicon-integrity.test.ts +94 -0
- package/src/lexicon-integrity.ts +69 -0
- package/src/lexicon-manifest.test.ts +101 -0
- package/src/lexicon-manifest.ts +71 -0
- package/src/lexicon-output.test.ts +182 -0
- package/src/lexicon-output.ts +82 -0
- package/src/lexicon-schema.test.ts +239 -0
- package/src/lexicon-schema.ts +144 -0
- package/src/lexicon.ts +212 -0
- package/src/lint/config-overrides.test.ts +254 -0
- package/src/lint/config.test.ts +644 -0
- package/src/lint/config.ts +375 -0
- package/src/lint/declarative.test.ts +256 -0
- package/src/lint/declarative.ts +187 -0
- package/src/lint/engine.test.ts +465 -0
- package/src/lint/engine.ts +172 -0
- package/src/lint/named-checks.test.ts +37 -0
- package/src/lint/named-checks.ts +33 -0
- package/src/lint/parser.test.ts +129 -0
- package/src/lint/parser.ts +42 -0
- package/src/lint/post-synth.test.ts +113 -0
- package/src/lint/post-synth.ts +76 -0
- package/src/lint/presets/relaxed.json +19 -0
- package/src/lint/presets/strict.json +19 -0
- package/src/lint/rule-loader.test.ts +67 -0
- package/src/lint/rule-loader.ts +67 -0
- package/src/lint/rule-options.test.ts +141 -0
- package/src/lint/rule.test.ts +196 -0
- package/src/lint/rule.ts +98 -0
- package/src/lint/rules/barrel-import-style.test.ts +80 -0
- package/src/lint/rules/barrel-import-style.ts +59 -0
- package/src/lint/rules/composite-scope.ts +55 -0
- package/src/lint/rules/cor017-composite-name-match.test.ts +107 -0
- package/src/lint/rules/cor017-composite-name-match.ts +108 -0
- package/src/lint/rules/cor018-composite-prefer-lexicon-type.test.ts +172 -0
- package/src/lint/rules/cor018-composite-prefer-lexicon-type.ts +167 -0
- package/src/lint/rules/declarable-naming-convention.test.ts +69 -0
- package/src/lint/rules/declarable-naming-convention.ts +70 -0
- package/src/lint/rules/enforce-barrel-import.test.ts +169 -0
- package/src/lint/rules/enforce-barrel-import.ts +81 -0
- package/src/lint/rules/enforce-barrel-ref.test.ts +114 -0
- package/src/lint/rules/enforce-barrel-ref.ts +75 -0
- package/src/lint/rules/evl001-non-literal-expression.test.ts +158 -0
- package/src/lint/rules/evl001-non-literal-expression.ts +149 -0
- package/src/lint/rules/evl002-control-flow-resource.test.ts +110 -0
- package/src/lint/rules/evl002-control-flow-resource.ts +61 -0
- package/src/lint/rules/evl003-dynamic-property-access.test.ts +63 -0
- package/src/lint/rules/evl003-dynamic-property-access.ts +41 -0
- package/src/lint/rules/evl004-spread-non-const.test.ts +130 -0
- package/src/lint/rules/evl004-spread-non-const.ts +111 -0
- package/src/lint/rules/evl005-resource-block-body.test.ts +59 -0
- package/src/lint/rules/evl005-resource-block-body.ts +49 -0
- package/src/lint/rules/evl006-barrel-usage.test.ts +63 -0
- package/src/lint/rules/evl006-barrel-usage.ts +95 -0
- package/src/lint/rules/evl007-invalid-siblings.test.ts +87 -0
- package/src/lint/rules/evl007-invalid-siblings.ts +139 -0
- package/src/lint/rules/evl008-unresolvable-barrel-ref.test.ts +118 -0
- package/src/lint/rules/evl008-unresolvable-barrel-ref.ts +140 -0
- package/src/lint/rules/evl009-composite-no-constant.test.ts +162 -0
- package/src/lint/rules/evl009-composite-no-constant.ts +171 -0
- package/src/lint/rules/evl010-composite-no-transform.test.ts +121 -0
- package/src/lint/rules/evl010-composite-no-transform.ts +69 -0
- package/src/lint/rules/export-required.test.ts +213 -0
- package/src/lint/rules/export-required.ts +158 -0
- package/src/lint/rules/file-declarable-limit.test.ts +148 -0
- package/src/lint/rules/file-declarable-limit.ts +96 -0
- package/src/lint/rules/flat-declarations.test.ts +210 -0
- package/src/lint/rules/flat-declarations.ts +70 -0
- package/src/lint/rules/index.ts +99 -0
- package/src/lint/rules/no-cyclic-declarable-ref.test.ts +135 -0
- package/src/lint/rules/no-cyclic-declarable-ref.ts +178 -0
- package/src/lint/rules/no-redundant-type-import.test.ts +129 -0
- package/src/lint/rules/no-redundant-type-import.ts +85 -0
- package/src/lint/rules/no-redundant-value-cast.test.ts +51 -0
- package/src/lint/rules/no-redundant-value-cast.ts +46 -0
- package/src/lint/rules/no-string-ref.test.ts +100 -0
- package/src/lint/rules/no-string-ref.ts +66 -0
- package/src/lint/rules/no-unused-declarable-import.test.ts +74 -0
- package/src/lint/rules/no-unused-declarable-import.ts +103 -0
- package/src/lint/rules/no-unused-declarable.test.ts +134 -0
- package/src/lint/rules/no-unused-declarable.ts +118 -0
- package/src/lint/rules/prefer-namespace-import.test.ts +102 -0
- package/src/lint/rules/prefer-namespace-import.ts +63 -0
- package/src/lint/rules/single-concern-file.test.ts +156 -0
- package/src/lint/rules/single-concern-file.ts +98 -0
- package/src/lint/rules/stale-barrel-types.ts +60 -0
- package/src/lint/selectors.test.ts +113 -0
- package/src/lint/selectors.ts +188 -0
- package/src/lsp/lexicon-providers.ts +191 -0
- package/src/lsp/types.ts +79 -0
- package/src/mcp/types.ts +22 -0
- package/src/project/scan.test.ts +178 -0
- package/src/project/scan.ts +182 -0
- package/src/project/sync.test.ts +87 -0
- package/src/project/sync.ts +46 -0
- package/src/project-validation.test.ts +64 -0
- package/src/project-validation.ts +79 -0
- package/src/pseudo-parameter.test.ts +39 -0
- package/src/pseudo-parameter.ts +47 -0
- package/src/runtime.ts +68 -0
- package/src/serializer-walker.test.ts +124 -0
- package/src/serializer-walker.ts +83 -0
- package/src/serializer.ts +42 -0
- package/src/sort.test.ts +290 -0
- package/src/sort.ts +58 -0
- package/src/stack-output.ts +82 -0
- package/src/types.test.ts +307 -0
- package/src/types.ts +46 -0
- package/src/utils.test.ts +195 -0
- package/src/utils.ts +46 -0
- package/src/validation.test.ts +308 -0
- package/src/validation.ts +50 -0
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
import { describe, test, expect } from "bun:test";
|
|
2
|
+
import {
|
|
3
|
+
validate,
|
|
4
|
+
type ValidationRule,
|
|
5
|
+
type ValidationResult,
|
|
6
|
+
} from "./validation";
|
|
7
|
+
import type { Declarable } from "./declarable";
|
|
8
|
+
import { DECLARABLE_MARKER } from "./declarable";
|
|
9
|
+
|
|
10
|
+
describe("ValidationResult", () => {
|
|
11
|
+
test("valid result has valid: true", () => {
|
|
12
|
+
const result: ValidationResult = {
|
|
13
|
+
valid: true,
|
|
14
|
+
};
|
|
15
|
+
expect(result.valid).toBe(true);
|
|
16
|
+
expect(result.message).toBeUndefined();
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
test("invalid result has valid: false and message", () => {
|
|
20
|
+
const result: ValidationResult = {
|
|
21
|
+
valid: false,
|
|
22
|
+
message: "Validation failed",
|
|
23
|
+
};
|
|
24
|
+
expect(result.valid).toBe(false);
|
|
25
|
+
expect(result.message).toBe("Validation failed");
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
test("can include optional context", () => {
|
|
29
|
+
const result: ValidationResult = {
|
|
30
|
+
valid: false,
|
|
31
|
+
message: "Invalid property",
|
|
32
|
+
context: { property: "name", value: null },
|
|
33
|
+
};
|
|
34
|
+
expect(result.context).toEqual({ property: "name", value: null });
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
describe("ValidationRule", () => {
|
|
39
|
+
test("rule has id, description, and validate function", () => {
|
|
40
|
+
const rule: ValidationRule = {
|
|
41
|
+
id: "test-rule",
|
|
42
|
+
description: "A test validation rule",
|
|
43
|
+
validate: (entity: Declarable) => ({ valid: true }),
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
expect(rule.id).toBe("test-rule");
|
|
47
|
+
expect(rule.description).toBe("A test validation rule");
|
|
48
|
+
expect(typeof rule.validate).toBe("function");
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
test("validate function receives entity and returns result", () => {
|
|
52
|
+
const entity: Declarable = {
|
|
53
|
+
entityType: "Test",
|
|
54
|
+
[DECLARABLE_MARKER]: true,
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
const rule: ValidationRule = {
|
|
58
|
+
id: "test-rule",
|
|
59
|
+
description: "Test rule",
|
|
60
|
+
validate: (e: Declarable) => {
|
|
61
|
+
expect(e).toBe(entity);
|
|
62
|
+
return { valid: true };
|
|
63
|
+
},
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
const result = rule.validate(entity);
|
|
67
|
+
expect(result.valid).toBe(true);
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
describe("validate", () => {
|
|
72
|
+
test("returns empty array when no rules provided", () => {
|
|
73
|
+
const entity: Declarable = {
|
|
74
|
+
entityType: "Test",
|
|
75
|
+
[DECLARABLE_MARKER]: true,
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
const results = validate(entity, []);
|
|
79
|
+
expect(results).toEqual([]);
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
test("applies single rule and returns result", () => {
|
|
83
|
+
const entity: Declarable = {
|
|
84
|
+
entityType: "Test",
|
|
85
|
+
[DECLARABLE_MARKER]: true,
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
const rule: ValidationRule = {
|
|
89
|
+
id: "always-valid",
|
|
90
|
+
description: "Always returns valid",
|
|
91
|
+
validate: () => ({ valid: true }),
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
const results = validate(entity, [rule]);
|
|
95
|
+
expect(results).toHaveLength(1);
|
|
96
|
+
expect(results[0].valid).toBe(true);
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
test("applies multiple rules and returns all results", () => {
|
|
100
|
+
const entity: Declarable = {
|
|
101
|
+
entityType: "Test",
|
|
102
|
+
[DECLARABLE_MARKER]: true,
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
const rule1: ValidationRule = {
|
|
106
|
+
id: "rule-1",
|
|
107
|
+
description: "First rule",
|
|
108
|
+
validate: () => ({ valid: true }),
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
const rule2: ValidationRule = {
|
|
112
|
+
id: "rule-2",
|
|
113
|
+
description: "Second rule",
|
|
114
|
+
validate: () => ({ valid: false, message: "Rule 2 failed" }),
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
const rule3: ValidationRule = {
|
|
118
|
+
id: "rule-3",
|
|
119
|
+
description: "Third rule",
|
|
120
|
+
validate: () => ({ valid: true }),
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
const results = validate(entity, [rule1, rule2, rule3]);
|
|
124
|
+
expect(results).toHaveLength(3);
|
|
125
|
+
expect(results[0].valid).toBe(true);
|
|
126
|
+
expect(results[1].valid).toBe(false);
|
|
127
|
+
expect(results[1].message).toBe("Rule 2 failed");
|
|
128
|
+
expect(results[2].valid).toBe(true);
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
test("is non-blocking - does not throw on validation failure", () => {
|
|
132
|
+
const entity: Declarable = {
|
|
133
|
+
entityType: "Test",
|
|
134
|
+
[DECLARABLE_MARKER]: true,
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
const failingRule: ValidationRule = {
|
|
138
|
+
id: "failing-rule",
|
|
139
|
+
description: "Always fails",
|
|
140
|
+
validate: () => ({ valid: false, message: "Validation failed" }),
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
// Should not throw
|
|
144
|
+
expect(() => validate(entity, [failingRule])).not.toThrow();
|
|
145
|
+
|
|
146
|
+
const results = validate(entity, [failingRule]);
|
|
147
|
+
expect(results[0].valid).toBe(false);
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
test("passes entity to each rule", () => {
|
|
151
|
+
const entity: Declarable & { name: string } = {
|
|
152
|
+
entityType: "Test",
|
|
153
|
+
[DECLARABLE_MARKER]: true,
|
|
154
|
+
name: "MyEntity",
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
const rule1: ValidationRule = {
|
|
158
|
+
id: "check-entity",
|
|
159
|
+
description: "Checks entity properties",
|
|
160
|
+
validate: (e: Declarable) => {
|
|
161
|
+
const hasName = "name" in e && e.name === "MyEntity";
|
|
162
|
+
return {
|
|
163
|
+
valid: hasName,
|
|
164
|
+
message: hasName ? undefined : "Entity missing name",
|
|
165
|
+
};
|
|
166
|
+
},
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
const results = validate(entity, [rule1]);
|
|
170
|
+
expect(results[0].valid).toBe(true);
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
test("preserves rule execution order", () => {
|
|
174
|
+
const entity: Declarable = {
|
|
175
|
+
entityType: "Test",
|
|
176
|
+
[DECLARABLE_MARKER]: true,
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
const executionOrder: string[] = [];
|
|
180
|
+
|
|
181
|
+
const rule1: ValidationRule = {
|
|
182
|
+
id: "first",
|
|
183
|
+
description: "First",
|
|
184
|
+
validate: () => {
|
|
185
|
+
executionOrder.push("first");
|
|
186
|
+
return { valid: true };
|
|
187
|
+
},
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
const rule2: ValidationRule = {
|
|
191
|
+
id: "second",
|
|
192
|
+
description: "Second",
|
|
193
|
+
validate: () => {
|
|
194
|
+
executionOrder.push("second");
|
|
195
|
+
return { valid: true };
|
|
196
|
+
},
|
|
197
|
+
};
|
|
198
|
+
|
|
199
|
+
const rule3: ValidationRule = {
|
|
200
|
+
id: "third",
|
|
201
|
+
description: "Third",
|
|
202
|
+
validate: () => {
|
|
203
|
+
executionOrder.push("third");
|
|
204
|
+
return { valid: true };
|
|
205
|
+
},
|
|
206
|
+
};
|
|
207
|
+
|
|
208
|
+
validate(entity, [rule1, rule2, rule3]);
|
|
209
|
+
expect(executionOrder).toEqual(["first", "second", "third"]);
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
test("handles rules with context in results", () => {
|
|
213
|
+
const entity: Declarable & { count: number } = {
|
|
214
|
+
entityType: "Test",
|
|
215
|
+
[DECLARABLE_MARKER]: true,
|
|
216
|
+
count: 5,
|
|
217
|
+
};
|
|
218
|
+
|
|
219
|
+
const rule: ValidationRule = {
|
|
220
|
+
id: "check-count",
|
|
221
|
+
description: "Validates count property",
|
|
222
|
+
validate: (e: Declarable) => {
|
|
223
|
+
const count = "count" in e ? (e.count as number) : 0;
|
|
224
|
+
if (count < 10) {
|
|
225
|
+
return {
|
|
226
|
+
valid: false,
|
|
227
|
+
message: "Count is too low",
|
|
228
|
+
context: { count, minimum: 10 },
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
return { valid: true };
|
|
232
|
+
},
|
|
233
|
+
};
|
|
234
|
+
|
|
235
|
+
const results = validate(entity, [rule]);
|
|
236
|
+
expect(results[0].valid).toBe(false);
|
|
237
|
+
expect(results[0].message).toBe("Count is too low");
|
|
238
|
+
expect(results[0].context).toEqual({ count: 5, minimum: 10 });
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
test("handles entity type validation", () => {
|
|
242
|
+
const entity: Declarable = {
|
|
243
|
+
entityType: "Bucket",
|
|
244
|
+
[DECLARABLE_MARKER]: true,
|
|
245
|
+
};
|
|
246
|
+
|
|
247
|
+
const rule: ValidationRule = {
|
|
248
|
+
id: "check-type",
|
|
249
|
+
description: "Validates entity type",
|
|
250
|
+
validate: (e: Declarable) => {
|
|
251
|
+
const expectedTypes = ["Bucket", "Table", "Function"];
|
|
252
|
+
const valid = expectedTypes.includes(e.entityType);
|
|
253
|
+
return {
|
|
254
|
+
valid,
|
|
255
|
+
message: valid ? undefined : `Invalid type: ${e.entityType}`,
|
|
256
|
+
};
|
|
257
|
+
},
|
|
258
|
+
};
|
|
259
|
+
|
|
260
|
+
const results = validate(entity, [rule]);
|
|
261
|
+
expect(results[0].valid).toBe(true);
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
test("validation is opt-in - only runs rules provided", () => {
|
|
265
|
+
const entity: Declarable = {
|
|
266
|
+
entityType: "Test",
|
|
267
|
+
[DECLARABLE_MARKER]: true,
|
|
268
|
+
};
|
|
269
|
+
|
|
270
|
+
// If no rules provided, no validation happens
|
|
271
|
+
const results = validate(entity, []);
|
|
272
|
+
expect(results).toHaveLength(0);
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
test("returns results in same order as rules", () => {
|
|
276
|
+
const entity: Declarable = {
|
|
277
|
+
entityType: "Test",
|
|
278
|
+
[DECLARABLE_MARKER]: true,
|
|
279
|
+
};
|
|
280
|
+
|
|
281
|
+
const rules: ValidationRule[] = [
|
|
282
|
+
{
|
|
283
|
+
id: "rule-a",
|
|
284
|
+
description: "Rule A",
|
|
285
|
+
validate: () => ({ valid: true }),
|
|
286
|
+
},
|
|
287
|
+
{
|
|
288
|
+
id: "rule-b",
|
|
289
|
+
description: "Rule B",
|
|
290
|
+
validate: () => ({ valid: false, message: "B failed" }),
|
|
291
|
+
},
|
|
292
|
+
{
|
|
293
|
+
id: "rule-c",
|
|
294
|
+
description: "Rule C",
|
|
295
|
+
validate: () => ({ valid: true }),
|
|
296
|
+
},
|
|
297
|
+
];
|
|
298
|
+
|
|
299
|
+
const results = validate(entity, rules);
|
|
300
|
+
expect(results).toHaveLength(3);
|
|
301
|
+
|
|
302
|
+
// Results correspond to rules by index
|
|
303
|
+
expect(results[0].valid).toBe(true); // rule-a
|
|
304
|
+
expect(results[1].valid).toBe(false); // rule-b
|
|
305
|
+
expect(results[1].message).toBe("B failed");
|
|
306
|
+
expect(results[2].valid).toBe(true); // rule-c
|
|
307
|
+
});
|
|
308
|
+
});
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Validation system for declarable entities
|
|
3
|
+
*
|
|
4
|
+
* Provides opt-in, non-blocking validation hooks that can be used
|
|
5
|
+
* to validate entities during build or discovery phases.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { Declarable } from "./declarable";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Result of a validation check
|
|
12
|
+
*/
|
|
13
|
+
export interface ValidationResult {
|
|
14
|
+
/** Whether validation passed */
|
|
15
|
+
valid: boolean;
|
|
16
|
+
/** Error message if validation failed */
|
|
17
|
+
message?: string;
|
|
18
|
+
/** Optional additional context about the validation */
|
|
19
|
+
context?: Record<string, unknown>;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* A validation rule that can check declarable entities
|
|
24
|
+
*/
|
|
25
|
+
export interface ValidationRule {
|
|
26
|
+
/** Unique identifier for this rule */
|
|
27
|
+
id: string;
|
|
28
|
+
/** Human-readable description of what this rule checks */
|
|
29
|
+
description: string;
|
|
30
|
+
/** Validate an entity and return result */
|
|
31
|
+
validate(entity: Declarable): ValidationResult;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Validate an entity against a set of validation rules
|
|
36
|
+
*
|
|
37
|
+
* This function is opt-in and non-blocking - it returns results
|
|
38
|
+
* but does not throw errors. Callers can decide how to handle
|
|
39
|
+
* validation failures.
|
|
40
|
+
*
|
|
41
|
+
* @param entity - The declarable entity to validate
|
|
42
|
+
* @param rules - Array of validation rules to apply
|
|
43
|
+
* @returns Array of validation results, one per rule
|
|
44
|
+
*/
|
|
45
|
+
export function validate(
|
|
46
|
+
entity: Declarable,
|
|
47
|
+
rules: ValidationRule[]
|
|
48
|
+
): ValidationResult[] {
|
|
49
|
+
return rules.map((rule) => rule.validate(entity));
|
|
50
|
+
}
|