dialekt 0.1.0 → 0.1.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 +8 -10
- package/TESTING.md +29 -29
- package/dist/cli/main.d.mts +1 -1
- package/dist/cli/main.mjs +549 -362
- package/dist/formatters-De4Q-X1d.mjs +516 -435
- package/dist/index.d.mts +162 -25
- package/dist/index.mjs +119 -34
- package/package.json +3 -3
- package/pnpm-workspace.yaml +3 -3
- package/src/adapter/types.test.ts +57 -57
- package/src/adapter/types.ts +7 -4
- package/src/benchmark/metrics.test.ts +141 -69
- package/src/benchmark/metrics.ts +6 -6
- package/src/benchmark/report.test.ts +38 -38
- package/src/benchmark/report.ts +6 -6
- package/src/benchmark/runner.test.ts +70 -72
- package/src/benchmark/runner.ts +4 -4
- package/src/cli/commands/add.test.ts +90 -109
- package/src/cli/commands/add.ts +40 -28
- package/src/cli/commands/benchmark.test.ts +77 -64
- package/src/cli/commands/benchmark.ts +64 -41
- package/src/cli/commands/languages.test.ts +45 -42
- package/src/cli/commands/languages.ts +16 -12
- package/src/cli/commands/missing.test.ts +143 -92
- package/src/cli/commands/missing.ts +24 -17
- package/src/cli/commands/translate.test.ts +79 -79
- package/src/cli/commands/translate.ts +41 -31
- package/src/cli/commands/unused.test.ts +62 -51
- package/src/cli/commands/unused.ts +18 -14
- package/src/cli/commands/validate.test.ts +130 -72
- package/src/cli/commands/validate.ts +25 -20
- package/src/cli/config-resolution.test.ts +169 -49
- package/src/cli/config-resolution.ts +5 -7
- package/src/cli/format.test.ts +50 -50
- package/src/cli/format.ts +57 -60
- package/src/cli/formatters.test.ts +128 -106
- package/src/cli/formatters.ts +72 -95
- package/src/cli/main.ts +13 -13
- package/src/config/define-config.test.ts +44 -29
- package/src/config/define-config.ts +1 -1
- package/src/config/load-config.test.ts +21 -18
- package/src/config/load-config.ts +5 -5
- package/src/config/types.test.ts +50 -44
- package/src/config/types.ts +2 -2
- package/src/index.ts +22 -26
- package/src/keys/flatten.test.ts +52 -52
- package/src/keys/flatten.ts +7 -9
- package/src/sdk/file-io.test.ts +47 -59
- package/src/sdk/file-io.ts +2 -2
- package/src/sdk/node-layer.test.ts +18 -18
- package/src/sdk/node-layer.ts +2 -2
- package/src/sdk/php-array-reader.test.ts +49 -40
- package/src/sdk/php-array-reader.ts +5 -5
- package/src/translation/chunking.test.ts +52 -44
- package/src/translation/chunking.ts +1 -1
- package/src/translation/missing-keys.test.ts +86 -93
- package/src/translation/missing-keys.ts +4 -6
- package/src/translation/model-registry.test.ts +41 -32
- package/src/translation/model-registry.ts +9 -9
- package/src/translation/one-shot-strategy.test.ts +105 -86
- package/src/translation/one-shot-strategy.ts +10 -12
- package/src/translation/orchestrator.test.ts +90 -101
- package/src/translation/orchestrator.ts +26 -26
- package/src/translation/prompt.test.ts +76 -76
- package/src/translation/prompt.ts +2 -2
- package/src/translation/tool-loop-strategy.test.ts +134 -107
- package/src/translation/tool-loop-strategy.ts +14 -18
- package/src/translation/types.test.ts +22 -22
- package/src/translation/types.ts +3 -3
- package/tsdown.config.ts +3 -3
- package/vitest.config.ts +3 -3
package/src/cli/formatters.ts
CHANGED
|
@@ -16,18 +16,18 @@ import {
|
|
|
16
16
|
warning,
|
|
17
17
|
info,
|
|
18
18
|
keyValue,
|
|
19
|
-
} from
|
|
20
|
-
import type { OutputFormat } from
|
|
19
|
+
} from "./format.js";
|
|
20
|
+
import type { OutputFormat } from "./format.js";
|
|
21
21
|
|
|
22
22
|
const C = {
|
|
23
|
-
reset:
|
|
24
|
-
bold:
|
|
25
|
-
dim:
|
|
26
|
-
red:
|
|
27
|
-
green:
|
|
28
|
-
yellow:
|
|
29
|
-
blue:
|
|
30
|
-
cyan:
|
|
23
|
+
reset: "\x1b[0m",
|
|
24
|
+
bold: "\x1b[1m",
|
|
25
|
+
dim: "\x1b[2m",
|
|
26
|
+
red: "\x1b[31m",
|
|
27
|
+
green: "\x1b[32m",
|
|
28
|
+
yellow: "\x1b[33m",
|
|
29
|
+
blue: "\x1b[34m",
|
|
30
|
+
cyan: "\x1b[36m",
|
|
31
31
|
} as const;
|
|
32
32
|
|
|
33
33
|
// ─── Missing keys formatter ──────────────────────────────────────────────────
|
|
@@ -43,12 +43,12 @@ export function formatMissingKeys(
|
|
|
43
43
|
entries: readonly MissingKeyEntry[],
|
|
44
44
|
format: OutputFormat,
|
|
45
45
|
): string {
|
|
46
|
-
if (format ===
|
|
47
|
-
return JSON.stringify(entries, null, 2) +
|
|
46
|
+
if (format === "json") {
|
|
47
|
+
return JSON.stringify(entries, null, 2) + "\n";
|
|
48
48
|
}
|
|
49
49
|
|
|
50
50
|
if (entries.length === 0) {
|
|
51
|
-
return success(
|
|
51
|
+
return success("All translations are complete. No missing keys.") + "\n";
|
|
52
52
|
}
|
|
53
53
|
|
|
54
54
|
const grouped = new Map<string, Map<string, Map<string, string[]>>>();
|
|
@@ -78,7 +78,9 @@ export function formatMissingKeys(
|
|
|
78
78
|
for (const [locale, byResource] of byLocale) {
|
|
79
79
|
let localeTotal = 0;
|
|
80
80
|
for (const keys of byResource.values()) localeTotal += keys.length;
|
|
81
|
-
lines.push(
|
|
81
|
+
lines.push(
|
|
82
|
+
` ${color(`${g.arrow} ${locale}`, C.yellow)} ${color(`(${localeTotal})`, C.dim)}`,
|
|
83
|
+
);
|
|
82
84
|
|
|
83
85
|
for (const [resource, keys] of byResource) {
|
|
84
86
|
lines.push(` ${color(resource, C.bold)}`);
|
|
@@ -89,7 +91,7 @@ export function formatMissingKeys(
|
|
|
89
91
|
}
|
|
90
92
|
}
|
|
91
93
|
|
|
92
|
-
return lines.join(
|
|
94
|
+
return lines.join("\n") + "\n";
|
|
93
95
|
}
|
|
94
96
|
|
|
95
97
|
// ─── Unused keys formatter ───────────────────────────────────────────────────
|
|
@@ -101,16 +103,13 @@ export interface UnusedKeyEntry {
|
|
|
101
103
|
readonly key: string;
|
|
102
104
|
}
|
|
103
105
|
|
|
104
|
-
export function formatUnusedKeys(
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
): string {
|
|
108
|
-
if (format === 'json') {
|
|
109
|
-
return JSON.stringify(entries, null, 2) + '\n';
|
|
106
|
+
export function formatUnusedKeys(entries: readonly UnusedKeyEntry[], format: OutputFormat): string {
|
|
107
|
+
if (format === "json") {
|
|
108
|
+
return JSON.stringify(entries, null, 2) + "\n";
|
|
110
109
|
}
|
|
111
110
|
|
|
112
111
|
if (entries.length === 0) {
|
|
113
|
-
return success(
|
|
112
|
+
return success("All keys are referenced in source files. No unused keys.") + "\n";
|
|
114
113
|
}
|
|
115
114
|
|
|
116
115
|
const grouped = new Map<string, Map<string, Map<string, string[]>>>();
|
|
@@ -140,7 +139,9 @@ export function formatUnusedKeys(
|
|
|
140
139
|
for (const [locale, byResource] of byLocale) {
|
|
141
140
|
let localeTotal = 0;
|
|
142
141
|
for (const keys of byResource.values()) localeTotal += keys.length;
|
|
143
|
-
lines.push(
|
|
142
|
+
lines.push(
|
|
143
|
+
` ${color(`${g.arrow} ${locale}`, C.yellow)} ${color(`(${localeTotal})`, C.dim)}`,
|
|
144
|
+
);
|
|
144
145
|
|
|
145
146
|
for (const [resource, keys] of byResource) {
|
|
146
147
|
lines.push(` ${color(resource, C.bold)}`);
|
|
@@ -151,7 +152,7 @@ export function formatUnusedKeys(
|
|
|
151
152
|
}
|
|
152
153
|
}
|
|
153
154
|
|
|
154
|
-
return lines.join(
|
|
155
|
+
return lines.join("\n") + "\n";
|
|
155
156
|
}
|
|
156
157
|
|
|
157
158
|
// ─── Validate formatter ──────────────────────────────────────────────────────
|
|
@@ -168,33 +169,27 @@ export interface ValidateResult {
|
|
|
168
169
|
readonly entries: readonly ValidateEntry[];
|
|
169
170
|
}
|
|
170
171
|
|
|
171
|
-
export function formatValidate(
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
): string {
|
|
175
|
-
if (format === 'json') {
|
|
176
|
-
return JSON.stringify(result, null, 2) + '\n';
|
|
172
|
+
export function formatValidate(result: ValidateResult, format: OutputFormat): string {
|
|
173
|
+
if (format === "json") {
|
|
174
|
+
return JSON.stringify(result, null, 2) + "\n";
|
|
177
175
|
}
|
|
178
176
|
|
|
179
177
|
if (result.passing) {
|
|
180
|
-
return
|
|
178
|
+
return "\n" + success("All translations are up to date.") + "\n";
|
|
181
179
|
}
|
|
182
180
|
|
|
183
|
-
const rows = result.entries.map((e) => [
|
|
184
|
-
e.adapter,
|
|
185
|
-
e.locale,
|
|
186
|
-
e.resource,
|
|
187
|
-
e.count.toString(),
|
|
188
|
-
]);
|
|
181
|
+
const rows = result.entries.map((e) => [e.adapter, e.locale, e.resource, e.count.toString()]);
|
|
189
182
|
|
|
190
183
|
const lines: string[] = [];
|
|
191
184
|
lines.push(failure(`Missing keys found in ${result.entries.length} resource(s)`));
|
|
192
|
-
lines.push(
|
|
193
|
-
lines.push(drawTable([
|
|
194
|
-
lines.push(
|
|
195
|
-
lines.push(
|
|
196
|
-
|
|
197
|
-
|
|
185
|
+
lines.push("");
|
|
186
|
+
lines.push(drawTable(["Adapter", "Locale", "Resource", "Missing"], rows));
|
|
187
|
+
lines.push("");
|
|
188
|
+
lines.push(
|
|
189
|
+
color(`Run ${color("dialekt translate", C.bold + C.cyan)} to fill missing keys.`, C.dim),
|
|
190
|
+
);
|
|
191
|
+
|
|
192
|
+
return lines.join("\n") + "\n";
|
|
198
193
|
}
|
|
199
194
|
|
|
200
195
|
// ─── Languages formatter ─────────────────────────────────────────────────────
|
|
@@ -204,16 +199,13 @@ export interface LanguageEntry {
|
|
|
204
199
|
readonly locales: readonly string[];
|
|
205
200
|
}
|
|
206
201
|
|
|
207
|
-
export function formatLanguages(
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
): string {
|
|
211
|
-
if (format === 'json') {
|
|
212
|
-
return JSON.stringify(entries, null, 2) + '\n';
|
|
202
|
+
export function formatLanguages(entries: readonly LanguageEntry[], format: OutputFormat): string {
|
|
203
|
+
if (format === "json") {
|
|
204
|
+
return JSON.stringify(entries, null, 2) + "\n";
|
|
213
205
|
}
|
|
214
206
|
|
|
215
207
|
if (entries.length === 0) {
|
|
216
|
-
return warning(
|
|
208
|
+
return warning("No adapters configured.") + "\n";
|
|
217
209
|
}
|
|
218
210
|
|
|
219
211
|
const lines: string[] = [];
|
|
@@ -221,10 +213,10 @@ export function formatLanguages(
|
|
|
221
213
|
|
|
222
214
|
for (const e of entries) {
|
|
223
215
|
lines.push(` ${color(e.adapter, C.bold + C.blue)}`);
|
|
224
|
-
lines.push(` ${color(`${g.arrow}`, C.dim)} ${e.locales.join(color(
|
|
216
|
+
lines.push(` ${color(`${g.arrow}`, C.dim)} ${e.locales.join(color(", ", C.dim))}`);
|
|
225
217
|
}
|
|
226
218
|
|
|
227
|
-
return lines.join(
|
|
219
|
+
return lines.join("\n") + "\n";
|
|
228
220
|
}
|
|
229
221
|
|
|
230
222
|
// ─── Translate formatter ─────────────────────────────────────────────────────
|
|
@@ -239,26 +231,23 @@ export interface TranslateResult {
|
|
|
239
231
|
};
|
|
240
232
|
}
|
|
241
233
|
|
|
242
|
-
export function formatTranslate(
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
): string {
|
|
246
|
-
if (format === 'json') {
|
|
247
|
-
return JSON.stringify(result, null, 2) + '\n';
|
|
234
|
+
export function formatTranslate(result: TranslateResult, format: OutputFormat): string {
|
|
235
|
+
if (format === "json") {
|
|
236
|
+
return JSON.stringify(result, null, 2) + "\n";
|
|
248
237
|
}
|
|
249
238
|
|
|
250
239
|
if (result.success) {
|
|
251
240
|
const lines: string[] = [success(result.message)];
|
|
252
241
|
if (result.stats) {
|
|
253
|
-
lines.push(
|
|
254
|
-
lines.push(keyValue(
|
|
255
|
-
lines.push(keyValue(
|
|
256
|
-
lines.push(keyValue(
|
|
242
|
+
lines.push("");
|
|
243
|
+
lines.push(keyValue("Adapters:", result.stats.adaptersProcessed.toString()));
|
|
244
|
+
lines.push(keyValue("Locales:", result.stats.localesTranslated.toString()));
|
|
245
|
+
lines.push(keyValue("Keys:", result.stats.keysTranslated.toString()));
|
|
257
246
|
}
|
|
258
|
-
return lines.join(
|
|
247
|
+
return lines.join("\n") + "\n";
|
|
259
248
|
}
|
|
260
249
|
|
|
261
|
-
return failure(result.message) +
|
|
250
|
+
return failure(result.message) + "\n";
|
|
262
251
|
}
|
|
263
252
|
|
|
264
253
|
// ─── Add formatter ───────────────────────────────────────────────────────────
|
|
@@ -269,27 +258,24 @@ export interface AddResult {
|
|
|
269
258
|
readonly addedResources?: readonly string[];
|
|
270
259
|
}
|
|
271
260
|
|
|
272
|
-
export function formatAdd(
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
): string {
|
|
276
|
-
if (format === 'json') {
|
|
277
|
-
return JSON.stringify(result, null, 2) + '\n';
|
|
261
|
+
export function formatAdd(result: AddResult, format: OutputFormat): string {
|
|
262
|
+
if (format === "json") {
|
|
263
|
+
return JSON.stringify(result, null, 2) + "\n";
|
|
278
264
|
}
|
|
279
265
|
|
|
280
266
|
if (result.success) {
|
|
281
267
|
const lines: string[] = [success(result.message)];
|
|
282
268
|
if (result.addedResources && result.addedResources.length > 0) {
|
|
283
|
-
lines.push(
|
|
284
|
-
lines.push(color(
|
|
269
|
+
lines.push("");
|
|
270
|
+
lines.push(color("Added to:", C.dim));
|
|
285
271
|
for (const r of result.addedResources) {
|
|
286
272
|
lines.push(` ${color(glyphs().bullet, C.dim)} ${r}`);
|
|
287
273
|
}
|
|
288
274
|
}
|
|
289
|
-
return lines.join(
|
|
275
|
+
return lines.join("\n") + "\n";
|
|
290
276
|
}
|
|
291
277
|
|
|
292
|
-
return failure(result.message) +
|
|
278
|
+
return failure(result.message) + "\n";
|
|
293
279
|
}
|
|
294
280
|
|
|
295
281
|
// ─── Benchmark formatter ─────────────────────────────────────────────────────
|
|
@@ -304,21 +290,18 @@ export interface BenchmarkEntry {
|
|
|
304
290
|
readonly totalAttempts: number;
|
|
305
291
|
}
|
|
306
292
|
|
|
307
|
-
export function formatBenchmark(
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
): string {
|
|
311
|
-
if (format === 'json') {
|
|
312
|
-
return JSON.stringify(entries, null, 2) + '\n';
|
|
293
|
+
export function formatBenchmark(entries: readonly BenchmarkEntry[], format: OutputFormat): string {
|
|
294
|
+
if (format === "json") {
|
|
295
|
+
return JSON.stringify(entries, null, 2) + "\n";
|
|
313
296
|
}
|
|
314
297
|
|
|
315
298
|
if (entries.length === 0) {
|
|
316
|
-
return warning(
|
|
299
|
+
return warning("No benchmark data available.") + "\n";
|
|
317
300
|
}
|
|
318
301
|
|
|
319
302
|
const lines: string[] = [];
|
|
320
303
|
|
|
321
|
-
lines.push(banner(
|
|
304
|
+
lines.push(banner("Benchmark Results"));
|
|
322
305
|
|
|
323
306
|
const rows = entries.map((e) => [
|
|
324
307
|
e.strategyName,
|
|
@@ -328,23 +311,17 @@ export function formatBenchmark(
|
|
|
328
311
|
e.totalAttempts.toString(),
|
|
329
312
|
]);
|
|
330
313
|
|
|
331
|
-
lines.push(
|
|
332
|
-
lines.push(drawTable(
|
|
333
|
-
['Strategy', 'Chunks', 'Total', 'Avg/Chunk', 'Attempts'],
|
|
334
|
-
rows,
|
|
335
|
-
));
|
|
314
|
+
lines.push("");
|
|
315
|
+
lines.push(drawTable(["Strategy", "Chunks", "Total", "Avg/Chunk", "Attempts"], rows));
|
|
336
316
|
|
|
337
|
-
return lines.join(
|
|
317
|
+
return lines.join("\n") + "\n";
|
|
338
318
|
}
|
|
339
319
|
|
|
340
320
|
// ─── Error formatter ─────────────────────────────────────────────────────────
|
|
341
321
|
|
|
342
|
-
export function formatError(
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
): string {
|
|
346
|
-
if (format === 'json') {
|
|
347
|
-
return JSON.stringify({ error: message }, null, 2) + '\n';
|
|
322
|
+
export function formatError(message: string, format: OutputFormat): string {
|
|
323
|
+
if (format === "json") {
|
|
324
|
+
return JSON.stringify({ error: message }, null, 2) + "\n";
|
|
348
325
|
}
|
|
349
|
-
return failure(message) +
|
|
326
|
+
return failure(message) + "\n";
|
|
350
327
|
}
|
package/src/cli/main.ts
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { Command } from
|
|
3
|
-
import { NodeContext, NodeRuntime } from
|
|
4
|
-
import { Effect } from
|
|
5
|
-
import { translateCommand } from
|
|
6
|
-
import { validateCommand } from
|
|
7
|
-
import { addCommand } from
|
|
8
|
-
import { missingCommand } from
|
|
9
|
-
import { unusedCommand } from
|
|
10
|
-
import { languagesCommand } from
|
|
11
|
-
import { benchmarkCommand } from
|
|
2
|
+
import { Command } from "@effect/cli";
|
|
3
|
+
import { NodeContext, NodeRuntime } from "@effect/platform-node";
|
|
4
|
+
import { Effect } from "effect";
|
|
5
|
+
import { translateCommand } from "./commands/translate.js";
|
|
6
|
+
import { validateCommand } from "./commands/validate.js";
|
|
7
|
+
import { addCommand } from "./commands/add.js";
|
|
8
|
+
import { missingCommand } from "./commands/missing.js";
|
|
9
|
+
import { unusedCommand } from "./commands/unused.js";
|
|
10
|
+
import { languagesCommand } from "./commands/languages.js";
|
|
11
|
+
import { benchmarkCommand } from "./commands/benchmark.js";
|
|
12
12
|
|
|
13
|
-
const rootCommand = Command.make(
|
|
13
|
+
const rootCommand = Command.make("dialekt").pipe(
|
|
14
14
|
Command.withSubcommands([
|
|
15
15
|
translateCommand,
|
|
16
16
|
validateCommand,
|
|
@@ -23,8 +23,8 @@ const rootCommand = Command.make('dialekt').pipe(
|
|
|
23
23
|
);
|
|
24
24
|
|
|
25
25
|
const cli = Command.run(rootCommand, {
|
|
26
|
-
name:
|
|
27
|
-
version:
|
|
26
|
+
name: "dialekt",
|
|
27
|
+
version: "0.1.0",
|
|
28
28
|
});
|
|
29
29
|
|
|
30
30
|
const program = Effect.provide(cli(process.argv), NodeContext.layer);
|
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
import { describe, expect, it } from
|
|
2
|
-
import { defineConfig } from
|
|
3
|
-
import type { DialektConfig } from
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { defineConfig } from "./define-config.js";
|
|
3
|
+
import type { DialektConfig } from "./types.js";
|
|
4
4
|
|
|
5
|
-
describe(
|
|
6
|
-
it(
|
|
5
|
+
describe("defineConfig", () => {
|
|
6
|
+
it("returns its input unchanged", () => {
|
|
7
7
|
const config: DialektConfig = {
|
|
8
|
-
sourceLocale:
|
|
9
|
-
targetLocales: [
|
|
10
|
-
strategy:
|
|
11
|
-
model: { provider:
|
|
12
|
-
fastModel: { provider:
|
|
8
|
+
sourceLocale: "en",
|
|
9
|
+
targetLocales: ["de"],
|
|
10
|
+
strategy: "one-shot",
|
|
11
|
+
model: { provider: "openai", modelId: "gpt-4o" },
|
|
12
|
+
fastModel: { provider: "openai", modelId: "gpt-4o-mini" },
|
|
13
13
|
chunking: { maxTokens: 3000, charsPerToken: 3.0, concurrency: 3 },
|
|
14
14
|
retry: { maxAttempts: 3, baseDelayMs: 1000 },
|
|
15
15
|
adapters: [],
|
|
@@ -17,14 +17,29 @@ describe('defineConfig', () => {
|
|
|
17
17
|
expect(defineConfig(config)).toBe(config);
|
|
18
18
|
});
|
|
19
19
|
|
|
20
|
-
it(
|
|
21
|
-
const adapter = {
|
|
20
|
+
it("accepts a config with adapters", () => {
|
|
21
|
+
const adapter = {
|
|
22
|
+
name: "test",
|
|
23
|
+
capabilities: { canCreateResource: true, unusedKeyDetection: false },
|
|
24
|
+
listLocales: () => {
|
|
25
|
+
throw new Error();
|
|
26
|
+
},
|
|
27
|
+
listResources: () => {
|
|
28
|
+
throw new Error();
|
|
29
|
+
},
|
|
30
|
+
readResource: () => {
|
|
31
|
+
throw new Error();
|
|
32
|
+
},
|
|
33
|
+
writeResource: () => {
|
|
34
|
+
throw new Error();
|
|
35
|
+
},
|
|
36
|
+
} as unknown as DialektConfig["adapters"][number];
|
|
22
37
|
const config: DialektConfig = {
|
|
23
|
-
sourceLocale:
|
|
24
|
-
targetLocales: [
|
|
25
|
-
strategy:
|
|
26
|
-
model: { provider:
|
|
27
|
-
fastModel: { provider:
|
|
38
|
+
sourceLocale: "en",
|
|
39
|
+
targetLocales: ["de"],
|
|
40
|
+
strategy: "one-shot",
|
|
41
|
+
model: { provider: "openai", modelId: "gpt-4o" },
|
|
42
|
+
fastModel: { provider: "openai", modelId: "gpt-4o-mini" },
|
|
28
43
|
chunking: { maxTokens: 3000, charsPerToken: 3.0, concurrency: 3 },
|
|
29
44
|
retry: { maxAttempts: 3, baseDelayMs: 1000 },
|
|
30
45
|
adapters: [adapter],
|
|
@@ -32,13 +47,13 @@ describe('defineConfig', () => {
|
|
|
32
47
|
expect(defineConfig(config)).toBe(config);
|
|
33
48
|
});
|
|
34
49
|
|
|
35
|
-
it(
|
|
50
|
+
it("accepts a config with tool-loop-agent strategy", () => {
|
|
36
51
|
const config: DialektConfig = {
|
|
37
|
-
sourceLocale:
|
|
38
|
-
targetLocales: [
|
|
39
|
-
strategy:
|
|
40
|
-
model: { provider:
|
|
41
|
-
fastModel: { provider:
|
|
52
|
+
sourceLocale: "en",
|
|
53
|
+
targetLocales: ["de"],
|
|
54
|
+
strategy: "tool-loop-agent",
|
|
55
|
+
model: { provider: "anthropic", modelId: "claude-3-sonnet" },
|
|
56
|
+
fastModel: { provider: "anthropic", modelId: "claude-3-haiku" },
|
|
42
57
|
chunking: { maxTokens: 4000, charsPerToken: 3.5, concurrency: 5 },
|
|
43
58
|
retry: { maxAttempts: 5, baseDelayMs: 500 },
|
|
44
59
|
adapters: [],
|
|
@@ -46,15 +61,15 @@ describe('defineConfig', () => {
|
|
|
46
61
|
expect(defineConfig(config)).toBe(config);
|
|
47
62
|
});
|
|
48
63
|
|
|
49
|
-
it(
|
|
50
|
-
const model = { provider:
|
|
64
|
+
it("preserves reference identity for nested objects", () => {
|
|
65
|
+
const model = { provider: "openai", modelId: "gpt-4o" };
|
|
51
66
|
const chunking = { maxTokens: 3000, charsPerToken: 3.0, concurrency: 3 };
|
|
52
67
|
const config: DialektConfig = {
|
|
53
|
-
sourceLocale:
|
|
54
|
-
targetLocales: [
|
|
55
|
-
strategy:
|
|
68
|
+
sourceLocale: "en",
|
|
69
|
+
targetLocales: ["de"],
|
|
70
|
+
strategy: "one-shot",
|
|
56
71
|
model,
|
|
57
|
-
fastModel: { provider:
|
|
72
|
+
fastModel: { provider: "openai", modelId: "gpt-4o-mini" },
|
|
58
73
|
chunking,
|
|
59
74
|
retry: { maxAttempts: 3, baseDelayMs: 1000 },
|
|
60
75
|
adapters: [],
|
|
@@ -1,35 +1,38 @@
|
|
|
1
|
-
import { describe, expect, it } from
|
|
2
|
-
import { Effect, Either } from
|
|
3
|
-
import { loadConfig, ConfigLoadError } from
|
|
4
|
-
import { writeFileSync, mkdirSync, rmSync } from
|
|
5
|
-
import { join } from
|
|
6
|
-
import { tmpdir } from
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { Effect, Either } from "effect";
|
|
3
|
+
import { loadConfig, ConfigLoadError } from "./load-config.js";
|
|
4
|
+
import { writeFileSync, mkdirSync, rmSync } from "node:fs";
|
|
5
|
+
import { join } from "node:path";
|
|
6
|
+
import { tmpdir } from "node:os";
|
|
7
7
|
|
|
8
|
-
describe(
|
|
9
|
-
it(
|
|
8
|
+
describe("loadConfig", () => {
|
|
9
|
+
it("loads a valid dialekt.config.ts", async () => {
|
|
10
10
|
const dir = join(tmpdir(), `dialekt-load-config-test-${Date.now()}`);
|
|
11
11
|
mkdirSync(dir, { recursive: true });
|
|
12
|
-
const configPath = join(dir,
|
|
12
|
+
const configPath = join(dir, "dialekt.config.ts");
|
|
13
13
|
writeFileSync(
|
|
14
14
|
configPath,
|
|
15
15
|
`export default { sourceLocale: 'en', targetLocales: ['de'], strategy: 'one-shot', model: { provider: 'openai', modelId: 'gpt-4o' }, fastModel: { provider: 'openai', modelId: 'gpt-4o-mini' }, chunking: { maxTokens: 3000, charsPerToken: 3.0, concurrency: 3 }, retry: { maxAttempts: 3, baseDelayMs: 1000 }, adapters: [] };`,
|
|
16
16
|
);
|
|
17
17
|
|
|
18
18
|
const result = await Effect.runPromise(loadConfig(configPath));
|
|
19
|
-
expect(result.sourceLocale).toBe(
|
|
20
|
-
expect(result.targetLocales).toEqual([
|
|
19
|
+
expect(result.sourceLocale).toBe("en");
|
|
20
|
+
expect(result.targetLocales).toEqual(["de"]);
|
|
21
21
|
|
|
22
22
|
rmSync(dir, { recursive: true, force: true });
|
|
23
23
|
});
|
|
24
24
|
|
|
25
|
-
it(
|
|
26
|
-
const program = loadConfig(
|
|
27
|
-
const exit = await Effect.runPromise(Effect.either(program)) as Either.Either<
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
25
|
+
it("returns ConfigLoadError for a nonexistent path", async () => {
|
|
26
|
+
const program = loadConfig("/nonexistent/path/config.ts");
|
|
27
|
+
const exit = (await Effect.runPromise(Effect.either(program))) as Either.Either<
|
|
28
|
+
unknown,
|
|
29
|
+
ConfigLoadError
|
|
30
|
+
>;
|
|
31
|
+
if (exit._tag === "Left") {
|
|
32
|
+
expect(exit.left._tag).toBe("ConfigLoadError");
|
|
33
|
+
expect(exit.left.path).toBe("/nonexistent/path/config.ts");
|
|
31
34
|
} else {
|
|
32
|
-
throw new Error(
|
|
35
|
+
throw new Error("Expected Left");
|
|
33
36
|
}
|
|
34
37
|
});
|
|
35
38
|
});
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { createJiti } from
|
|
2
|
-
import { Effect, Data } from
|
|
3
|
-
import { resolve } from
|
|
4
|
-
import type { DialektConfig } from
|
|
1
|
+
import { createJiti } from "jiti";
|
|
2
|
+
import { Effect, Data } from "effect";
|
|
3
|
+
import { resolve } from "node:path";
|
|
4
|
+
import type { DialektConfig } from "./types.js";
|
|
5
5
|
|
|
6
|
-
export class ConfigLoadError extends Data.TaggedError(
|
|
6
|
+
export class ConfigLoadError extends Data.TaggedError("ConfigLoadError")<{
|
|
7
7
|
readonly path: string;
|
|
8
8
|
readonly cause: unknown;
|
|
9
9
|
}> {}
|