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,16 +1,16 @@
1
- import { describe, expect, it } from 'vitest';
2
- import { Effect, Option } from 'effect';
3
- import { runLanguages, languagesCommand } from './languages.js';
4
- import type { DialektConfig } from '../../config/types.js';
5
- import type { TranslationAdapter } from '../../adapter/types.js';
1
+ import { describe, expect, it } from "vitest";
2
+ import { Effect, Option } from "effect";
3
+ import { runLanguages, languagesCommand } from "./languages.js";
4
+ import type { DialektConfig } from "../../config/types.js";
5
+ import type { TranslationAdapter } from "../../adapter/types.js";
6
6
 
7
- describe('runLanguages', () => {
7
+ describe("runLanguages", () => {
8
8
  const baseConfig: DialektConfig = {
9
- sourceLocale: 'en',
10
- targetLocales: ['de', 'fr'],
11
- strategy: 'one-shot',
12
- model: { provider: 'openai', modelId: 'gpt-4o' },
13
- fastModel: { provider: 'openai', modelId: 'gpt-4o-mini' },
9
+ sourceLocale: "en",
10
+ targetLocales: ["de", "fr"],
11
+ strategy: "one-shot",
12
+ model: { provider: "openai", modelId: "gpt-4o" },
13
+ fastModel: { provider: "openai", modelId: "gpt-4o-mini" },
14
14
  chunking: { maxTokens: 3000, charsPerToken: 3.0, concurrency: 3 },
15
15
  retry: { maxAttempts: 3, baseDelayMs: 1000 },
16
16
  adapters: [],
@@ -20,21 +20,24 @@ describe('runLanguages', () => {
20
20
  return {
21
21
  name: opts.name,
22
22
  capabilities: { canCreateResource: true, unusedKeyDetection: false },
23
- listLocales: () => Effect.succeed(opts.locales ?? ['en', 'de']),
23
+ listLocales: () => Effect.succeed(opts.locales ?? ["en", "de"]),
24
24
  listResources: () => Effect.succeed([]),
25
25
  readResource: () => Effect.succeed({}),
26
26
  writeResource: () => Effect.void,
27
27
  };
28
28
  }
29
29
 
30
- it('logs locales for each adapter', async () => {
30
+ it("logs locales for each adapter", async () => {
31
31
  const logs: string[] = [];
32
- const laravel = makeAdapter({ name: 'laravel', locales: ['en', 'de', 'fr'] });
33
- const paraglide = makeAdapter({ name: 'paraglide', locales: ['en', 'es'] });
34
- const config = { ...baseConfig, adapters: [laravel, paraglide] as unknown as DialektConfig['adapters'] };
32
+ const laravel = makeAdapter({ name: "laravel", locales: ["en", "de", "fr"] });
33
+ const paraglide = makeAdapter({ name: "paraglide", locales: ["en", "es"] });
34
+ const config = {
35
+ ...baseConfig,
36
+ adapters: [laravel, paraglide] as unknown as DialektConfig["adapters"],
37
+ };
35
38
 
36
39
  const program = runLanguages(
37
- { config: './config.ts' },
40
+ { config: "./config.ts" },
38
41
  () => Effect.succeed(config),
39
42
  (msg) => Effect.sync(() => logs.push(msg)),
40
43
  );
@@ -43,17 +46,17 @@ describe('runLanguages', () => {
43
46
  expect(logs).toHaveLength(1);
44
47
  const parsed = JSON.parse(logs[0]!);
45
48
  expect(parsed).toHaveLength(2);
46
- expect(parsed).toContainEqual({ adapter: 'laravel', locales: ['en', 'de', 'fr'] });
47
- expect(parsed).toContainEqual({ adapter: 'paraglide', locales: ['en', 'es'] });
49
+ expect(parsed).toContainEqual({ adapter: "laravel", locales: ["en", "de", "fr"] });
50
+ expect(parsed).toContainEqual({ adapter: "paraglide", locales: ["en", "es"] });
48
51
  });
49
52
 
50
- it('handles single adapter with single locale', async () => {
53
+ it("handles single adapter with single locale", async () => {
51
54
  const logs: string[] = [];
52
- const adapter = makeAdapter({ name: 'mono', locales: ['en'] });
53
- const config = { ...baseConfig, adapters: [adapter] as unknown as DialektConfig['adapters'] };
55
+ const adapter = makeAdapter({ name: "mono", locales: ["en"] });
56
+ const config = { ...baseConfig, adapters: [adapter] as unknown as DialektConfig["adapters"] };
54
57
 
55
58
  const program = runLanguages(
56
- { config: './config.ts' },
59
+ { config: "./config.ts" },
57
60
  () => Effect.succeed(config),
58
61
  (msg) => Effect.sync(() => logs.push(msg)),
59
62
  );
@@ -61,15 +64,15 @@ describe('runLanguages', () => {
61
64
  await Effect.runPromise(program);
62
65
  expect(logs).toHaveLength(1);
63
66
  const parsed = JSON.parse(logs[0]!);
64
- expect(parsed).toEqual([{ adapter: 'mono', locales: ['en'] }]);
67
+ expect(parsed).toEqual([{ adapter: "mono", locales: ["en"] }]);
65
68
  });
66
69
 
67
- it('handles empty adapter list', async () => {
70
+ it("handles empty adapter list", async () => {
68
71
  const logs: string[] = [];
69
72
  const config = { ...baseConfig, adapters: [] };
70
73
 
71
74
  const program = runLanguages(
72
- { config: './config.ts' },
75
+ { config: "./config.ts" },
73
76
  () => Effect.succeed(config),
74
77
  (msg) => Effect.sync(() => logs.push(msg)),
75
78
  );
@@ -80,23 +83,23 @@ describe('runLanguages', () => {
80
83
  expect(parsed).toEqual([]);
81
84
  });
82
85
 
83
- it('fails when configLoader fails', async () => {
86
+ it("fails when configLoader fails", async () => {
84
87
  const program = runLanguages(
85
- { config: './missing.ts' },
86
- () => Effect.fail(new Error('Config not found')),
88
+ { config: "./missing.ts" },
89
+ () => Effect.fail(new Error("Config not found")),
87
90
  () => Effect.void,
88
91
  );
89
92
 
90
- await expect(Effect.runPromise(program)).rejects.toThrow('Config not found');
93
+ await expect(Effect.runPromise(program)).rejects.toThrow("Config not found");
91
94
  });
92
95
 
93
- it('handles adapter with empty locales', async () => {
96
+ it("handles adapter with empty locales", async () => {
94
97
  const logs: string[] = [];
95
- const adapter = makeAdapter({ name: 'empty', locales: [] });
96
- const config = { ...baseConfig, adapters: [adapter] as unknown as DialektConfig['adapters'] };
98
+ const adapter = makeAdapter({ name: "empty", locales: [] });
99
+ const config = { ...baseConfig, adapters: [adapter] as unknown as DialektConfig["adapters"] };
97
100
 
98
101
  const program = runLanguages(
99
- { config: './config.ts' },
102
+ { config: "./config.ts" },
100
103
  () => Effect.succeed(config),
101
104
  (msg) => Effect.sync(() => logs.push(msg)),
102
105
  );
@@ -104,24 +107,24 @@ describe('runLanguages', () => {
104
107
  await Effect.runPromise(program);
105
108
  expect(logs).toHaveLength(1);
106
109
  const parsed = JSON.parse(logs[0]!);
107
- expect(parsed).toEqual([{ adapter: 'empty', locales: [] }]);
110
+ expect(parsed).toEqual([{ adapter: "empty", locales: [] }]);
108
111
  });
109
112
 
110
- it('outputs pretty when --format pretty is passed', async () => {
113
+ it("outputs pretty when --format pretty is passed", async () => {
111
114
  const logs: string[] = [];
112
- const adapter = makeAdapter({ name: 'test', locales: ['en', 'de'] });
113
- const config = { ...baseConfig, adapters: [adapter] as unknown as DialektConfig['adapters'] };
115
+ const adapter = makeAdapter({ name: "test", locales: ["en", "de"] });
116
+ const config = { ...baseConfig, adapters: [adapter] as unknown as DialektConfig["adapters"] };
114
117
 
115
118
  const program = runLanguages(
116
- { config: './config.ts', format: Option.some('pretty') },
119
+ { config: "./config.ts", format: Option.some("pretty") },
117
120
  () => Effect.succeed(config),
118
121
  (msg) => Effect.sync(() => logs.push(msg)),
119
122
  );
120
123
 
121
124
  await Effect.runPromise(program);
122
125
  expect(logs).toHaveLength(1);
123
- expect(logs[0]).toContain('test');
124
- expect(logs[0]).toContain('en');
125
- expect(logs[0]).toContain('de');
126
+ expect(logs[0]).toContain("test");
127
+ expect(logs[0]).toContain("en");
128
+ expect(logs[0]).toContain("de");
126
129
  });
127
130
  });
@@ -1,11 +1,11 @@
1
- import { Command, Options } from '@effect/cli';
2
- import { Effect, Console, Option } from 'effect';
3
- import { loadConfig } from '../../config/load-config.js';
4
- import { resolveEffectiveConfig } from '../config-resolution.js';
5
- import { detectFormat, type OutputFormat } from '../format.js';
6
- import { formatLanguages } from '../formatters.js';
7
- import type { DialektConfig } from '../../config/types.js';
8
- import type { TranslationAdapter } from '../../adapter/types.js';
1
+ import { Command, Options } from "@effect/cli";
2
+ import { Effect, Console, Option } from "effect";
3
+ import { loadConfig } from "../../config/load-config.js";
4
+ import { resolveEffectiveConfig } from "../config-resolution.js";
5
+ import { detectFormat, type OutputFormat } from "../format.js";
6
+ import { formatLanguages } from "../formatters.js";
7
+ import type { DialektConfig } from "../../config/types.js";
8
+ import type { TranslationAdapter } from "../../adapter/types.js";
9
9
 
10
10
  export interface LanguagesFlags {
11
11
  readonly config: string;
@@ -36,7 +36,11 @@ export function runLanguages(
36
36
  }).pipe(Effect.mapError((e) => e as never)) as Effect.Effect<void, never, never>;
37
37
  }
38
38
 
39
- export const languagesCommand = Command.make('languages', {
40
- config: Options.text('config').pipe(Options.withDefault('./dialekt.config.ts')),
41
- format: Options.optional(Options.text('format')),
42
- }, (flags) => runLanguages(flags));
39
+ export const languagesCommand = Command.make(
40
+ "languages",
41
+ {
42
+ config: Options.text("config").pipe(Options.withDefault("./dialekt.config.ts")),
43
+ format: Options.optional(Options.text("format")),
44
+ },
45
+ (flags) => runLanguages(flags),
46
+ );
@@ -1,16 +1,16 @@
1
- import { describe, expect, it } from 'vitest';
2
- import { Effect, Option } from 'effect';
3
- import { runMissing, missingCommand } from './missing.js';
4
- import type { DialektConfig } from '../../config/types.js';
5
- import type { TranslationAdapter, ResourceRef } from '../../adapter/types.js';
1
+ import { describe, expect, it } from "vitest";
2
+ import { Effect, Option } from "effect";
3
+ import { runMissing, missingCommand } from "./missing.js";
4
+ import type { DialektConfig } from "../../config/types.js";
5
+ import type { TranslationAdapter, ResourceRef } from "../../adapter/types.js";
6
6
 
7
- describe('runMissing', () => {
7
+ describe("runMissing", () => {
8
8
  const baseConfig: DialektConfig = {
9
- sourceLocale: 'en',
10
- targetLocales: ['de', 'fr'],
11
- strategy: 'one-shot',
12
- model: { provider: 'openai', modelId: 'gpt-4o' },
13
- fastModel: { provider: 'openai', modelId: 'gpt-4o-mini' },
9
+ sourceLocale: "en",
10
+ targetLocales: ["de", "fr"],
11
+ strategy: "one-shot",
12
+ model: { provider: "openai", modelId: "gpt-4o" },
13
+ fastModel: { provider: "openai", modelId: "gpt-4o-mini" },
14
14
  chunking: { maxTokens: 3000, charsPerToken: 3.0, concurrency: 3 },
15
15
  retry: { maxAttempts: 3, baseDelayMs: 1000 },
16
16
  adapters: [],
@@ -24,26 +24,29 @@ describe('runMissing', () => {
24
24
  return {
25
25
  name: opts.name,
26
26
  capabilities: { canCreateResource: true, unusedKeyDetection: false },
27
- listLocales: () => Effect.succeed(opts.locales ?? ['en', 'de']),
28
- listResources: () => Effect.succeed([{ key: 'messages', label: 'messages' }]),
27
+ listLocales: () => Effect.succeed(opts.locales ?? ["en", "de"]),
28
+ listResources: () => Effect.succeed([{ key: "messages", label: "messages" }]),
29
29
  readResource: () => Effect.succeed({}),
30
30
  writeResource: () => Effect.void,
31
31
  };
32
32
  }
33
33
 
34
- it('logs missing keys in default format', async () => {
34
+ it("logs missing keys in default format", async () => {
35
35
  const logs: string[] = [];
36
- const adapter = makeAdapter({ name: 'test' });
37
- const config = { ...baseConfig, adapters: [adapter] as unknown as DialektConfig['adapters'] };
38
- const resource: ResourceRef = { key: 'messages', label: 'messages' };
36
+ const adapter = makeAdapter({ name: "test" });
37
+ const config = { ...baseConfig, adapters: [adapter] as unknown as DialektConfig["adapters"] };
38
+ const resource: ResourceRef = { key: "messages", label: "messages" };
39
39
 
40
40
  const program = runMissing(
41
- { config: './config.ts', adapter: Option.none(), baseLanguage: Option.none(), language: Option.none() },
41
+ {
42
+ config: "./config.ts",
43
+ adapter: Option.none(),
44
+ baseLanguage: Option.none(),
45
+ language: Option.none(),
46
+ },
42
47
  () => Effect.succeed(config),
43
48
  () =>
44
- Effect.succeed([
45
- { adapter: 'test', locale: 'de', resource, missing: ['hello', 'bye'] },
46
- ]),
49
+ Effect.succeed([{ adapter: "test", locale: "de", resource, missing: ["hello", "bye"] }]),
47
50
  (msg) => Effect.sync(() => logs.push(msg)),
48
51
  );
49
52
 
@@ -51,66 +54,76 @@ describe('runMissing', () => {
51
54
  expect(logs).toHaveLength(1);
52
55
  const parsed = JSON.parse(logs[0]!);
53
56
  expect(parsed).toEqual([
54
- { adapter: 'test', locale: 'de', resource: 'messages', key: 'hello' },
55
- { adapter: 'test', locale: 'de', resource: 'messages', key: 'bye' },
57
+ { adapter: "test", locale: "de", resource: "messages", key: "hello" },
58
+ { adapter: "test", locale: "de", resource: "messages", key: "bye" },
56
59
  ]);
57
60
  });
58
61
 
59
- it('outputs JSON when --format json is passed', async () => {
62
+ it("outputs JSON when --format json is passed", async () => {
60
63
  const logs: string[] = [];
61
- const adapter = makeAdapter({ name: 'test' });
62
- const config = { ...baseConfig, adapters: [adapter] as unknown as DialektConfig['adapters'] };
63
- const resource: ResourceRef = { key: 'messages', label: 'messages' };
64
+ const adapter = makeAdapter({ name: "test" });
65
+ const config = { ...baseConfig, adapters: [adapter] as unknown as DialektConfig["adapters"] };
66
+ const resource: ResourceRef = { key: "messages", label: "messages" };
64
67
 
65
68
  const program = runMissing(
66
- { config: './config.ts', adapter: Option.none(), baseLanguage: Option.none(), language: Option.none(), format: Option.some('json') },
69
+ {
70
+ config: "./config.ts",
71
+ adapter: Option.none(),
72
+ baseLanguage: Option.none(),
73
+ language: Option.none(),
74
+ format: Option.some("json"),
75
+ },
67
76
  () => Effect.succeed(config),
68
- () =>
69
- Effect.succeed([
70
- { adapter: 'test', locale: 'de', resource, missing: ['hello'] },
71
- ]),
77
+ () => Effect.succeed([{ adapter: "test", locale: "de", resource, missing: ["hello"] }]),
72
78
  (msg) => Effect.sync(() => logs.push(msg)),
73
79
  );
74
80
 
75
81
  await Effect.runPromise(program);
76
82
  expect(logs).toHaveLength(1);
77
83
  const parsed = JSON.parse(logs[0]!);
78
- expect(parsed).toEqual([
79
- { adapter: 'test', locale: 'de', resource: 'messages', key: 'hello' },
80
- ]);
84
+ expect(parsed).toEqual([{ adapter: "test", locale: "de", resource: "messages", key: "hello" }]);
81
85
  });
82
86
 
83
- it('outputs pretty when --format pretty is passed', async () => {
87
+ it("outputs pretty when --format pretty is passed", async () => {
84
88
  const logs: string[] = [];
85
- const adapter = makeAdapter({ name: 'test' });
86
- const config = { ...baseConfig, adapters: [adapter] as unknown as DialektConfig['adapters'] };
87
- const resource: ResourceRef = { key: 'messages', label: 'messages' };
89
+ const adapter = makeAdapter({ name: "test" });
90
+ const config = { ...baseConfig, adapters: [adapter] as unknown as DialektConfig["adapters"] };
91
+ const resource: ResourceRef = { key: "messages", label: "messages" };
88
92
 
89
93
  const program = runMissing(
90
- { config: './config.ts', adapter: Option.none(), baseLanguage: Option.none(), language: Option.none(), format: Option.some('pretty') },
94
+ {
95
+ config: "./config.ts",
96
+ adapter: Option.none(),
97
+ baseLanguage: Option.none(),
98
+ language: Option.none(),
99
+ format: Option.some("pretty"),
100
+ },
91
101
  () => Effect.succeed(config),
92
102
  () =>
93
- Effect.succeed([
94
- { adapter: 'test', locale: 'de', resource, missing: ['hello', 'bye'] },
95
- ]),
103
+ Effect.succeed([{ adapter: "test", locale: "de", resource, missing: ["hello", "bye"] }]),
96
104
  (msg) => Effect.sync(() => logs.push(msg)),
97
105
  );
98
106
 
99
107
  await Effect.runPromise(program);
100
108
  expect(logs).toHaveLength(1);
101
- expect(logs[0]).toContain('test');
102
- expect(logs[0]).toContain('de');
103
- expect(logs[0]).toContain('hello');
104
- expect(logs[0]).toContain('bye');
109
+ expect(logs[0]).toContain("test");
110
+ expect(logs[0]).toContain("de");
111
+ expect(logs[0]).toContain("hello");
112
+ expect(logs[0]).toContain("bye");
105
113
  });
106
114
 
107
- it('returns empty JSON array when nothing is missing', async () => {
115
+ it("returns empty JSON array when nothing is missing", async () => {
108
116
  const logs: string[] = [];
109
- const adapter = makeAdapter({ name: 'test' });
110
- const config = { ...baseConfig, adapters: [adapter] as unknown as DialektConfig['adapters'] };
117
+ const adapter = makeAdapter({ name: "test" });
118
+ const config = { ...baseConfig, adapters: [adapter] as unknown as DialektConfig["adapters"] };
111
119
 
112
120
  const program = runMissing(
113
- { config: './config.ts', adapter: Option.none(), baseLanguage: Option.none(), language: Option.none() },
121
+ {
122
+ config: "./config.ts",
123
+ adapter: Option.none(),
124
+ baseLanguage: Option.none(),
125
+ language: Option.none(),
126
+ },
114
127
  () => Effect.succeed(config),
115
128
  () => Effect.succeed([]),
116
129
  (msg) => Effect.sync(() => logs.push(msg)),
@@ -121,19 +134,24 @@ describe('runMissing', () => {
121
134
  expect(JSON.parse(logs[0]!)).toEqual([]);
122
135
  });
123
136
 
124
- it('handles multiple adapters and multiple locales', async () => {
137
+ it("handles multiple adapters and multiple locales", async () => {
125
138
  const logs: string[] = [];
126
- const a1 = makeAdapter({ name: 'a1' });
127
- const a2 = makeAdapter({ name: 'a2', locales: ['en', 'fr'] });
128
- const config = { ...baseConfig, adapters: [a1, a2] as unknown as DialektConfig['adapters'] };
129
- const resource: ResourceRef = { key: 'messages', label: 'messages' };
139
+ const a1 = makeAdapter({ name: "a1" });
140
+ const a2 = makeAdapter({ name: "a2", locales: ["en", "fr"] });
141
+ const config = { ...baseConfig, adapters: [a1, a2] as unknown as DialektConfig["adapters"] };
142
+ const resource: ResourceRef = { key: "messages", label: "messages" };
130
143
 
131
144
  const program = runMissing(
132
- { config: './config.ts', adapter: Option.none(), baseLanguage: Option.none(), language: Option.none() },
145
+ {
146
+ config: "./config.ts",
147
+ adapter: Option.none(),
148
+ baseLanguage: Option.none(),
149
+ language: Option.none(),
150
+ },
133
151
  () => Effect.succeed(config),
134
152
  (a) =>
135
153
  Effect.succeed([
136
- { adapter: a.name, locale: a.name === 'a1' ? 'de' : 'fr', resource, missing: ['k1'] },
154
+ { adapter: a.name, locale: a.name === "a1" ? "de" : "fr", resource, missing: ["k1"] },
137
155
  ]),
138
156
  (msg) => Effect.sync(() => logs.push(msg)),
139
157
  );
@@ -142,18 +160,23 @@ describe('runMissing', () => {
142
160
  expect(logs).toHaveLength(1);
143
161
  const parsed = JSON.parse(logs[0]!);
144
162
  expect(parsed).toHaveLength(2);
145
- expect(parsed).toContainEqual({ adapter: 'a1', locale: 'de', resource: 'messages', key: 'k1' });
146
- expect(parsed).toContainEqual({ adapter: 'a2', locale: 'fr', resource: 'messages', key: 'k1' });
163
+ expect(parsed).toContainEqual({ adapter: "a1", locale: "de", resource: "messages", key: "k1" });
164
+ expect(parsed).toContainEqual({ adapter: "a2", locale: "fr", resource: "messages", key: "k1" });
147
165
  });
148
166
 
149
- it('filters adapters by --adapter flag', async () => {
167
+ it("filters adapters by --adapter flag", async () => {
150
168
  let queriedAdapter: string | undefined;
151
- const a1 = makeAdapter({ name: 'a1' });
152
- const a2 = makeAdapter({ name: 'a2' });
153
- const config = { ...baseConfig, adapters: [a1, a2] as unknown as DialektConfig['adapters'] };
169
+ const a1 = makeAdapter({ name: "a1" });
170
+ const a2 = makeAdapter({ name: "a2" });
171
+ const config = { ...baseConfig, adapters: [a1, a2] as unknown as DialektConfig["adapters"] };
154
172
 
155
173
  const program = runMissing(
156
- { config: './config.ts', adapter: Option.some('a2'), baseLanguage: Option.none(), language: Option.none() },
174
+ {
175
+ config: "./config.ts",
176
+ adapter: Option.some("a2"),
177
+ baseLanguage: Option.none(),
178
+ language: Option.none(),
179
+ },
157
180
  () => Effect.succeed(config),
158
181
  (a) =>
159
182
  Effect.sync(() => {
@@ -164,47 +187,62 @@ describe('runMissing', () => {
164
187
  );
165
188
 
166
189
  await Effect.runPromise(program);
167
- expect(queriedAdapter).toBe('a2');
190
+ expect(queriedAdapter).toBe("a2");
168
191
  });
169
192
 
170
- it('fails when configLoader fails', async () => {
193
+ it("fails when configLoader fails", async () => {
171
194
  const program = runMissing(
172
- { config: './missing.ts', adapter: Option.none(), baseLanguage: Option.none(), language: Option.none() },
173
- () => Effect.fail(new Error('Config not found')),
195
+ {
196
+ config: "./missing.ts",
197
+ adapter: Option.none(),
198
+ baseLanguage: Option.none(),
199
+ language: Option.none(),
200
+ },
201
+ () => Effect.fail(new Error("Config not found")),
174
202
  () => Effect.succeed([]),
175
203
  () => Effect.void,
176
204
  );
177
205
 
178
- await expect(Effect.runPromise(program)).rejects.toThrow('Config not found');
206
+ await expect(Effect.runPromise(program)).rejects.toThrow("Config not found");
179
207
  });
180
208
 
181
- it('fails when listLocales fails', async () => {
209
+ it("fails when listLocales fails", async () => {
182
210
  const adapter: TranslationAdapter = {
183
- name: 'broken',
211
+ name: "broken",
184
212
  capabilities: { canCreateResource: true, unusedKeyDetection: false },
185
- listLocales: () => Effect.fail(new Error('disk error') as never),
213
+ listLocales: () => Effect.fail(new Error("disk error") as never),
186
214
  listResources: () => Effect.succeed([]),
187
215
  readResource: () => Effect.succeed({}),
188
216
  writeResource: () => Effect.void,
189
217
  };
190
- const config = { ...baseConfig, adapters: [adapter] as unknown as DialektConfig['adapters'] };
218
+ const config = { ...baseConfig, adapters: [adapter] as unknown as DialektConfig["adapters"] };
191
219
 
192
220
  const program = runMissing(
193
- { config: './config.ts', adapter: Option.none(), baseLanguage: Option.none(), language: Option.none() },
221
+ {
222
+ config: "./config.ts",
223
+ adapter: Option.none(),
224
+ baseLanguage: Option.none(),
225
+ language: Option.none(),
226
+ },
194
227
  () => Effect.succeed(config),
195
228
  () => Effect.succeed([]),
196
229
  () => Effect.void,
197
230
  );
198
231
 
199
- await expect(Effect.runPromise(program)).rejects.toThrow('disk error');
232
+ await expect(Effect.runPromise(program)).rejects.toThrow("disk error");
200
233
  });
201
234
 
202
- it('handles empty adapter list', async () => {
235
+ it("handles empty adapter list", async () => {
203
236
  const logs: string[] = [];
204
237
  const config = { ...baseConfig, adapters: [] };
205
238
 
206
239
  const program = runMissing(
207
- { config: './config.ts', adapter: Option.none(), baseLanguage: Option.none(), language: Option.none() },
240
+ {
241
+ config: "./config.ts",
242
+ adapter: Option.none(),
243
+ baseLanguage: Option.none(),
244
+ language: Option.none(),
245
+ },
208
246
  () => Effect.succeed(config),
209
247
  () => Effect.succeed([]),
210
248
  (msg) => Effect.sync(() => logs.push(msg)),
@@ -215,13 +253,18 @@ describe('runMissing', () => {
215
253
  expect(JSON.parse(logs[0]!)).toEqual([]);
216
254
  });
217
255
 
218
- it('handles single-locale adapter (no targets)', async () => {
256
+ it("handles single-locale adapter (no targets)", async () => {
219
257
  const logs: string[] = [];
220
- const adapter = makeAdapter({ name: 'mono', locales: ['en'] });
221
- const config = { ...baseConfig, adapters: [adapter] as unknown as DialektConfig['adapters'] };
258
+ const adapter = makeAdapter({ name: "mono", locales: ["en"] });
259
+ const config = { ...baseConfig, adapters: [adapter] as unknown as DialektConfig["adapters"] };
222
260
 
223
261
  const program = runMissing(
224
- { config: './config.ts', adapter: Option.none(), baseLanguage: Option.none(), language: Option.none() },
262
+ {
263
+ config: "./config.ts",
264
+ adapter: Option.none(),
265
+ baseLanguage: Option.none(),
266
+ language: Option.none(),
267
+ },
225
268
  () => Effect.succeed(config),
226
269
  () => Effect.succeed([]),
227
270
  (msg) => Effect.sync(() => logs.push(msg)),
@@ -232,25 +275,33 @@ describe('runMissing', () => {
232
275
  expect(JSON.parse(logs[0]!)).toEqual([]);
233
276
  });
234
277
 
235
- it('produces valid JSON array even with many entries', async () => {
278
+ it("produces valid JSON array even with many entries", async () => {
236
279
  const logs: string[] = [];
237
- const adapter = makeAdapter({ name: 'test' });
238
- const config = { ...baseConfig, adapters: [adapter] as unknown as DialektConfig['adapters'] };
239
- const resource: ResourceRef = { key: 'messages', label: 'messages' };
280
+ const adapter = makeAdapter({ name: "test" });
281
+ const config = { ...baseConfig, adapters: [adapter] as unknown as DialektConfig["adapters"] };
282
+ const resource: ResourceRef = { key: "messages", label: "messages" };
240
283
 
241
284
  const program = runMissing(
242
- { config: './config.ts', adapter: Option.none(), baseLanguage: Option.none(), language: Option.none(), format: Option.some('json') },
285
+ {
286
+ config: "./config.ts",
287
+ adapter: Option.none(),
288
+ baseLanguage: Option.none(),
289
+ language: Option.none(),
290
+ format: Option.some("json"),
291
+ },
243
292
  () => Effect.succeed(config),
244
- () =>
245
- Effect.succeed([
246
- { adapter: 'test', locale: 'de', resource, missing: ['a', 'b', 'c'] },
247
- ]),
293
+ () => Effect.succeed([{ adapter: "test", locale: "de", resource, missing: ["a", "b", "c"] }]),
248
294
  (msg) => Effect.sync(() => logs.push(msg)),
249
295
  );
250
296
 
251
297
  await Effect.runPromise(program);
252
298
  const parsed = JSON.parse(logs[0]!);
253
299
  expect(parsed).toHaveLength(3);
254
- expect(parsed[0]).toMatchObject({ adapter: 'test', locale: 'de', resource: 'messages', key: 'a' });
300
+ expect(parsed[0]).toMatchObject({
301
+ adapter: "test",
302
+ locale: "de",
303
+ resource: "messages",
304
+ key: "a",
305
+ });
255
306
  });
256
307
  });
@@ -1,12 +1,12 @@
1
- import { Command, Options } from '@effect/cli';
2
- import { Effect, Console, Option } from 'effect';
3
- import { loadConfig } from '../../config/load-config.js';
4
- import { resolveEffectiveConfig } from '../config-resolution.js';
5
- import { computeMissingKeys } from '../../translation/missing-keys.js';
6
- import { detectFormat, type OutputFormat } from '../format.js';
7
- import { formatMissingKeys } from '../formatters.js';
8
- import type { DialektConfig } from '../../config/types.js';
9
- import type { TranslationAdapter } from '../../adapter/types.js';
1
+ import { Command, Options } from "@effect/cli";
2
+ import { Effect, Console, Option } from "effect";
3
+ import { loadConfig } from "../../config/load-config.js";
4
+ import { resolveEffectiveConfig } from "../config-resolution.js";
5
+ import { computeMissingKeys } from "../../translation/missing-keys.js";
6
+ import { detectFormat, type OutputFormat } from "../format.js";
7
+ import { formatMissingKeys } from "../formatters.js";
8
+ import type { DialektConfig } from "../../config/types.js";
9
+ import type { TranslationAdapter } from "../../adapter/types.js";
10
10
 
11
11
  export interface MissingFlags {
12
12
  readonly config: string;
@@ -30,7 +30,10 @@ export function runMissing(
30
30
  adapter: TranslationAdapter,
31
31
  sourceLocale: string,
32
32
  targetLocales: readonly string[],
33
- ) => Effect.Effect<readonly MissingKeysEntry[], unknown> = computeMissingKeys as unknown as typeof missingKeysComputer,
33
+ ) => Effect.Effect<
34
+ readonly MissingKeysEntry[],
35
+ unknown
36
+ > = computeMissingKeys as unknown as typeof missingKeysComputer,
34
37
  logger: (msg: string) => Effect.Effect<void> = (msg: string) => Console.log(msg),
35
38
  ): Effect.Effect<void, never> {
36
39
  return Effect.gen(function* () {
@@ -79,10 +82,14 @@ export function runMissing(
79
82
  }).pipe(Effect.mapError((e) => e as never)) as Effect.Effect<void, never, never>;
80
83
  }
81
84
 
82
- export const missingCommand = Command.make('missing', {
83
- config: Options.text('config').pipe(Options.withDefault('./dialekt.config.ts')),
84
- adapter: Options.optional(Options.text('adapter')),
85
- baseLanguage: Options.optional(Options.text('base-language')),
86
- language: Options.optional(Options.text('language')),
87
- format: Options.optional(Options.text('format')),
88
- }, (flags) => runMissing(flags));
85
+ export const missingCommand = Command.make(
86
+ "missing",
87
+ {
88
+ config: Options.text("config").pipe(Options.withDefault("./dialekt.config.ts")),
89
+ adapter: Options.optional(Options.text("adapter")),
90
+ baseLanguage: Options.optional(Options.text("base-language")),
91
+ language: Options.optional(Options.text("language")),
92
+ format: Options.optional(Options.text("format")),
93
+ },
94
+ (flags) => runMissing(flags),
95
+ );