@intentius/chant 0.0.11 → 0.0.13
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/package.json +1 -1
- package/src/cli/commands/__fixtures__/init-lexicon-output/docs/astro.config.mjs +3 -0
- package/src/cli/commands/__fixtures__/init-lexicon-output/docs/src/content/docs/getting-started.mdx +6 -0
- package/src/cli/commands/__fixtures__/init-lexicon-output/docs/src/content/docs/lint-rules.mdx +6 -0
- package/src/cli/commands/__fixtures__/init-lexicon-output/docs/src/content/docs/serialization.mdx +6 -0
- package/src/cli/commands/__fixtures__/init-lexicon-output/examples/getting-started/package.json +9 -0
- package/src/cli/commands/__fixtures__/init-lexicon-output/examples/getting-started/src/infra.ts +12 -0
- package/src/cli/commands/__fixtures__/init-lexicon-output/src/composites/.gitkeep +0 -0
- package/src/cli/commands/__fixtures__/init-lexicon-output/src/lint/post-synth/.gitkeep +0 -0
- package/src/cli/commands/__fixtures__/init-lexicon-output/src/plugin.ts +37 -42
- package/src/cli/commands/__snapshots__/init-lexicon.test.ts.snap +37 -42
- package/src/cli/commands/build.ts +12 -7
- package/src/cli/commands/check-lexicon.ts +385 -0
- package/src/cli/commands/import.ts +6 -3
- package/src/cli/commands/init-lexicon.test.ts +22 -1
- package/src/cli/commands/init-lexicon.ts +194 -43
- package/src/cli/commands/init.ts +3 -3
- package/src/cli/commands/onboard.test.ts +295 -0
- package/src/cli/commands/onboard.ts +313 -0
- package/src/cli/handlers/dev.ts +26 -1
- package/src/cli/main.ts +5 -1
- package/src/codegen/docs.ts +11 -5
- package/src/codegen/generate-registry.ts +3 -2
- package/src/codegen/typecheck.ts +44 -2
- package/src/detectLexicon.test.ts +24 -0
- package/src/detectLexicon.ts +4 -2
- package/src/lsp/lexicon-providers.ts +1 -0
- package/src/runtime.ts +4 -0
- package/src/serializer-walker.test.ts +8 -0
- package/src/toml.test.ts +388 -0
- package/src/toml.ts +606 -0
- package/src/yaml.test.ts +192 -0
- package/src/yaml.ts +308 -0
- /package/src/cli/commands/__fixtures__/init-lexicon-output/{examples/getting-started → src/actions}/.gitkeep +0 -0
package/src/toml.test.ts
ADDED
|
@@ -0,0 +1,388 @@
|
|
|
1
|
+
import { describe, test, expect } from "bun:test";
|
|
2
|
+
import { emitTOML, parseTOML } from "./toml";
|
|
3
|
+
|
|
4
|
+
describe("emitTOML", () => {
|
|
5
|
+
test("emits scalar values", () => {
|
|
6
|
+
const result = emitTOML({
|
|
7
|
+
name: "my-project",
|
|
8
|
+
version: 42,
|
|
9
|
+
enabled: true,
|
|
10
|
+
ratio: 3.14,
|
|
11
|
+
});
|
|
12
|
+
expect(result).toBe(
|
|
13
|
+
`name = "my-project"\nversion = 42\nenabled = true\nratio = 3.14\n`,
|
|
14
|
+
);
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
test("emits arrays of scalars inline", () => {
|
|
18
|
+
const result = emitTOML({
|
|
19
|
+
schemas: ["public", "audit"],
|
|
20
|
+
ports: [8080, 9090],
|
|
21
|
+
});
|
|
22
|
+
expect(result).toContain('schemas = ["public", "audit"]');
|
|
23
|
+
expect(result).toContain("ports = [8080, 9090]");
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
test("emits nested tables as sections", () => {
|
|
27
|
+
const result = emitTOML({
|
|
28
|
+
name: "proj",
|
|
29
|
+
flyway: {
|
|
30
|
+
locations: ["filesystem:sql"],
|
|
31
|
+
encoding: "UTF-8",
|
|
32
|
+
},
|
|
33
|
+
});
|
|
34
|
+
expect(result).toContain('name = "proj"');
|
|
35
|
+
expect(result).toContain("[flyway]");
|
|
36
|
+
expect(result).toContain('locations = ["filesystem:sql"]');
|
|
37
|
+
expect(result).toContain('encoding = "UTF-8"');
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
test("emits deeply nested tables with dotted paths", () => {
|
|
41
|
+
const result = emitTOML({
|
|
42
|
+
environments: {
|
|
43
|
+
dev: {
|
|
44
|
+
url: "jdbc:postgresql://localhost:5432/devdb",
|
|
45
|
+
user: "dev_user",
|
|
46
|
+
},
|
|
47
|
+
},
|
|
48
|
+
});
|
|
49
|
+
expect(result).toContain("[environments.dev]");
|
|
50
|
+
expect(result).toContain('url = "jdbc:postgresql://localhost:5432/devdb"');
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
test("emits array of tables", () => {
|
|
54
|
+
const result = emitTOML({
|
|
55
|
+
servers: [
|
|
56
|
+
{ host: "alpha", port: 5432 },
|
|
57
|
+
{ host: "beta", port: 5433 },
|
|
58
|
+
],
|
|
59
|
+
});
|
|
60
|
+
expect(result).toContain("[[servers]]");
|
|
61
|
+
expect(result).toContain('host = "alpha"');
|
|
62
|
+
expect(result).toContain("port = 5432");
|
|
63
|
+
expect(result).toContain('host = "beta"');
|
|
64
|
+
expect(result).toContain("port = 5433");
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
test("emits header comment", () => {
|
|
68
|
+
const result = emitTOML({ name: "test" }, { header: "Generated by Chant" });
|
|
69
|
+
expect(result).toStartWith("# Generated by Chant\n");
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
test("respects key ordering", () => {
|
|
73
|
+
const result = emitTOML(
|
|
74
|
+
{ z: 1, a: 2, m: 3 },
|
|
75
|
+
{ keyOrder: ["a", "m", "z"] },
|
|
76
|
+
);
|
|
77
|
+
const lines = result.split("\n").filter((l) => l.includes("="));
|
|
78
|
+
expect(lines[0]).toContain("a = 2");
|
|
79
|
+
expect(lines[1]).toContain("m = 3");
|
|
80
|
+
expect(lines[2]).toContain("z = 1");
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
test("escapes special characters in strings", () => {
|
|
84
|
+
const result = emitTOML({
|
|
85
|
+
path: 'C:\\Users\\admin',
|
|
86
|
+
query: 'SELECT "name" FROM users',
|
|
87
|
+
});
|
|
88
|
+
expect(result).toContain('path = "C:\\\\Users\\\\admin"');
|
|
89
|
+
expect(result).toContain('query = "SELECT \\"name\\" FROM users"');
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
test("escapes special characters in keys", () => {
|
|
93
|
+
const result = emitTOML({
|
|
94
|
+
"simple-key": 1,
|
|
95
|
+
"key with spaces": 2,
|
|
96
|
+
});
|
|
97
|
+
expect(result).toContain("simple-key = 1");
|
|
98
|
+
expect(result).toContain('"key with spaces" = 2');
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
test("emits empty arrays", () => {
|
|
102
|
+
const result = emitTOML({ items: [] });
|
|
103
|
+
expect(result).toContain("items = []");
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
test("emits boolean values correctly", () => {
|
|
107
|
+
const result = emitTOML({
|
|
108
|
+
cleanDisabled: true,
|
|
109
|
+
outOfOrder: false,
|
|
110
|
+
});
|
|
111
|
+
expect(result).toContain("cleanDisabled = true");
|
|
112
|
+
expect(result).toContain("outOfOrder = false");
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
test("handles null/undefined values", () => {
|
|
116
|
+
const result = emitTOML({
|
|
117
|
+
present: "yes",
|
|
118
|
+
absent: null,
|
|
119
|
+
});
|
|
120
|
+
expect(result).toContain('present = "yes"');
|
|
121
|
+
expect(result).toContain('absent = ""');
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
test("skips undefined values", () => {
|
|
125
|
+
const result = emitTOML({
|
|
126
|
+
present: "yes",
|
|
127
|
+
absent: undefined,
|
|
128
|
+
});
|
|
129
|
+
expect(result).toContain('present = "yes"');
|
|
130
|
+
expect(result).not.toContain("absent");
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
test("emits Flyway-realistic config", () => {
|
|
134
|
+
const result = emitTOML({
|
|
135
|
+
flyway: {
|
|
136
|
+
locations: ["filesystem:sql/migrations"],
|
|
137
|
+
defaultSchema: "public",
|
|
138
|
+
schemas: ["public", "audit"],
|
|
139
|
+
encoding: "UTF-8",
|
|
140
|
+
validateMigrationNaming: true,
|
|
141
|
+
validateOnMigrate: true,
|
|
142
|
+
cleanDisabled: true,
|
|
143
|
+
baselineOnMigrate: false,
|
|
144
|
+
baselineVersion: "1",
|
|
145
|
+
sqlMigrationPrefix: "V",
|
|
146
|
+
sqlMigrationSeparator: "__",
|
|
147
|
+
sqlMigrationSuffixes: [".sql"],
|
|
148
|
+
table: "flyway_schema_history",
|
|
149
|
+
placeholders: {
|
|
150
|
+
defaultSchema: "public",
|
|
151
|
+
appName: "myapp",
|
|
152
|
+
},
|
|
153
|
+
},
|
|
154
|
+
environments: {
|
|
155
|
+
dev: {
|
|
156
|
+
url: "jdbc:postgresql://localhost:5432/devdb",
|
|
157
|
+
user: "dev_user",
|
|
158
|
+
schemas: ["public"],
|
|
159
|
+
},
|
|
160
|
+
prod: {
|
|
161
|
+
url: "jdbc:postgresql://prod-host:5432/proddb",
|
|
162
|
+
user: "prod_user",
|
|
163
|
+
schemas: ["public", "audit"],
|
|
164
|
+
},
|
|
165
|
+
},
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
expect(result).toContain("[flyway]");
|
|
169
|
+
expect(result).toContain('[flyway.placeholders]');
|
|
170
|
+
expect(result).toContain("[environments.dev]");
|
|
171
|
+
expect(result).toContain("[environments.prod]");
|
|
172
|
+
expect(result).toContain('defaultSchema = "public"');
|
|
173
|
+
expect(result).toContain("validateMigrationNaming = true");
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
test("throws for non-object input", () => {
|
|
177
|
+
expect(() => emitTOML("string" as unknown)).toThrow();
|
|
178
|
+
expect(() => emitTOML([1, 2, 3] as unknown)).toThrow();
|
|
179
|
+
expect(() => emitTOML(42 as unknown)).toThrow();
|
|
180
|
+
});
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
describe("parseTOML", () => {
|
|
184
|
+
test("parses scalar values", () => {
|
|
185
|
+
const result = parseTOML(`
|
|
186
|
+
name = "my-project"
|
|
187
|
+
version = 42
|
|
188
|
+
enabled = true
|
|
189
|
+
ratio = 3.14
|
|
190
|
+
`);
|
|
191
|
+
expect(result.name).toBe("my-project");
|
|
192
|
+
expect(result.version).toBe(42);
|
|
193
|
+
expect(result.enabled).toBe(true);
|
|
194
|
+
expect(result.ratio).toBe(3.14);
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
test("parses arrays", () => {
|
|
198
|
+
const result = parseTOML(`
|
|
199
|
+
schemas = ["public", "audit"]
|
|
200
|
+
ports = [8080, 9090]
|
|
201
|
+
`);
|
|
202
|
+
expect(result.schemas).toEqual(["public", "audit"]);
|
|
203
|
+
expect(result.ports).toEqual([8080, 9090]);
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
test("parses table sections", () => {
|
|
207
|
+
const result = parseTOML(`
|
|
208
|
+
[flyway]
|
|
209
|
+
encoding = "UTF-8"
|
|
210
|
+
cleanDisabled = true
|
|
211
|
+
`);
|
|
212
|
+
const flyway = result.flyway as Record<string, unknown>;
|
|
213
|
+
expect(flyway.encoding).toBe("UTF-8");
|
|
214
|
+
expect(flyway.cleanDisabled).toBe(true);
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
test("parses dotted table paths", () => {
|
|
218
|
+
const result = parseTOML(`
|
|
219
|
+
[environments.dev]
|
|
220
|
+
url = "jdbc:postgresql://localhost:5432/devdb"
|
|
221
|
+
user = "dev_user"
|
|
222
|
+
`);
|
|
223
|
+
const envs = result.environments as Record<string, unknown>;
|
|
224
|
+
const dev = envs.dev as Record<string, unknown>;
|
|
225
|
+
expect(dev.url).toBe("jdbc:postgresql://localhost:5432/devdb");
|
|
226
|
+
expect(dev.user).toBe("dev_user");
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
test("parses array of tables", () => {
|
|
230
|
+
const result = parseTOML(`
|
|
231
|
+
[[servers]]
|
|
232
|
+
host = "alpha"
|
|
233
|
+
port = 5432
|
|
234
|
+
|
|
235
|
+
[[servers]]
|
|
236
|
+
host = "beta"
|
|
237
|
+
port = 5433
|
|
238
|
+
`);
|
|
239
|
+
const servers = result.servers as Record<string, unknown>[];
|
|
240
|
+
expect(servers).toHaveLength(2);
|
|
241
|
+
expect(servers[0].host).toBe("alpha");
|
|
242
|
+
expect(servers[0].port).toBe(5432);
|
|
243
|
+
expect(servers[1].host).toBe("beta");
|
|
244
|
+
expect(servers[1].port).toBe(5433);
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
test("ignores comments", () => {
|
|
248
|
+
const result = parseTOML(`
|
|
249
|
+
# This is a comment
|
|
250
|
+
name = "test" # inline comment
|
|
251
|
+
# Another comment
|
|
252
|
+
version = 1
|
|
253
|
+
`);
|
|
254
|
+
expect(result.name).toBe("test");
|
|
255
|
+
expect(result.version).toBe(1);
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
test("parses boolean values", () => {
|
|
259
|
+
const result = parseTOML(`
|
|
260
|
+
enabled = true
|
|
261
|
+
disabled = false
|
|
262
|
+
`);
|
|
263
|
+
expect(result.enabled).toBe(true);
|
|
264
|
+
expect(result.disabled).toBe(false);
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
test("parses inline tables", () => {
|
|
268
|
+
const result = parseTOML(`
|
|
269
|
+
point = { x = 1, y = 2 }
|
|
270
|
+
`);
|
|
271
|
+
const point = result.point as Record<string, unknown>;
|
|
272
|
+
expect(point.x).toBe(1);
|
|
273
|
+
expect(point.y).toBe(2);
|
|
274
|
+
});
|
|
275
|
+
|
|
276
|
+
test("parses literal strings (single quotes)", () => {
|
|
277
|
+
const result = parseTOML(`
|
|
278
|
+
path = 'C:\\Users\\admin'
|
|
279
|
+
`);
|
|
280
|
+
expect(result.path).toBe("C:\\Users\\admin");
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
test("parses escaped strings", () => {
|
|
284
|
+
const result = parseTOML(`
|
|
285
|
+
escaped = "line1\\nline2"
|
|
286
|
+
`);
|
|
287
|
+
expect(result.escaped).toBe("line1\nline2");
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
test("parses empty values", () => {
|
|
291
|
+
const result = parseTOML(`
|
|
292
|
+
empty_string = ""
|
|
293
|
+
empty_array = []
|
|
294
|
+
`);
|
|
295
|
+
expect(result.empty_string).toBe("");
|
|
296
|
+
expect(result.empty_array).toEqual([]);
|
|
297
|
+
});
|
|
298
|
+
|
|
299
|
+
test("parses Flyway-realistic config", () => {
|
|
300
|
+
const result = parseTOML(`
|
|
301
|
+
[flyway]
|
|
302
|
+
locations = ["filesystem:sql/migrations"]
|
|
303
|
+
defaultSchema = "public"
|
|
304
|
+
schemas = ["public", "audit"]
|
|
305
|
+
encoding = "UTF-8"
|
|
306
|
+
validateMigrationNaming = true
|
|
307
|
+
cleanDisabled = true
|
|
308
|
+
baselineVersion = "1"
|
|
309
|
+
table = "flyway_schema_history"
|
|
310
|
+
|
|
311
|
+
[flyway.placeholders]
|
|
312
|
+
defaultSchema = "public"
|
|
313
|
+
appName = "myapp"
|
|
314
|
+
|
|
315
|
+
[environments.dev]
|
|
316
|
+
url = "jdbc:postgresql://localhost:5432/devdb"
|
|
317
|
+
user = "dev_user"
|
|
318
|
+
schemas = ["public"]
|
|
319
|
+
|
|
320
|
+
[environments.prod]
|
|
321
|
+
url = "jdbc:postgresql://prod-host:5432/proddb"
|
|
322
|
+
user = "prod_user"
|
|
323
|
+
schemas = ["public", "audit"]
|
|
324
|
+
`);
|
|
325
|
+
|
|
326
|
+
const flyway = result.flyway as Record<string, unknown>;
|
|
327
|
+
expect(flyway.defaultSchema).toBe("public");
|
|
328
|
+
expect(flyway.validateMigrationNaming).toBe(true);
|
|
329
|
+
expect((flyway.placeholders as Record<string, unknown>).appName).toBe("myapp");
|
|
330
|
+
|
|
331
|
+
const envs = result.environments as Record<string, unknown>;
|
|
332
|
+
const dev = envs.dev as Record<string, unknown>;
|
|
333
|
+
expect(dev.url).toBe("jdbc:postgresql://localhost:5432/devdb");
|
|
334
|
+
|
|
335
|
+
const prod = envs.prod as Record<string, unknown>;
|
|
336
|
+
expect(prod.schemas).toEqual(["public", "audit"]);
|
|
337
|
+
});
|
|
338
|
+
});
|
|
339
|
+
|
|
340
|
+
describe("roundtrip", () => {
|
|
341
|
+
test("emit → parse → emit preserves structure", () => {
|
|
342
|
+
const original = {
|
|
343
|
+
name: "roundtrip-test",
|
|
344
|
+
flyway: {
|
|
345
|
+
locations: ["filesystem:sql"],
|
|
346
|
+
encoding: "UTF-8",
|
|
347
|
+
cleanDisabled: true,
|
|
348
|
+
baselineVersion: "1",
|
|
349
|
+
placeholders: {
|
|
350
|
+
schema: "public",
|
|
351
|
+
},
|
|
352
|
+
},
|
|
353
|
+
environments: {
|
|
354
|
+
dev: {
|
|
355
|
+
url: "jdbc:postgresql://localhost:5432/db",
|
|
356
|
+
user: "admin",
|
|
357
|
+
schemas: ["public"],
|
|
358
|
+
},
|
|
359
|
+
},
|
|
360
|
+
};
|
|
361
|
+
|
|
362
|
+
const toml = emitTOML(original);
|
|
363
|
+
const parsed = parseTOML(toml);
|
|
364
|
+
const toml2 = emitTOML(parsed);
|
|
365
|
+
|
|
366
|
+
expect(toml2).toBe(toml);
|
|
367
|
+
});
|
|
368
|
+
|
|
369
|
+
test("roundtrip with booleans and numbers", () => {
|
|
370
|
+
const original = {
|
|
371
|
+
settings: {
|
|
372
|
+
enabled: true,
|
|
373
|
+
disabled: false,
|
|
374
|
+
count: 42,
|
|
375
|
+
ratio: 3.14,
|
|
376
|
+
},
|
|
377
|
+
};
|
|
378
|
+
|
|
379
|
+
const toml = emitTOML(original);
|
|
380
|
+
const parsed = parseTOML(toml);
|
|
381
|
+
|
|
382
|
+
const settings = parsed.settings as Record<string, unknown>;
|
|
383
|
+
expect(settings.enabled).toBe(true);
|
|
384
|
+
expect(settings.disabled).toBe(false);
|
|
385
|
+
expect(settings.count).toBe(42);
|
|
386
|
+
expect(settings.ratio).toBe(3.14);
|
|
387
|
+
});
|
|
388
|
+
});
|