@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,307 @@
|
|
|
1
|
+
import { describe, test, expectTypeOf } from "bun:test";
|
|
2
|
+
import type { Value, AllValues, PartialValues, RequiredProps } from "./types";
|
|
3
|
+
import type { Intrinsic } from "./intrinsic";
|
|
4
|
+
|
|
5
|
+
describe("Value<T>", () => {
|
|
6
|
+
test("accepts concrete value", () => {
|
|
7
|
+
expectTypeOf<string>().toMatchTypeOf<Value<string>>();
|
|
8
|
+
expectTypeOf<number>().toMatchTypeOf<Value<number>>();
|
|
9
|
+
expectTypeOf<boolean>().toMatchTypeOf<Value<boolean>>();
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
test("accepts Intrinsic", () => {
|
|
13
|
+
expectTypeOf<Intrinsic>().toMatchTypeOf<Value<string>>();
|
|
14
|
+
expectTypeOf<Intrinsic>().toMatchTypeOf<Value<number>>();
|
|
15
|
+
expectTypeOf<Intrinsic>().toMatchTypeOf<Value<boolean>>();
|
|
16
|
+
});
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
describe("AllValues<T>", () => {
|
|
20
|
+
test("converts all properties to Value types", () => {
|
|
21
|
+
interface Props {
|
|
22
|
+
name: string;
|
|
23
|
+
count: number;
|
|
24
|
+
enabled: boolean;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
type ValueProps = AllValues<Props>;
|
|
28
|
+
|
|
29
|
+
expectTypeOf<ValueProps>().toEqualTypeOf<{
|
|
30
|
+
name: Value<string>;
|
|
31
|
+
count: Value<number>;
|
|
32
|
+
enabled: Value<boolean>;
|
|
33
|
+
}>();
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
test("preserves optional properties", () => {
|
|
37
|
+
interface Props {
|
|
38
|
+
required: string;
|
|
39
|
+
optional?: number;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
type ValueProps = AllValues<Props>;
|
|
43
|
+
|
|
44
|
+
expectTypeOf<ValueProps>().toEqualTypeOf<{
|
|
45
|
+
required: Value<string>;
|
|
46
|
+
optional?: Value<number>;
|
|
47
|
+
}>();
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
test("handles complex types", () => {
|
|
51
|
+
interface Props {
|
|
52
|
+
arr: string[];
|
|
53
|
+
obj: { key: string };
|
|
54
|
+
union: string | number;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
type ValueProps = AllValues<Props>;
|
|
58
|
+
|
|
59
|
+
expectTypeOf<ValueProps>().toEqualTypeOf<{
|
|
60
|
+
arr: Value<string[]>;
|
|
61
|
+
obj: Value<{ key: string }>;
|
|
62
|
+
union: Value<string | number>;
|
|
63
|
+
}>();
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
test("handles empty interface", () => {
|
|
67
|
+
interface Props {}
|
|
68
|
+
|
|
69
|
+
type ValueProps = AllValues<Props>;
|
|
70
|
+
|
|
71
|
+
expectTypeOf<ValueProps>().toEqualTypeOf<{}>();
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
test("handles nested objects", () => {
|
|
75
|
+
interface Props {
|
|
76
|
+
config: {
|
|
77
|
+
host: string;
|
|
78
|
+
port: number;
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
type ValueProps = AllValues<Props>;
|
|
83
|
+
|
|
84
|
+
expectTypeOf<ValueProps>().toEqualTypeOf<{
|
|
85
|
+
config: Value<{
|
|
86
|
+
host: string;
|
|
87
|
+
port: number;
|
|
88
|
+
}>;
|
|
89
|
+
}>();
|
|
90
|
+
});
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
describe("PartialValues<T, K>", () => {
|
|
94
|
+
test("converts specified properties to Value types", () => {
|
|
95
|
+
interface Props {
|
|
96
|
+
name: string;
|
|
97
|
+
count: number;
|
|
98
|
+
enabled: boolean;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
type ValueProps = PartialValues<Props, "name" | "count">;
|
|
102
|
+
|
|
103
|
+
expectTypeOf<ValueProps>().toEqualTypeOf<{
|
|
104
|
+
name: Value<string>;
|
|
105
|
+
count: Value<number>;
|
|
106
|
+
enabled: boolean;
|
|
107
|
+
}>();
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
test("converts single property to Value type", () => {
|
|
111
|
+
interface Props {
|
|
112
|
+
name: string;
|
|
113
|
+
count: number;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
type ValueProps = PartialValues<Props, "name">;
|
|
117
|
+
|
|
118
|
+
expectTypeOf<ValueProps>().toEqualTypeOf<{
|
|
119
|
+
name: Value<string>;
|
|
120
|
+
count: number;
|
|
121
|
+
}>();
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
test("preserves optional properties", () => {
|
|
125
|
+
interface Props {
|
|
126
|
+
name: string;
|
|
127
|
+
count?: number;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
type ValueProps = PartialValues<Props, "name">;
|
|
131
|
+
|
|
132
|
+
expectTypeOf<ValueProps>().toEqualTypeOf<{
|
|
133
|
+
name: Value<string>;
|
|
134
|
+
count?: number;
|
|
135
|
+
}>();
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
test("handles complex types", () => {
|
|
139
|
+
interface Props {
|
|
140
|
+
arr: string[];
|
|
141
|
+
obj: { key: string };
|
|
142
|
+
primitive: string;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
type ValueProps = PartialValues<Props, "arr" | "obj">;
|
|
146
|
+
|
|
147
|
+
expectTypeOf<ValueProps>().toEqualTypeOf<{
|
|
148
|
+
arr: Value<string[]>;
|
|
149
|
+
obj: Value<{ key: string }>;
|
|
150
|
+
primitive: string;
|
|
151
|
+
}>();
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
test("works with all properties", () => {
|
|
155
|
+
interface Props {
|
|
156
|
+
name: string;
|
|
157
|
+
count: number;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
type ValueProps = PartialValues<Props, "name" | "count">;
|
|
161
|
+
|
|
162
|
+
expectTypeOf<ValueProps>().toEqualTypeOf<{
|
|
163
|
+
name: Value<string>;
|
|
164
|
+
count: Value<number>;
|
|
165
|
+
}>();
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
test("works with no conversion (unrealistic but valid)", () => {
|
|
169
|
+
interface Props {
|
|
170
|
+
name: string;
|
|
171
|
+
count: number;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
type ValueProps = PartialValues<Props, never>;
|
|
175
|
+
|
|
176
|
+
expectTypeOf<ValueProps>().toEqualTypeOf<{
|
|
177
|
+
name: string;
|
|
178
|
+
count: number;
|
|
179
|
+
}>();
|
|
180
|
+
});
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
describe("RequiredProps<T, K>", () => {
|
|
184
|
+
test("makes specified properties required", () => {
|
|
185
|
+
interface Props {
|
|
186
|
+
name?: string;
|
|
187
|
+
count?: number;
|
|
188
|
+
enabled?: boolean;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
type RequiredNameProps = RequiredProps<Props, "name">;
|
|
192
|
+
|
|
193
|
+
expectTypeOf<RequiredNameProps>().toEqualTypeOf<{
|
|
194
|
+
name: string;
|
|
195
|
+
count?: number;
|
|
196
|
+
enabled?: boolean;
|
|
197
|
+
}>();
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
test("makes multiple properties required", () => {
|
|
201
|
+
interface Props {
|
|
202
|
+
name?: string;
|
|
203
|
+
count?: number;
|
|
204
|
+
enabled?: boolean;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
type RequiredMultiProps = RequiredProps<Props, "name" | "count">;
|
|
208
|
+
|
|
209
|
+
expectTypeOf<RequiredMultiProps>().toEqualTypeOf<{
|
|
210
|
+
name: string;
|
|
211
|
+
count: number;
|
|
212
|
+
enabled?: boolean;
|
|
213
|
+
}>();
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
test("handles already required properties", () => {
|
|
217
|
+
interface Props {
|
|
218
|
+
name: string;
|
|
219
|
+
count?: number;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
type RequiredNameProps = RequiredProps<Props, "name">;
|
|
223
|
+
|
|
224
|
+
expectTypeOf<RequiredNameProps>().toEqualTypeOf<{
|
|
225
|
+
name: string;
|
|
226
|
+
count?: number;
|
|
227
|
+
}>();
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
test("makes all properties required", () => {
|
|
231
|
+
interface Props {
|
|
232
|
+
name?: string;
|
|
233
|
+
count?: number;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
type AllRequiredProps = RequiredProps<Props, "name" | "count">;
|
|
237
|
+
|
|
238
|
+
expectTypeOf<AllRequiredProps>().toEqualTypeOf<{
|
|
239
|
+
name: string;
|
|
240
|
+
count: number;
|
|
241
|
+
}>();
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
test("handles complex types", () => {
|
|
245
|
+
interface Props {
|
|
246
|
+
arr?: string[];
|
|
247
|
+
obj?: { key: string };
|
|
248
|
+
primitive?: string;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
type RequiredArrProps = RequiredProps<Props, "arr">;
|
|
252
|
+
|
|
253
|
+
expectTypeOf<RequiredArrProps>().toEqualTypeOf<{
|
|
254
|
+
arr: string[];
|
|
255
|
+
obj?: { key: string };
|
|
256
|
+
primitive?: string;
|
|
257
|
+
}>();
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
test("handles mixed required and optional", () => {
|
|
261
|
+
interface Props {
|
|
262
|
+
required: string;
|
|
263
|
+
optional1?: number;
|
|
264
|
+
optional2?: boolean;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
type PartiallyRequired = RequiredProps<Props, "optional1">;
|
|
268
|
+
|
|
269
|
+
expectTypeOf<PartiallyRequired>().toEqualTypeOf<{
|
|
270
|
+
required: string;
|
|
271
|
+
optional1: number;
|
|
272
|
+
optional2?: boolean;
|
|
273
|
+
}>();
|
|
274
|
+
});
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
describe("Combined usage", () => {
|
|
278
|
+
test("AllValues and RequiredProps can be combined", () => {
|
|
279
|
+
interface Props {
|
|
280
|
+
name?: string;
|
|
281
|
+
count?: number;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
type Combined = AllValues<RequiredProps<Props, "name">>;
|
|
285
|
+
|
|
286
|
+
expectTypeOf<Combined>().toEqualTypeOf<{
|
|
287
|
+
name: Value<string>;
|
|
288
|
+
count?: Value<number>;
|
|
289
|
+
}>();
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
test("PartialValues and RequiredProps can be combined", () => {
|
|
293
|
+
interface Props {
|
|
294
|
+
name?: string;
|
|
295
|
+
count?: number;
|
|
296
|
+
enabled?: boolean;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
type Combined = PartialValues<RequiredProps<Props, "name">, "count">;
|
|
300
|
+
|
|
301
|
+
expectTypeOf<Combined>().toEqualTypeOf<{
|
|
302
|
+
name: string;
|
|
303
|
+
count?: Value<number>;
|
|
304
|
+
enabled?: boolean;
|
|
305
|
+
}>();
|
|
306
|
+
});
|
|
307
|
+
});
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { Intrinsic } from "./intrinsic";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Value type that can be either a concrete value T or an Intrinsic
|
|
5
|
+
*/
|
|
6
|
+
export type Value<T> = T | Intrinsic;
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Converts all properties of T to Value<T> types
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* ```ts
|
|
13
|
+
* interface Props { name: string; count: number }
|
|
14
|
+
* type ValueProps = AllValues<Props>
|
|
15
|
+
* // Result: { name: Value<string>; count: Value<number> }
|
|
16
|
+
* ```
|
|
17
|
+
*/
|
|
18
|
+
export type AllValues<T> = {
|
|
19
|
+
[K in keyof T]: Value<T[K]>;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Makes specific properties K of T be Value types while keeping others unchanged
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* ```ts
|
|
27
|
+
* interface Props { name: string; count: number; enabled: boolean }
|
|
28
|
+
* type ValueProps = PartialValues<Props, 'name' | 'count'>
|
|
29
|
+
* // Result: { name: Value<string>; count: Value<number>; enabled: boolean }
|
|
30
|
+
* ```
|
|
31
|
+
*/
|
|
32
|
+
export type PartialValues<T, K extends keyof T> = {
|
|
33
|
+
[P in keyof T]: P extends K ? Value<T[P]> : T[P];
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Makes specific properties K of T required while keeping others unchanged
|
|
38
|
+
*
|
|
39
|
+
* @example
|
|
40
|
+
* ```ts
|
|
41
|
+
* interface Props { name?: string; count?: number; enabled?: boolean }
|
|
42
|
+
* type RequiredNameProps = RequiredProps<Props, 'name'>
|
|
43
|
+
* // Result: { name: string; count?: number; enabled?: boolean }
|
|
44
|
+
* ```
|
|
45
|
+
*/
|
|
46
|
+
export type RequiredProps<T, K extends keyof T> = T & Required<Pick<T, K>>;
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
import { describe, test, expect } from "bun:test";
|
|
2
|
+
import {
|
|
3
|
+
LOGICAL_NAME_SYMBOL,
|
|
4
|
+
getAttributes,
|
|
5
|
+
getLogicalName,
|
|
6
|
+
} from "./utils";
|
|
7
|
+
import { AttrRef } from "./attrref";
|
|
8
|
+
import type { Declarable } from "./declarable";
|
|
9
|
+
import { DECLARABLE_MARKER } from "./declarable";
|
|
10
|
+
|
|
11
|
+
describe("LOGICAL_NAME_SYMBOL", () => {
|
|
12
|
+
test("is a symbol", () => {
|
|
13
|
+
expect(typeof LOGICAL_NAME_SYMBOL).toBe("symbol");
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
test("uses Symbol.for for global registry", () => {
|
|
17
|
+
expect(LOGICAL_NAME_SYMBOL).toBe(Symbol.for("chant.logicalName"));
|
|
18
|
+
});
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
describe("getAttributes", () => {
|
|
22
|
+
test("returns empty array for entity with no AttrRef properties", () => {
|
|
23
|
+
const entity: Declarable = {
|
|
24
|
+
entityType: "Test",
|
|
25
|
+
[DECLARABLE_MARKER]: true,
|
|
26
|
+
prop1: "value",
|
|
27
|
+
prop2: 123,
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
const attributes = getAttributes(entity);
|
|
31
|
+
expect(attributes).toEqual([]);
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
test("returns property names with AttrRef values", () => {
|
|
35
|
+
const parent = {};
|
|
36
|
+
const entity: Declarable & { arn: AttrRef; name: AttrRef } = {
|
|
37
|
+
entityType: "Test",
|
|
38
|
+
[DECLARABLE_MARKER]: true,
|
|
39
|
+
arn: new AttrRef(parent, "Arn"),
|
|
40
|
+
name: new AttrRef(parent, "Name"),
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
const attributes = getAttributes(entity);
|
|
44
|
+
expect(attributes).toContain("arn");
|
|
45
|
+
expect(attributes).toContain("name");
|
|
46
|
+
expect(attributes).toHaveLength(2);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
test("ignores non-AttrRef properties", () => {
|
|
50
|
+
const parent = {};
|
|
51
|
+
const entity: Declarable & {
|
|
52
|
+
arn: AttrRef;
|
|
53
|
+
regularProp: string;
|
|
54
|
+
numberProp: number;
|
|
55
|
+
} = {
|
|
56
|
+
entityType: "Test",
|
|
57
|
+
[DECLARABLE_MARKER]: true,
|
|
58
|
+
arn: new AttrRef(parent, "Arn"),
|
|
59
|
+
regularProp: "value",
|
|
60
|
+
numberProp: 42,
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
const attributes = getAttributes(entity);
|
|
64
|
+
expect(attributes).toEqual(["arn"]);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
test("returns attributes in order they appear", () => {
|
|
68
|
+
const parent = {};
|
|
69
|
+
const entity: Declarable & {
|
|
70
|
+
first: AttrRef;
|
|
71
|
+
second: AttrRef;
|
|
72
|
+
third: AttrRef;
|
|
73
|
+
} = {
|
|
74
|
+
entityType: "Test",
|
|
75
|
+
[DECLARABLE_MARKER]: true,
|
|
76
|
+
first: new AttrRef(parent, "First"),
|
|
77
|
+
second: new AttrRef(parent, "Second"),
|
|
78
|
+
third: new AttrRef(parent, "Third"),
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
const attributes = getAttributes(entity);
|
|
82
|
+
expect(attributes).toEqual(["first", "second", "third"]);
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
test("handles entity with mixed property types", () => {
|
|
86
|
+
const parent = {};
|
|
87
|
+
const entity: Declarable & {
|
|
88
|
+
attrRef: AttrRef;
|
|
89
|
+
string: string;
|
|
90
|
+
number: number;
|
|
91
|
+
boolean: boolean;
|
|
92
|
+
object: object;
|
|
93
|
+
anotherAttrRef: AttrRef;
|
|
94
|
+
} = {
|
|
95
|
+
entityType: "Test",
|
|
96
|
+
[DECLARABLE_MARKER]: true,
|
|
97
|
+
attrRef: new AttrRef(parent, "Attr1"),
|
|
98
|
+
string: "test",
|
|
99
|
+
number: 123,
|
|
100
|
+
boolean: true,
|
|
101
|
+
object: {},
|
|
102
|
+
anotherAttrRef: new AttrRef(parent, "Attr2"),
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
const attributes = getAttributes(entity);
|
|
106
|
+
expect(attributes).toContain("attrRef");
|
|
107
|
+
expect(attributes).toContain("anotherAttrRef");
|
|
108
|
+
expect(attributes).toHaveLength(2);
|
|
109
|
+
});
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
describe("getLogicalName", () => {
|
|
113
|
+
test("returns logical name when set", () => {
|
|
114
|
+
const entity: Declarable & Record<symbol, unknown> = {
|
|
115
|
+
entityType: "Test",
|
|
116
|
+
[DECLARABLE_MARKER]: true,
|
|
117
|
+
[LOGICAL_NAME_SYMBOL]: "MyResource",
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
const name = getLogicalName(entity);
|
|
121
|
+
expect(name).toBe("MyResource");
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
test("throws when logical name is not set", () => {
|
|
125
|
+
const entity: Declarable = {
|
|
126
|
+
entityType: "TestEntity",
|
|
127
|
+
[DECLARABLE_MARKER]: true,
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
expect(() => getLogicalName(entity)).toThrow(
|
|
131
|
+
'Logical name not set on entity of type "TestEntity"'
|
|
132
|
+
);
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
test("throws when logical name is not a string", () => {
|
|
136
|
+
const entity: Declarable & Record<symbol, unknown> = {
|
|
137
|
+
entityType: "TestEntity",
|
|
138
|
+
[DECLARABLE_MARKER]: true,
|
|
139
|
+
[LOGICAL_NAME_SYMBOL]: 123,
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
expect(() => getLogicalName(entity)).toThrow(
|
|
143
|
+
'Logical name not set on entity of type "TestEntity"'
|
|
144
|
+
);
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
test("handles different logical name values", () => {
|
|
148
|
+
const names = ["MyBucket", "MyTable", "My-Function-123", "Resource_1"];
|
|
149
|
+
|
|
150
|
+
for (const name of names) {
|
|
151
|
+
const entity: Declarable & Record<symbol, unknown> = {
|
|
152
|
+
entityType: "Test",
|
|
153
|
+
[DECLARABLE_MARKER]: true,
|
|
154
|
+
[LOGICAL_NAME_SYMBOL]: name,
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
expect(getLogicalName(entity)).toBe(name);
|
|
158
|
+
}
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
test("includes entity type in error message", () => {
|
|
162
|
+
const entity: Declarable = {
|
|
163
|
+
entityType: "MyCustomType",
|
|
164
|
+
[DECLARABLE_MARKER]: true,
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
expect(() => getLogicalName(entity)).toThrow(
|
|
168
|
+
'Logical name not set on entity of type "MyCustomType"'
|
|
169
|
+
);
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
test("throws when logical name is undefined", () => {
|
|
173
|
+
const entity: Declarable & Record<symbol, unknown> = {
|
|
174
|
+
entityType: "TestEntity",
|
|
175
|
+
[DECLARABLE_MARKER]: true,
|
|
176
|
+
[LOGICAL_NAME_SYMBOL]: undefined,
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
expect(() => getLogicalName(entity)).toThrow(
|
|
180
|
+
'Logical name not set on entity of type "TestEntity"'
|
|
181
|
+
);
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
test("throws when logical name is null", () => {
|
|
185
|
+
const entity: Declarable & Record<symbol, unknown> = {
|
|
186
|
+
entityType: "TestEntity",
|
|
187
|
+
[DECLARABLE_MARKER]: true,
|
|
188
|
+
[LOGICAL_NAME_SYMBOL]: null,
|
|
189
|
+
};
|
|
190
|
+
|
|
191
|
+
expect(() => getLogicalName(entity)).toThrow(
|
|
192
|
+
'Logical name not set on entity of type "TestEntity"'
|
|
193
|
+
);
|
|
194
|
+
});
|
|
195
|
+
});
|
package/src/utils.ts
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { Declarable } from "./declarable";
|
|
2
|
+
import { AttrRef } from "./attrref";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Symbol for storing logical names on Declarable entities
|
|
6
|
+
*/
|
|
7
|
+
export const LOGICAL_NAME_SYMBOL = Symbol.for("chant.logicalName");
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Get all property names that have AttrRef values
|
|
11
|
+
* @param entity - The declarable entity to inspect
|
|
12
|
+
* @returns Array of property names with AttrRef values
|
|
13
|
+
*/
|
|
14
|
+
export function getAttributes(entity: Declarable): string[] {
|
|
15
|
+
const attributes: string[] = [];
|
|
16
|
+
const obj = entity as unknown as Record<string, unknown>;
|
|
17
|
+
|
|
18
|
+
for (const key in obj) {
|
|
19
|
+
if (Object.prototype.hasOwnProperty.call(obj, key)) {
|
|
20
|
+
const value = obj[key];
|
|
21
|
+
if (value instanceof AttrRef) {
|
|
22
|
+
attributes.push(key);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return attributes;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Get the logical name stored on a Declarable entity
|
|
32
|
+
* @param entity - The declarable entity
|
|
33
|
+
* @returns The logical name
|
|
34
|
+
* @throws {Error} If logical name is not set
|
|
35
|
+
*/
|
|
36
|
+
export function getLogicalName(entity: Declarable): string {
|
|
37
|
+
const logicalName = (entity as unknown as Record<symbol, unknown>)[LOGICAL_NAME_SYMBOL];
|
|
38
|
+
|
|
39
|
+
if (typeof logicalName !== "string") {
|
|
40
|
+
throw new Error(
|
|
41
|
+
`Logical name not set on entity of type "${entity.entityType}"`
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return logicalName;
|
|
46
|
+
}
|