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.
Files changed (71) hide show
  1. package/README.md +8 -10
  2. package/TESTING.md +29 -29
  3. package/dist/cli/main.d.mts +1 -1
  4. package/dist/cli/main.mjs +549 -362
  5. package/dist/formatters-De4Q-X1d.mjs +516 -435
  6. package/dist/index.d.mts +162 -25
  7. package/dist/index.mjs +119 -34
  8. package/package.json +3 -3
  9. package/pnpm-workspace.yaml +3 -3
  10. package/src/adapter/types.test.ts +57 -57
  11. package/src/adapter/types.ts +7 -4
  12. package/src/benchmark/metrics.test.ts +141 -69
  13. package/src/benchmark/metrics.ts +6 -6
  14. package/src/benchmark/report.test.ts +38 -38
  15. package/src/benchmark/report.ts +6 -6
  16. package/src/benchmark/runner.test.ts +70 -72
  17. package/src/benchmark/runner.ts +4 -4
  18. package/src/cli/commands/add.test.ts +90 -109
  19. package/src/cli/commands/add.ts +40 -28
  20. package/src/cli/commands/benchmark.test.ts +77 -64
  21. package/src/cli/commands/benchmark.ts +64 -41
  22. package/src/cli/commands/languages.test.ts +45 -42
  23. package/src/cli/commands/languages.ts +16 -12
  24. package/src/cli/commands/missing.test.ts +143 -92
  25. package/src/cli/commands/missing.ts +24 -17
  26. package/src/cli/commands/translate.test.ts +79 -79
  27. package/src/cli/commands/translate.ts +41 -31
  28. package/src/cli/commands/unused.test.ts +62 -51
  29. package/src/cli/commands/unused.ts +18 -14
  30. package/src/cli/commands/validate.test.ts +130 -72
  31. package/src/cli/commands/validate.ts +25 -20
  32. package/src/cli/config-resolution.test.ts +169 -49
  33. package/src/cli/config-resolution.ts +5 -7
  34. package/src/cli/format.test.ts +50 -50
  35. package/src/cli/format.ts +57 -60
  36. package/src/cli/formatters.test.ts +128 -106
  37. package/src/cli/formatters.ts +72 -95
  38. package/src/cli/main.ts +13 -13
  39. package/src/config/define-config.test.ts +44 -29
  40. package/src/config/define-config.ts +1 -1
  41. package/src/config/load-config.test.ts +21 -18
  42. package/src/config/load-config.ts +5 -5
  43. package/src/config/types.test.ts +50 -44
  44. package/src/config/types.ts +2 -2
  45. package/src/index.ts +22 -26
  46. package/src/keys/flatten.test.ts +52 -52
  47. package/src/keys/flatten.ts +7 -9
  48. package/src/sdk/file-io.test.ts +47 -59
  49. package/src/sdk/file-io.ts +2 -2
  50. package/src/sdk/node-layer.test.ts +18 -18
  51. package/src/sdk/node-layer.ts +2 -2
  52. package/src/sdk/php-array-reader.test.ts +49 -40
  53. package/src/sdk/php-array-reader.ts +5 -5
  54. package/src/translation/chunking.test.ts +52 -44
  55. package/src/translation/chunking.ts +1 -1
  56. package/src/translation/missing-keys.test.ts +86 -93
  57. package/src/translation/missing-keys.ts +4 -6
  58. package/src/translation/model-registry.test.ts +41 -32
  59. package/src/translation/model-registry.ts +9 -9
  60. package/src/translation/one-shot-strategy.test.ts +105 -86
  61. package/src/translation/one-shot-strategy.ts +10 -12
  62. package/src/translation/orchestrator.test.ts +90 -101
  63. package/src/translation/orchestrator.ts +26 -26
  64. package/src/translation/prompt.test.ts +76 -76
  65. package/src/translation/prompt.ts +2 -2
  66. package/src/translation/tool-loop-strategy.test.ts +134 -107
  67. package/src/translation/tool-loop-strategy.ts +14 -18
  68. package/src/translation/types.test.ts +22 -22
  69. package/src/translation/types.ts +3 -3
  70. package/tsdown.config.ts +3 -3
  71. package/vitest.config.ts +3 -3
@@ -1,12 +1,12 @@
1
- import { describe, expect, it } from 'vitest';
2
- import { formatBenchmarkReport } from './report.js';
3
- import type { StrategyBenchmarkSummary } from './metrics.js';
1
+ import { describe, expect, it } from "vitest";
2
+ import { formatBenchmarkReport } from "./report.js";
3
+ import type { StrategyBenchmarkSummary } from "./metrics.js";
4
4
 
5
- describe('formatBenchmarkReport', () => {
6
- it('produces valid JSON round-trip', () => {
5
+ describe("formatBenchmarkReport", () => {
6
+ it("produces valid JSON round-trip", () => {
7
7
  const summaries: StrategyBenchmarkSummary[] = [
8
8
  {
9
- strategyName: 'one-shot',
9
+ strategyName: "one-shot",
10
10
  totalChunks: 3,
11
11
  succeededChunks: 3,
12
12
  failedChunks: 0,
@@ -15,14 +15,14 @@ describe('formatBenchmarkReport', () => {
15
15
  totalAttempts: 3,
16
16
  },
17
17
  ];
18
- const json = formatBenchmarkReport(summaries, 'json');
18
+ const json = formatBenchmarkReport(summaries, "json");
19
19
  expect(JSON.parse(json)).toEqual(summaries);
20
20
  });
21
21
 
22
- it('produces human-readable table with key metrics', () => {
22
+ it("produces human-readable table with key metrics", () => {
23
23
  const summaries: StrategyBenchmarkSummary[] = [
24
24
  {
25
- strategyName: 'one-shot',
25
+ strategyName: "one-shot",
26
26
  totalChunks: 3,
27
27
  succeededChunks: 3,
28
28
  failedChunks: 0,
@@ -31,17 +31,17 @@ describe('formatBenchmarkReport', () => {
31
31
  totalAttempts: 3,
32
32
  },
33
33
  ];
34
- const table = formatBenchmarkReport(summaries, 'table');
35
- expect(table).toContain('one-shot');
36
- expect(table).toContain('300');
37
- expect(table).toContain('100.0');
38
- expect(table).toContain('3');
34
+ const table = formatBenchmarkReport(summaries, "table");
35
+ expect(table).toContain("one-shot");
36
+ expect(table).toContain("300");
37
+ expect(table).toContain("100.0");
38
+ expect(table).toContain("3");
39
39
  });
40
40
 
41
- it('handles multiple strategies in table format', () => {
41
+ it("handles multiple strategies in table format", () => {
42
42
  const summaries: StrategyBenchmarkSummary[] = [
43
43
  {
44
- strategyName: 'one-shot',
44
+ strategyName: "one-shot",
45
45
  totalChunks: 5,
46
46
  succeededChunks: 5,
47
47
  failedChunks: 0,
@@ -50,7 +50,7 @@ describe('formatBenchmarkReport', () => {
50
50
  totalAttempts: 5,
51
51
  },
52
52
  {
53
- strategyName: 'tool-loop-agent',
53
+ strategyName: "tool-loop-agent",
54
54
  totalChunks: 5,
55
55
  succeededChunks: 4,
56
56
  failedChunks: 1,
@@ -59,26 +59,26 @@ describe('formatBenchmarkReport', () => {
59
59
  totalAttempts: 6,
60
60
  },
61
61
  ];
62
- const table = formatBenchmarkReport(summaries, 'table');
63
- expect(table).toContain('one-shot');
64
- expect(table).toContain('tool-loop-agent');
65
- expect(table).toContain('4 ok, 1 failed');
62
+ const table = formatBenchmarkReport(summaries, "table");
63
+ expect(table).toContain("one-shot");
64
+ expect(table).toContain("tool-loop-agent");
65
+ expect(table).toContain("4 ok, 1 failed");
66
66
  });
67
67
 
68
- it('handles empty summaries in JSON format', () => {
69
- const json = formatBenchmarkReport([], 'json');
68
+ it("handles empty summaries in JSON format", () => {
69
+ const json = formatBenchmarkReport([], "json");
70
70
  expect(JSON.parse(json)).toEqual([]);
71
71
  });
72
72
 
73
- it('handles empty summaries in table format', () => {
74
- const table = formatBenchmarkReport([], 'table');
75
- expect(table).toContain('Benchmark Results');
73
+ it("handles empty summaries in table format", () => {
74
+ const table = formatBenchmarkReport([], "table");
75
+ expect(table).toContain("Benchmark Results");
76
76
  });
77
77
 
78
- it('handles all-failed strategy in table', () => {
78
+ it("handles all-failed strategy in table", () => {
79
79
  const summaries: StrategyBenchmarkSummary[] = [
80
80
  {
81
- strategyName: 'one-shot',
81
+ strategyName: "one-shot",
82
82
  totalChunks: 3,
83
83
  succeededChunks: 0,
84
84
  failedChunks: 3,
@@ -87,14 +87,14 @@ describe('formatBenchmarkReport', () => {
87
87
  totalAttempts: 3,
88
88
  },
89
89
  ];
90
- const table = formatBenchmarkReport(summaries, 'table');
91
- expect(table).toContain('0 ok, 3 failed');
90
+ const table = formatBenchmarkReport(summaries, "table");
91
+ expect(table).toContain("0 ok, 3 failed");
92
92
  });
93
93
 
94
- it('handles zero-duration strategy', () => {
94
+ it("handles zero-duration strategy", () => {
95
95
  const summaries: StrategyBenchmarkSummary[] = [
96
96
  {
97
- strategyName: 'one-shot',
97
+ strategyName: "one-shot",
98
98
  totalChunks: 1,
99
99
  succeededChunks: 1,
100
100
  failedChunks: 0,
@@ -103,15 +103,15 @@ describe('formatBenchmarkReport', () => {
103
103
  totalAttempts: 1,
104
104
  },
105
105
  ];
106
- const table = formatBenchmarkReport(summaries, 'table');
107
- expect(table).toContain('0ms');
108
- expect(table).toContain('0.0ms');
106
+ const table = formatBenchmarkReport(summaries, "table");
107
+ expect(table).toContain("0ms");
108
+ expect(table).toContain("0.0ms");
109
109
  });
110
110
 
111
- it('JSON format includes all numeric fields', () => {
111
+ it("JSON format includes all numeric fields", () => {
112
112
  const summaries: StrategyBenchmarkSummary[] = [
113
113
  {
114
- strategyName: 'tool-loop-agent',
114
+ strategyName: "tool-loop-agent",
115
115
  totalChunks: 2,
116
116
  succeededChunks: 1,
117
117
  failedChunks: 1,
@@ -120,7 +120,7 @@ describe('formatBenchmarkReport', () => {
120
120
  totalAttempts: 4,
121
121
  },
122
122
  ];
123
- const json = formatBenchmarkReport(summaries, 'json');
123
+ const json = formatBenchmarkReport(summaries, "json");
124
124
  const parsed = JSON.parse(json) as StrategyBenchmarkSummary[];
125
125
  expect(parsed[0]!.totalChunks).toBe(2);
126
126
  expect(parsed[0]!.totalAttempts).toBe(4);
@@ -1,21 +1,21 @@
1
- import type { StrategyBenchmarkSummary } from './metrics.js';
1
+ import type { StrategyBenchmarkSummary } from "./metrics.js";
2
2
 
3
3
  export function formatBenchmarkReport(
4
4
  summaries: readonly StrategyBenchmarkSummary[],
5
- format: 'table' | 'json',
5
+ format: "table" | "json",
6
6
  ): string {
7
- if (format === 'json') {
7
+ if (format === "json") {
8
8
  return JSON.stringify(summaries, null, 2);
9
9
  }
10
10
 
11
- const lines: string[] = ['Benchmark Results', '================='];
11
+ const lines: string[] = ["Benchmark Results", "================="];
12
12
  for (const s of summaries) {
13
13
  lines.push(`Strategy: ${s.strategyName}`);
14
14
  lines.push(` Chunks: ${s.totalChunks} (${s.succeededChunks} ok, ${s.failedChunks} failed)`);
15
15
  lines.push(` Total duration: ${s.totalDurationMs.toFixed(0)}ms`);
16
16
  lines.push(` Avg per chunk: ${s.averageDurationMsPerChunk.toFixed(1)}ms`);
17
17
  lines.push(` Total attempts: ${s.totalAttempts}`);
18
- lines.push('');
18
+ lines.push("");
19
19
  }
20
- return lines.join('\n');
20
+ return lines.join("\n");
21
21
  }
@@ -1,160 +1,158 @@
1
- import { describe, expect, it } from 'vitest';
2
- import { Effect } from 'effect';
3
- import { runBenchmark } from './runner.js';
4
- import type { StrategyBenchmarkSummary } from './metrics.js';
5
- import type { TranslationStrategy, TranslationContext } from '../translation/types.js';
6
- import { TranslationFailedError } from '../translation/types.js';
1
+ import { describe, expect, it } from "vitest";
2
+ import { Effect } from "effect";
3
+ import { runBenchmark } from "./runner.js";
4
+ import type { StrategyBenchmarkSummary } from "./metrics.js";
5
+ import type { TranslationStrategy, TranslationContext } from "../translation/types.js";
6
+ import { TranslationFailedError } from "../translation/types.js";
7
7
 
8
- describe('runBenchmark', () => {
9
- it('returns summaries for both strategies', async () => {
8
+ describe("runBenchmark", () => {
9
+ it("returns summaries for both strategies", async () => {
10
10
  const a: TranslationStrategy = {
11
- name: 'one-shot',
12
- translateChunk: () => Effect.succeed({ k: 'a' }),
11
+ name: "one-shot",
12
+ translateChunk: () => Effect.succeed({ k: "a" }),
13
13
  };
14
14
  const b: TranslationStrategy = {
15
- name: 'tool-loop-agent',
16
- translateChunk: () => Effect.succeed({ k: 'b' }),
15
+ name: "tool-loop-agent",
16
+ translateChunk: () => Effect.succeed({ k: "b" }),
17
17
  };
18
18
  const chunks: TranslationContext[] = [
19
19
  {
20
- sourceLocale: 'en',
21
- targetLocale: 'de',
22
- sourceMap: { k: 'K' },
20
+ sourceLocale: "en",
21
+ targetLocale: "de",
22
+ sourceMap: { k: "K" },
23
23
  targetMap: {},
24
- keys: ['k'],
24
+ keys: ["k"],
25
25
  },
26
26
  ];
27
- const result = await Effect.runPromise(
27
+ const result = (await Effect.runPromise(
28
28
  runBenchmark({ strategies: [a, b], chunks, concurrency: 1 }),
29
- ) as StrategyBenchmarkSummary[];
29
+ )) as StrategyBenchmarkSummary[];
30
30
  expect(result).toHaveLength(2);
31
- expect(result[0]!.strategyName).toBe('one-shot');
32
- expect(result[1]!.strategyName).toBe('tool-loop-agent');
31
+ expect(result[0]!.strategyName).toBe("one-shot");
32
+ expect(result[1]!.strategyName).toBe("tool-loop-agent");
33
33
  });
34
34
 
35
- it('does not abort when one strategy fails every chunk', async () => {
35
+ it("does not abort when one strategy fails every chunk", async () => {
36
36
  const a: TranslationStrategy = {
37
- name: 'one-shot',
38
- translateChunk: () => Effect.succeed({ k: 'a' }),
37
+ name: "one-shot",
38
+ translateChunk: () => Effect.succeed({ k: "a" }),
39
39
  };
40
40
  const b: TranslationStrategy = {
41
- name: 'tool-loop-agent',
42
- translateChunk: () =>
43
- Effect.fail(new TranslationFailedError({ keys: ['k'], cause: 'boom' })),
41
+ name: "tool-loop-agent",
42
+ translateChunk: () => Effect.fail(new TranslationFailedError({ keys: ["k"], cause: "boom" })),
44
43
  };
45
44
  const chunks: TranslationContext[] = [
46
45
  {
47
- sourceLocale: 'en',
48
- targetLocale: 'de',
49
- sourceMap: { k: 'K' },
46
+ sourceLocale: "en",
47
+ targetLocale: "de",
48
+ sourceMap: { k: "K" },
50
49
  targetMap: {},
51
- keys: ['k'],
50
+ keys: ["k"],
52
51
  },
53
52
  ];
54
- const result = await Effect.runPromise(
53
+ const result = (await Effect.runPromise(
55
54
  runBenchmark({ strategies: [a, b], chunks, concurrency: 1 }),
56
- ) as StrategyBenchmarkSummary[];
55
+ )) as StrategyBenchmarkSummary[];
57
56
  expect(result[0]!.succeededChunks).toBe(1);
58
57
  expect(result[1]!.failedChunks).toBe(1);
59
58
  });
60
59
 
61
- it('handles empty chunks', async () => {
60
+ it("handles empty chunks", async () => {
62
61
  const a: TranslationStrategy = {
63
- name: 'one-shot',
62
+ name: "one-shot",
64
63
  translateChunk: () => Effect.succeed({}),
65
64
  };
66
- const result = await Effect.runPromise(
65
+ const result = (await Effect.runPromise(
67
66
  runBenchmark({ strategies: [a], chunks: [], concurrency: 1 }),
68
- ) as StrategyBenchmarkSummary[];
67
+ )) as StrategyBenchmarkSummary[];
69
68
  expect(result).toHaveLength(1);
70
69
  expect(result[0]!.totalChunks).toBe(0);
71
70
  expect(result[0]!.succeededChunks).toBe(0);
72
71
  });
73
72
 
74
- it('handles single strategy', async () => {
73
+ it("handles single strategy", async () => {
75
74
  const a: TranslationStrategy = {
76
- name: 'one-shot',
77
- translateChunk: () => Effect.succeed({ a: 'A', b: 'B' }),
75
+ name: "one-shot",
76
+ translateChunk: () => Effect.succeed({ a: "A", b: "B" }),
78
77
  };
79
78
  const chunks: TranslationContext[] = [
80
79
  {
81
- sourceLocale: 'en',
82
- targetLocale: 'de',
83
- sourceMap: { a: 'A', b: 'B' },
80
+ sourceLocale: "en",
81
+ targetLocale: "de",
82
+ sourceMap: { a: "A", b: "B" },
84
83
  targetMap: {},
85
- keys: ['a', 'b'],
84
+ keys: ["a", "b"],
86
85
  },
87
86
  ];
88
- const result = await Effect.runPromise(
87
+ const result = (await Effect.runPromise(
89
88
  runBenchmark({ strategies: [a], chunks, concurrency: 1 }),
90
- ) as StrategyBenchmarkSummary[];
89
+ )) as StrategyBenchmarkSummary[];
91
90
  expect(result).toHaveLength(1);
92
91
  expect(result[0]!.totalChunks).toBe(1);
93
92
  expect(result[0]!.succeededChunks).toBe(1);
94
93
  });
95
94
 
96
- it('handles multiple chunks with concurrency', async () => {
95
+ it("handles multiple chunks with concurrency", async () => {
97
96
  const a: TranslationStrategy = {
98
- name: 'one-shot',
99
- translateChunk: () => Effect.succeed({ k: 'v' }),
97
+ name: "one-shot",
98
+ translateChunk: () => Effect.succeed({ k: "v" }),
100
99
  };
101
100
  const chunks: TranslationContext[] = Array.from({ length: 5 }, (_, i) => ({
102
- sourceLocale: 'en',
103
- targetLocale: 'de',
101
+ sourceLocale: "en",
102
+ targetLocale: "de",
104
103
  sourceMap: { [`k${i}`]: `V${i}` },
105
104
  targetMap: {},
106
105
  keys: [`k${i}`],
107
106
  }));
108
- const result = await Effect.runPromise(
107
+ const result = (await Effect.runPromise(
109
108
  runBenchmark({ strategies: [a], chunks, concurrency: 3 }),
110
- ) as StrategyBenchmarkSummary[];
109
+ )) as StrategyBenchmarkSummary[];
111
110
  expect(result[0]!.totalChunks).toBe(5);
112
111
  expect(result[0]!.succeededChunks).toBe(5);
113
112
  });
114
113
 
115
- it('handles all strategies failing', async () => {
114
+ it("handles all strategies failing", async () => {
116
115
  const a: TranslationStrategy = {
117
- name: 'one-shot',
118
- translateChunk: () =>
119
- Effect.fail(new TranslationFailedError({ keys: ['k'], cause: 'fail' })),
116
+ name: "one-shot",
117
+ translateChunk: () => Effect.fail(new TranslationFailedError({ keys: ["k"], cause: "fail" })),
120
118
  };
121
119
  const chunks: TranslationContext[] = [
122
120
  {
123
- sourceLocale: 'en',
124
- targetLocale: 'de',
125
- sourceMap: { k: 'K' },
121
+ sourceLocale: "en",
122
+ targetLocale: "de",
123
+ sourceMap: { k: "K" },
126
124
  targetMap: {},
127
- keys: ['k'],
125
+ keys: ["k"],
128
126
  },
129
127
  ];
130
- const result = await Effect.runPromise(
128
+ const result = (await Effect.runPromise(
131
129
  runBenchmark({ strategies: [a], chunks, concurrency: 1 }),
132
- ) as StrategyBenchmarkSummary[];
130
+ )) as StrategyBenchmarkSummary[];
133
131
  expect(result[0]!.failedChunks).toBe(1);
134
132
  expect(result[0]!.succeededChunks).toBe(0);
135
133
  });
136
134
 
137
- it('handles mixed success and failure across chunks', async () => {
135
+ it("handles mixed success and failure across chunks", async () => {
138
136
  let callCount = 0;
139
137
  const a: TranslationStrategy = {
140
- name: 'one-shot',
138
+ name: "one-shot",
141
139
  translateChunk: () => {
142
140
  callCount++;
143
141
  return callCount % 2 === 1
144
- ? Effect.succeed({ k: 'v' })
145
- : Effect.fail(new TranslationFailedError({ keys: ['k'], cause: 'odd' }));
142
+ ? Effect.succeed({ k: "v" })
143
+ : Effect.fail(new TranslationFailedError({ keys: ["k"], cause: "odd" }));
146
144
  },
147
145
  };
148
146
  const chunks: TranslationContext[] = Array.from({ length: 4 }, () => ({
149
- sourceLocale: 'en',
150
- targetLocale: 'de',
151
- sourceMap: { k: 'K' },
147
+ sourceLocale: "en",
148
+ targetLocale: "de",
149
+ sourceMap: { k: "K" },
152
150
  targetMap: {},
153
- keys: ['k'],
151
+ keys: ["k"],
154
152
  }));
155
- const result = await Effect.runPromise(
153
+ const result = (await Effect.runPromise(
156
154
  runBenchmark({ strategies: [a], chunks, concurrency: 1 }),
157
- ) as StrategyBenchmarkSummary[];
155
+ )) as StrategyBenchmarkSummary[];
158
156
  expect(result[0]!.totalChunks).toBe(4);
159
157
  expect(result[0]!.succeededChunks).toBe(2);
160
158
  expect(result[0]!.failedChunks).toBe(2);
@@ -1,7 +1,7 @@
1
- import { Effect } from 'effect';
2
- import type { TranslationStrategy, TranslationContext } from '../translation/types.js';
3
- import type { StrategyBenchmarkSummary } from './metrics.js';
4
- import { runBenchmarkedChunk, summarizeBenchmarkResults } from './metrics.js';
1
+ import { Effect } from "effect";
2
+ import type { TranslationStrategy, TranslationContext } from "../translation/types.js";
3
+ import type { StrategyBenchmarkSummary } from "./metrics.js";
4
+ import { runBenchmarkedChunk, summarizeBenchmarkResults } from "./metrics.js";
5
5
 
6
6
  export interface BenchmarkConfig {
7
7
  readonly strategies: readonly TranslationStrategy[];