@walkinissue/angy 0.2.18 → 0.2.19

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 CHANGED
@@ -1,6 +1,8 @@
1
1
  # Angy
2
2
 
3
- Dev-only SvelteKit translation helper for in-app PO workflow.
3
+ Dev-only SvelteKit translation helper for in-app PO workflows.
4
+
5
+ <video src="./docs/images/Backdrop_And_Caching.webm" controls muted playsinline width="100%"></video>
4
6
 
5
7
  ## Install
6
8
 
@@ -8,134 +10,30 @@ Dev-only SvelteKit translation helper for in-app PO workflow.
8
10
  npm install @walkinissue/angy
9
11
  ```
10
12
 
11
- ## Project links
12
-
13
- - Repository: [github.com/walkingIssue/Angy](https://github.com/walkingIssue/Angy)
14
- - Changelog: [CHANGELOG.md](./CHANGELOG.md)
15
- - Roadmap: [docs/roadmap.md](./docs/roadmap.md)
16
- - Wuchale runtime wiring: [docs/wuchale-runtime.md](./docs/wuchale-runtime.md)
17
-
18
- ## What it is
19
-
20
- - A QA widget for in-app translation work
21
- - A PO catalog lookup and commit layer
22
- - A suggestion pipeline for untranslated strings
23
- - A SvelteKit-friendly integration shape
24
-
25
- ## Showcase
26
-
27
- Angy is meant to keep translators and developers inside the app while they work.
28
-
29
- Core loop:
13
+ ## What it does
30
14
 
31
- - select visible copy directly in the UI
32
- - inspect the matched PO key, references, flags, and nearby alternatives
33
- - stage a translation into the working catalog
15
+ - select copy directly in the app
16
+ - resolve the best PO key with nearby alternatives
17
+ - stage and commit translations without leaving the page
34
18
  - request AI suggestions for untranslated strings
35
- - rotate locales to visually QA layout and copy fit
36
- - commit reviewed changes back to the working PO
37
-
38
- Planned repo assets:
39
-
40
- - screenshots in [`docs/images`](./docs/images)
41
- - short workflow recordings once the capture set is ready
42
-
43
- ## UX flow
44
-
45
- ### 1. Capture text in the app
46
-
47
- - select text on the page
48
- - or hold `Alt` and click an interactive element
49
- - Angy opens with the selected string prefilled
50
-
51
- ### 2. Resolve the key
52
-
53
- - Angy looks up the best PO match
54
- - it shows the matched key, context, references, comments, and flags
55
- - it also expands nearby alternatives so repeated copy can be resolved safely
19
+ - rotate locales for visual QA
56
20
 
57
- ### 3. Stage and review
21
+ ## Quick flow
58
22
 
59
- - edit the translation in place
60
- - press `Enter` to stage quickly
61
- - use `Tab` to move through unresolved candidates
62
- - compare target candidates and existing working/base state through the status icons
23
+ 1. Select text or `Alt`+click an interactive element.
24
+ 2. Review the matched key and alternatives.
25
+ 3. Edit or accept a suggestion.
26
+ 4. Stage and commit.
27
+ 5. Rotate locales and verify the page visually.
63
28
 
64
- ### 4. Suggest and commit
29
+ ## Docs site
65
30
 
66
- - ask for AI suggestions when a string is untranslated
67
- - keep or edit the suggestion
68
- - commit reviewed changes into the working catalog
69
-
70
- ### 5. QA in the page
71
-
72
- - use the QA locale toggle to rotate rendered locales
73
- - compare source and target visually
74
- - catch wrapping, spacing, and hierarchy regressions before leaving the page
75
-
76
- ### 6. Promotion workflow
77
-
78
- - if a project is using Angy for migration or catalog promotion work, the rotate action can promote the working catalog into the base catalog
79
- - this is intentionally a specialized workflow, not the everyday translation path
80
- - base vs working catalog design still needs a stricter product pass
81
-
82
- ## Packages used
83
-
84
- - `svelte`
85
- UI for the helper widget
86
- - `@sveltejs/kit`
87
- Route handler shape and dev/server integration
88
- - `wuchale`
89
- Runtime i18n layer and locale switching
90
- - `gettext-parser`
91
- Read and write `.po` catalogs
92
- - `fuse.js`
93
- Fuzzy lookup for key discovery
94
- - `esbuild`
95
- Load `angy.config.ts` in dev and server contexts
96
- - OpenAI API
97
- Optional translation suggestions
98
-
99
- ## What problem it solves
100
-
101
- - Large apps are painful to internationalize late
102
- - Many strings have weak or missing context
103
- - Repeated copy appears in many components
104
- - PO workflows are slow when you must search manually
105
- - Existing i18n libraries solve extraction/runtime, not translator workflow
106
- - Teams need a way to select text in the app, inspect context, and commit safely
107
-
108
- ## Why it exists
109
-
110
- - Plain catalog editing was too slow
111
- - Context from extraction alone was not enough
112
- - Similar strings caused ambiguity
113
- - Repeated strings needed reference-aware lookup
114
- - Suggestion workflows needed to stay inside the app
115
- - A dev-only helper reduced friction enough to keep the migration moving
116
-
117
- ## Workflow
118
-
119
- - Developer runs app in dev
120
- - Developer mounts `<Angy />` in layout
121
- - App exports `POST` from the package handler
122
- - User selects text or uses the helper trigger in the UI
123
- - Client asks server for PO context and alternatives
124
- - Server matches best key, expands related entries, and returns alternatives
125
- - User edits, stages, tabs through unresolved strings, and commits
126
- - Suggestions can be requested for untranslated strings
127
- - Commits are written to a draft working catalog
128
- - Entering `*-working` preview promotes that draft into the runtime working catalog
31
+ - [Showcase and docs](https://walkingissue.github.io/Angy/)
32
+ - [Release notes](./docs/changelog/0.2.19.md)
129
33
 
130
34
  ## Integration
131
35
 
132
- Use the plugin for shared config, then mount the component and define the route yourself.
133
-
134
- The plugin is optional dev glue:
135
-
136
- - injects route/locale defaults into the client
137
- - coordinates watcher behavior during catalog writes
138
- - does not replace the core library/runtime pieces
36
+ Use the plugin for dev glue, mount the component, and expose the server handler.
139
37
 
140
38
  ```ts
141
39
  // vite.config.ts
@@ -159,7 +57,7 @@ export default defineAngyConfig({
159
57
  targetLocale: "en",
160
58
  routePath: "/api/translations",
161
59
  apiKey: "",
162
- watchIgnore: ["**/locales/en-working.po"]
60
+ suggestionModel: { model: "gpt-4.1-mini" }
163
61
  });
164
62
  ```
165
63
 
@@ -168,6 +66,8 @@ export default defineAngyConfig({
168
66
  <script lang="ts">
169
67
  import { dev } from "$app/environment";
170
68
  import { Angy } from "@walkinissue/angy";
69
+
70
+ let { children } = $props();
171
71
  </script>
172
72
 
173
73
  {#if dev}
@@ -184,172 +84,46 @@ export { handler as POST } from "@walkinissue/angy/server";
184
84
 
185
85
  ## Catalog model
186
86
 
187
- - `en.po` is the base catalog
188
- - `en-working.po` is the mutable working catalog
189
- - `en-working.angy-draft.po` is the draft write target during editing
190
- - Base catalog is the source of truth for valid keys
191
- - Working catalog is the source of truth for current translation state
192
- - Lookup reads working state first
193
- - Commit validates against base, then writes to the draft working catalog
194
- - Working preview promotes draft into runtime working
195
-
196
- ## Discovery algorithm
197
-
198
- - User sends selected text and current route path
199
- - Server creates lookup variants from the selected text
200
- - Fuzzy search runs against the effective working view of the catalog
201
- - Best match must clear a score threshold
202
- - Top 4 similar direct matches are kept
203
- - Server infers a page reference like `src/routes/.../+page.svelte`
204
- - It then pulls entries that share PO references with the best match
205
- - It then pulls entries whose references look similar to the current route
206
- - If space remains, it fills with similar unresolved strings
207
- - It also keeps a smaller translated slice for cohesion
208
-
209
- ## Why the lookup works like this
210
-
211
- - Direct fuzzy match alone is not enough
212
- - Repeated UI copy often exists in several components
213
- - Shared PO references are strong local context
214
- - Route similarity helps reconstruct page-level context
215
- - A translated slice helps keep wording consistent
216
- - An untranslated-heavy pool focuses effort where work remains
217
-
218
- ## Retrieval targets
219
-
220
- - Up to 300 alternatives
221
- - Roughly 80% untranslated
222
- - Roughly 20% already translated
223
- - Enough translated context for tone and syntax
224
- - Enough untranslated context for efficient batch work
225
-
226
- ## Suggestions
227
-
228
- - Suggestions are dev-only
229
- - Server-side request to OpenAI
230
- - Prompt aims to preserve placeholders, markup, casing, and product terminology
231
- - Existing translations are used as style anchors
232
- - Suggestions are cached client-side to avoid repeat requests
233
- - Built-in suggestions are disabled when `apiKey` is empty
234
- - Consumers can replace the suggestion pipeline with `suggestionProvider`
87
+ - `en.po` is base
88
+ - `en-working.po` is runtime working state
89
+ - `en-working.angy-draft.po` is the draft write target
90
+ - commits write to draft first
91
+ - entering `*-working` preview promotes draft into runtime working
235
92
 
236
93
  ## Config
237
94
 
238
- | Key | Required | Default | Notes |
239
- | --- | --- | --- | --- |
240
- | `basePoPath` | Yes | None | Path to base catalog |
241
- | `workingPoPath` | Yes | None | Path to working catalog |
242
- | `sourceLocale` | Yes | None | Source language for suggestions. Can be set to `"base"` to infer from `basePoPath` |
243
- | `targetLocale` | Yes | None | Target language for suggestions. Can be set to `"working"` to infer from `workingPoPath` |
244
- | `routePath` | No | `/api/translations` | Route used by the helper client and consumer server handler |
245
- | `apiKey` | Yes | None | Used by built-in suggestion pipeline. If empty, suggestions are disabled |
246
- | `systemMessage` | No | Built from locales | Default AI system prompt |
247
- | `suggestionModel` | No | `gpt-4.1-mini` | Cheap default to avoid cost surprises |
248
- | `watchIgnore` | No | `["**/en-working.po"]` | Extra Vite watch ignore patterns |
249
- | `suggestionProvider` | No | None | Custom suggestion pipeline hook |
250
-
251
- ## Route config
252
-
253
- - The server route path is defined by the consumer app
254
- - The default route path is `/api/translations`
255
- - `routePath` lives in `angy.config.ts`
256
- - The `Angy` `endpoint` prop is optional
257
- - Use the prop only if you want to override `routePath` per instance
258
-
259
- ## Wuchale Runtime
260
-
261
- Angy does not replace Wuchale's runtime setup. The host app still needs to:
262
-
263
- - import the generated client loader
264
- - preload server catalogs
265
- - await `loadLocales(...)`
266
- - wrap requests with `runWithLocale(...)`
267
-
268
- If that wiring is missing, locale rotation can appear broken and Wuchale may warn:
269
-
270
- ```text
271
- Catalog for 'main.main' not found.
272
- Either 'runWithLocale' was not called or the environment has a problem.
273
- ```
274
-
275
- See [docs/wuchale-runtime.md](./docs/wuchale-runtime.md) for a working pattern, including the localStorage-and-cookie sync used in the smoke test.
95
+ | Key | Required | Notes |
96
+ | --- | --- | --- |
97
+ | `basePoPath` | Yes | Base catalog path |
98
+ | `workingPoPath` | Yes | Working catalog path |
99
+ | `sourceLocale` | Yes | Source language for suggestions |
100
+ | `targetLocale` | Yes | Target language for suggestions |
101
+ | `routePath` | No | Defaults to `/api/translations` |
102
+ | `apiKey` | No | Empty string disables built-in suggestions |
103
+ | `systemMessage` | No | Custom suggestion prompt |
104
+ | `suggestionModel` | No | Object config, defaults to `{ model: "gpt-4.1-mini" }` |
105
+ | `watchIgnore` | No | Extra Vite watch ignore patterns |
106
+ | `suggestionProvider` | No | Custom suggestion pipeline |
107
+
108
+ Reasoning is validated per model. Non-reasoning models like `gpt-4.1-mini` do not accept it.
276
109
 
277
110
  ## Frequent issues
278
111
 
279
- - Locale toggle changes in Angy, but the page does not rerender
280
- - Wuchale is usually not fully bootstrapped in the host app yet. Check the generated loader imports, awaited `loadLocales(...)`, and server locale wiring first.
281
- - Working locale is visible in the QA rotation, but edit/commit controls are disabled
282
- - This is intentional. `*-working` is preview-only so Angy does not write while the working catalog is the actively rendered runtime source.
112
+ - Locale changes in Angy, but the page does not rerender
113
+ - Wuchale usually is not fully bootstrapped in the host app.
114
+ - `*-working` is visible, but commit is disabled
115
+ - Working locale is preview-only by design.
283
116
  - Rotation shows a warning modal
284
- - This is a preview of the working-vs-base differences that rotation will promote into base. It is not the same thing as a catalog integrity failure.
117
+ - That is a preview of what rotation will promote into base.
285
118
  - Angy refuses to operate with a catalog integrity error
286
- - The working catalog no longer matches the base key set, or it is missing translations that base already has. Regenerate or replace the working catalog before continuing.
287
- - Commit succeeded, but the helper still looks unsure
288
- - Angy now keeps local drafts until the server truth matches the committed value. If Wuchale/Vite reloads at a bad moment, your staged edits stay cached instead of being silently discarded.
289
-
290
- ## Custom suggestion provider
291
-
292
- Use `suggestionProvider` if you want your own AI pipeline.
293
-
294
- Input:
295
-
296
- - `context`
297
- - `items`
298
- - `sourceLocale`
299
- - `targetLocale`
300
- - `systemMessage`
301
- - `model`
302
- - `apiKey`
303
-
304
- Return:
305
-
306
- - `Array<{ msgid: string; msgctxt: string | null; suggestion: string }>`
307
-
308
- Example:
309
-
310
- ```ts
311
- import { defineAngyConfig, type SuggestionProvider } from "@walkinissue/angy/server";
312
-
313
- const suggestionProvider: SuggestionProvider = async ({ items }) => {
314
- return items.map((item) => ({
315
- msgid: item.msgid,
316
- msgctxt: item.msgctxt,
317
- suggestion: `TODO: ${item.msgid}`
318
- }));
319
- };
320
-
321
- export default defineAngyConfig({
322
- basePoPath: "./src/locales/en.po",
323
- workingPoPath: "./src/locales/en-working.po",
324
- sourceLocale: "sv",
325
- targetLocale: "en",
326
- routePath: "/api/translations",
327
- apiKey: "",
328
- suggestionProvider
329
- });
330
- ```
331
-
332
- ## Exports
333
-
334
- - `Angy` from `@walkinissue/angy`
335
- - `handler` and `defineAngyConfig` from `@walkinissue/angy/server`
336
- - `angy` from `@walkinissue/angy/plugin`
119
+ - Regenerate or replace the working catalog.
337
120
 
338
- ## Notes
121
+ ## Docs
339
122
 
340
- - The default `handler` returns a `404` outside dev.
341
- - The package still has fallback internal paths, but consumers should define their own explicit catalog paths.
342
- - Override paths, locales, `routePath`, `apiKey`, `systemMessage`, `suggestionModel`, `watchIgnore`, and `suggestionProvider` through `angy.config.ts`.
343
- - The default export surface is intentionally small: plugin, component, config helper, and server handler.
123
+ - [Release notes](./docs/changelog/0.2.19.md)
124
+ - [Roadmap](./docs/roadmap.md)
125
+ - [Wuchale runtime wiring](./docs/wuchale-runtime.md)
344
126
 
345
- ## Future work
127
+ ## License
346
128
 
347
- - Handle server paths for single config and single app
348
- - Support multiple catalogs
349
- - Support multiple translations from a single source locale
350
- - Add a compact focus mode so the helper can hide dead detail, keep a smaller matched-key summary, and only show the active alternative by default
351
- - Revisit how commit feedback is surfaced so successful or failed commits stay visible without making the helper flow awkward
352
- - Improve cross-page reference browsing, likely through a popup/details surface instead of always-visible reference noise
353
- - Make working-locale rotation trustworthy and visually explicit so users can tell when the app is actually rendering the working catalog
354
- - Harden the base-vs-working catalog design and decide how much of promotion/rotation should be exposed in the default UX
355
- - This seems trivially implemented with the current setup
129
+ MIT
package/dist/plugin.js CHANGED
@@ -11,6 +11,30 @@ var CONFIG_FILENAMES = [
11
11
  "angy.config.mjs",
12
12
  "angy.config.cjs"
13
13
  ];
14
+ var NON_REASONING_SUGGESTION_MODELS = [
15
+ "gpt-4.1",
16
+ "gpt-4.1-mini",
17
+ "gpt-4.1-nano"
18
+ ];
19
+ var GPT54_REASONING_EFFORTS = ["none", "low", "medium", "high", "xhigh"];
20
+ var GPT51_REASONING_EFFORTS = ["none", "low", "medium", "high"];
21
+ var GPT5_REASONING_EFFORTS = ["minimal", "low", "medium", "high"];
22
+ var GPT5_PRO_REASONING_EFFORTS = ["high"];
23
+ var GPT5X_PRO_REASONING_EFFORTS = ["medium", "high", "xhigh"];
24
+ var REASONING_EFFORTS_BY_MODEL = {
25
+ "gpt-5.4": GPT54_REASONING_EFFORTS,
26
+ "gpt-5.4-mini": GPT54_REASONING_EFFORTS,
27
+ "gpt-5.4-nano": GPT54_REASONING_EFFORTS,
28
+ "gpt-5.2": GPT54_REASONING_EFFORTS,
29
+ "gpt-5.1": GPT51_REASONING_EFFORTS,
30
+ "gpt-5": GPT5_REASONING_EFFORTS,
31
+ "gpt-5-pro": GPT5_PRO_REASONING_EFFORTS,
32
+ "gpt-5.2-pro": GPT5X_PRO_REASONING_EFFORTS,
33
+ "gpt-5.4-pro": GPT5X_PRO_REASONING_EFFORTS
34
+ };
35
+ var DEFAULT_SUGGESTION_MODEL = {
36
+ model: "gpt-4.1-mini"
37
+ };
14
38
  function buildDefaultSystemMessage(sourceLocale, targetLocale) {
15
39
  return `You are an expert product localization assistant translating ${sourceLocale} UI copy into polished ${targetLocale}. Keep already-${targetLocale} text unchanged. Preserve placeholders like {0}, {1}, ICU fragments, HTML-like markers such as <0/> and <strong>, line breaks, punctuation, capitalization, and button-like brevity. Prefer terminology and syntax that stay close to the translated examples so the product voice remains cohesive. Do not invent extra context, do not expand abbreviations unless the examples already do, and avoid semantic drift. Return only high-confidence translation suggestions for the provided untranslated strings.`;
16
40
  }
@@ -37,6 +61,51 @@ function validateSuggestionProvider(suggestionProvider) {
37
61
  throw new Error(`[angy] suggestionProvider must be a function.`);
38
62
  }
39
63
  }
64
+ function isNonReasoningSuggestionModel(model) {
65
+ return NON_REASONING_SUGGESTION_MODELS.includes(model);
66
+ }
67
+ function isSupportedSuggestionModel(model) {
68
+ return isNonReasoningSuggestionModel(model) || Object.prototype.hasOwnProperty.call(REASONING_EFFORTS_BY_MODEL, model);
69
+ }
70
+ function normalizeSuggestionModelConfig(input) {
71
+ const suggestionModel = input ?? DEFAULT_SUGGESTION_MODEL;
72
+ if (typeof suggestionModel !== "object" || suggestionModel == null || Array.isArray(suggestionModel)) {
73
+ throw new Error(
74
+ `[angy] suggestionModel must be an object like { model: "gpt-4.1-mini" }.`
75
+ );
76
+ }
77
+ if (typeof suggestionModel.model !== "string" || !suggestionModel.model.trim()) {
78
+ throw new Error(`[angy] suggestionModel.model is required and must be a non-empty string.`);
79
+ }
80
+ if (!isSupportedSuggestionModel(suggestionModel.model)) {
81
+ throw new Error(`[angy] Unsupported suggestionModel.model "${suggestionModel.model}".`);
82
+ }
83
+ if (isNonReasoningSuggestionModel(suggestionModel.model)) {
84
+ if (!("reasoning" in suggestionModel) || suggestionModel.reasoning === void 0 || suggestionModel.reasoning === null) {
85
+ return { model: suggestionModel.model, reasoning: null };
86
+ }
87
+ throw new Error(
88
+ `[angy] suggestionModel.reasoning is not supported for ${suggestionModel.model}.`
89
+ );
90
+ }
91
+ const allowedReasoning = REASONING_EFFORTS_BY_MODEL[suggestionModel.model];
92
+ const reasoning = suggestionModel.reasoning;
93
+ if (reasoning == null) {
94
+ if (suggestionModel.model === "gpt-5-pro") {
95
+ return { model: suggestionModel.model, reasoning: "high" };
96
+ }
97
+ return { model: suggestionModel.model };
98
+ }
99
+ if (!allowedReasoning.includes(reasoning)) {
100
+ throw new Error(
101
+ `[angy] suggestionModel.reasoning "${reasoning}" is not supported for ${suggestionModel.model}.`
102
+ );
103
+ }
104
+ return {
105
+ model: suggestionModel.model,
106
+ reasoning
107
+ };
108
+ }
40
109
  var config = {
41
110
  basePoPath: resolve(__dirname, "../../locales/en.po"),
42
111
  workingPoPath: resolve(__dirname, "../../locales/en-working.po"),
@@ -45,7 +114,7 @@ var config = {
45
114
  routePath: "/api/translations",
46
115
  apiKey: "",
47
116
  systemMessage: buildDefaultSystemMessage("sv", "en"),
48
- suggestionModel: "gpt-4.1-mini",
117
+ suggestionModel: DEFAULT_SUGGESTION_MODEL,
49
118
  watchIgnore: ["**/en-working.po"]
50
119
  };
51
120
  var loadedConfigRoot = null;
@@ -149,7 +218,7 @@ function completeAngyConfig(input) {
149
218
  routePath: input.routePath ?? "/api/translations",
150
219
  apiKey: input.apiKey ?? "",
151
220
  systemMessage: input.systemMessage ?? buildDefaultSystemMessage(input.sourceLocale, input.targetLocale),
152
- suggestionModel: input.suggestionModel ?? "gpt-4.1-mini",
221
+ suggestionModel: normalizeSuggestionModelConfig(input.suggestionModel),
153
222
  watchIgnore: input.watchIgnore ?? ["**/en-working.po"],
154
223
  suggestionProvider: input.suggestionProvider
155
224
  };
package/dist/server.d.ts CHANGED
@@ -56,7 +56,7 @@ export type SuggestionProviderInput = {
56
56
  sourceLocale: string;
57
57
  targetLocale: string;
58
58
  systemMessage: string;
59
- model: string;
59
+ suggestionModel: SuggestionModelConfig;
60
60
  apiKey: string | undefined;
61
61
  };
62
62
 
@@ -72,11 +72,36 @@ export type AngyConfigInput = {
72
72
  routePath?: string;
73
73
  apiKey?: string;
74
74
  systemMessage?: string;
75
- suggestionModel?: string;
75
+ suggestionModel?: SuggestionModelConfig;
76
76
  watchIgnore?: string[];
77
77
  suggestionProvider?: SuggestionProvider;
78
78
  };
79
79
 
80
+ export type SuggestionModelConfig =
81
+ | { model: "gpt-4.1"; reasoning?: null }
82
+ | { model: "gpt-4.1-mini"; reasoning?: null }
83
+ | { model: "gpt-4.1-nano"; reasoning?: null }
84
+ | {
85
+ model: "gpt-5.4" | "gpt-5.4-mini" | "gpt-5.4-nano" | "gpt-5.2";
86
+ reasoning?: "none" | "low" | "medium" | "high" | "xhigh";
87
+ }
88
+ | {
89
+ model: "gpt-5.1";
90
+ reasoning?: "none" | "low" | "medium" | "high";
91
+ }
92
+ | {
93
+ model: "gpt-5";
94
+ reasoning?: "minimal" | "low" | "medium" | "high";
95
+ }
96
+ | {
97
+ model: "gpt-5-pro";
98
+ reasoning?: "high";
99
+ }
100
+ | {
101
+ model: "gpt-5.2-pro" | "gpt-5.4-pro";
102
+ reasoning?: "medium" | "high" | "xhigh";
103
+ };
104
+
80
105
  export type AngyResolvedConfig = {
81
106
  basePoPath: string;
82
107
  workingPoPath: string;
@@ -85,7 +110,7 @@ export type AngyResolvedConfig = {
85
110
  routePath: string;
86
111
  apiKey: string;
87
112
  systemMessage: string;
88
- suggestionModel: string;
113
+ suggestionModel: SuggestionModelConfig;
89
114
  watchIgnore: string[];
90
115
  suggestionProvider?: SuggestionProvider;
91
116
  };
package/dist/server.js CHANGED
@@ -25,6 +25,30 @@ var CONFIG_FILENAMES = [
25
25
  "angy.config.mjs",
26
26
  "angy.config.cjs"
27
27
  ];
28
+ var NON_REASONING_SUGGESTION_MODELS = [
29
+ "gpt-4.1",
30
+ "gpt-4.1-mini",
31
+ "gpt-4.1-nano"
32
+ ];
33
+ var GPT54_REASONING_EFFORTS = ["none", "low", "medium", "high", "xhigh"];
34
+ var GPT51_REASONING_EFFORTS = ["none", "low", "medium", "high"];
35
+ var GPT5_REASONING_EFFORTS = ["minimal", "low", "medium", "high"];
36
+ var GPT5_PRO_REASONING_EFFORTS = ["high"];
37
+ var GPT5X_PRO_REASONING_EFFORTS = ["medium", "high", "xhigh"];
38
+ var REASONING_EFFORTS_BY_MODEL = {
39
+ "gpt-5.4": GPT54_REASONING_EFFORTS,
40
+ "gpt-5.4-mini": GPT54_REASONING_EFFORTS,
41
+ "gpt-5.4-nano": GPT54_REASONING_EFFORTS,
42
+ "gpt-5.2": GPT54_REASONING_EFFORTS,
43
+ "gpt-5.1": GPT51_REASONING_EFFORTS,
44
+ "gpt-5": GPT5_REASONING_EFFORTS,
45
+ "gpt-5-pro": GPT5_PRO_REASONING_EFFORTS,
46
+ "gpt-5.2-pro": GPT5X_PRO_REASONING_EFFORTS,
47
+ "gpt-5.4-pro": GPT5X_PRO_REASONING_EFFORTS
48
+ };
49
+ var DEFAULT_SUGGESTION_MODEL = {
50
+ model: "gpt-4.1-mini"
51
+ };
28
52
  function buildDefaultSystemMessage(sourceLocale, targetLocale) {
29
53
  return `You are an expert product localization assistant translating ${sourceLocale} UI copy into polished ${targetLocale}. Keep already-${targetLocale} text unchanged. Preserve placeholders like {0}, {1}, ICU fragments, HTML-like markers such as <0/> and <strong>, line breaks, punctuation, capitalization, and button-like brevity. Prefer terminology and syntax that stay close to the translated examples so the product voice remains cohesive. Do not invent extra context, do not expand abbreviations unless the examples already do, and avoid semantic drift. Return only high-confidence translation suggestions for the provided untranslated strings.`;
30
54
  }
@@ -51,6 +75,51 @@ function validateSuggestionProvider(suggestionProvider) {
51
75
  throw new Error(`[angy] suggestionProvider must be a function.`);
52
76
  }
53
77
  }
78
+ function isNonReasoningSuggestionModel(model) {
79
+ return NON_REASONING_SUGGESTION_MODELS.includes(model);
80
+ }
81
+ function isSupportedSuggestionModel(model) {
82
+ return isNonReasoningSuggestionModel(model) || Object.prototype.hasOwnProperty.call(REASONING_EFFORTS_BY_MODEL, model);
83
+ }
84
+ function normalizeSuggestionModelConfig(input) {
85
+ const suggestionModel = input ?? DEFAULT_SUGGESTION_MODEL;
86
+ if (typeof suggestionModel !== "object" || suggestionModel == null || Array.isArray(suggestionModel)) {
87
+ throw new Error(
88
+ `[angy] suggestionModel must be an object like { model: "gpt-4.1-mini" }.`
89
+ );
90
+ }
91
+ if (typeof suggestionModel.model !== "string" || !suggestionModel.model.trim()) {
92
+ throw new Error(`[angy] suggestionModel.model is required and must be a non-empty string.`);
93
+ }
94
+ if (!isSupportedSuggestionModel(suggestionModel.model)) {
95
+ throw new Error(`[angy] Unsupported suggestionModel.model "${suggestionModel.model}".`);
96
+ }
97
+ if (isNonReasoningSuggestionModel(suggestionModel.model)) {
98
+ if (!("reasoning" in suggestionModel) || suggestionModel.reasoning === void 0 || suggestionModel.reasoning === null) {
99
+ return { model: suggestionModel.model, reasoning: null };
100
+ }
101
+ throw new Error(
102
+ `[angy] suggestionModel.reasoning is not supported for ${suggestionModel.model}.`
103
+ );
104
+ }
105
+ const allowedReasoning = REASONING_EFFORTS_BY_MODEL[suggestionModel.model];
106
+ const reasoning = suggestionModel.reasoning;
107
+ if (reasoning == null) {
108
+ if (suggestionModel.model === "gpt-5-pro") {
109
+ return { model: suggestionModel.model, reasoning: "high" };
110
+ }
111
+ return { model: suggestionModel.model };
112
+ }
113
+ if (!allowedReasoning.includes(reasoning)) {
114
+ throw new Error(
115
+ `[angy] suggestionModel.reasoning "${reasoning}" is not supported for ${suggestionModel.model}.`
116
+ );
117
+ }
118
+ return {
119
+ model: suggestionModel.model,
120
+ reasoning
121
+ };
122
+ }
54
123
  var config = {
55
124
  basePoPath: resolve(__dirname, "../../locales/en.po"),
56
125
  workingPoPath: resolve(__dirname, "../../locales/en-working.po"),
@@ -59,7 +128,7 @@ var config = {
59
128
  routePath: "/api/translations",
60
129
  apiKey: "",
61
130
  systemMessage: buildDefaultSystemMessage("sv", "en"),
62
- suggestionModel: "gpt-4.1-mini",
131
+ suggestionModel: DEFAULT_SUGGESTION_MODEL,
63
132
  watchIgnore: ["**/en-working.po"]
64
133
  };
65
134
  var loadedConfigRoot = null;
@@ -169,7 +238,7 @@ function completeAngyConfig(input) {
169
238
  routePath: input.routePath ?? "/api/translations",
170
239
  apiKey: input.apiKey ?? "",
171
240
  systemMessage: input.systemMessage ?? buildDefaultSystemMessage(input.sourceLocale, input.targetLocale),
172
- suggestionModel: input.suggestionModel ?? "gpt-4.1-mini",
241
+ suggestionModel: normalizeSuggestionModelConfig(input.suggestionModel),
173
242
  watchIgnore: input.watchIgnore ?? ["**/en-working.po"],
174
243
  suggestionProvider: input.suggestionProvider
175
244
  };
@@ -1189,6 +1258,58 @@ function parseSuggestions(responseText) {
1189
1258
  return [];
1190
1259
  }
1191
1260
  }
1261
+ function buildSuggestionRequestBody({
1262
+ context,
1263
+ items,
1264
+ modelConfig,
1265
+ systemMessage
1266
+ }) {
1267
+ const body = {
1268
+ model: modelConfig.model,
1269
+ input: [
1270
+ {
1271
+ role: "system",
1272
+ content: [{ type: "input_text", text: systemMessage }]
1273
+ },
1274
+ {
1275
+ role: "user",
1276
+ content: [{ type: "input_text", text: buildUserMessage(context, items) }]
1277
+ }
1278
+ ],
1279
+ text: {
1280
+ format: {
1281
+ type: "json_schema",
1282
+ name: "translation_suggestions",
1283
+ schema: {
1284
+ type: "object",
1285
+ additionalProperties: false,
1286
+ properties: {
1287
+ items: {
1288
+ type: "array",
1289
+ items: {
1290
+ type: "object",
1291
+ additionalProperties: false,
1292
+ properties: {
1293
+ msgid: { type: "string" },
1294
+ msgctxt: { type: ["string", "null"] },
1295
+ suggestion: { type: "string" }
1296
+ },
1297
+ required: ["msgid", "msgctxt", "suggestion"]
1298
+ }
1299
+ }
1300
+ },
1301
+ required: ["items"]
1302
+ }
1303
+ }
1304
+ }
1305
+ };
1306
+ if (modelConfig.reasoning && modelConfig.reasoning !== "none") {
1307
+ body.reasoning = {
1308
+ effort: modelConfig.reasoning
1309
+ };
1310
+ }
1311
+ return body;
1312
+ }
1192
1313
  async function handleSuggestions(request) {
1193
1314
  const {
1194
1315
  apiKey,
@@ -1217,7 +1338,7 @@ async function handleSuggestions(request) {
1217
1338
  sourceLocale,
1218
1339
  targetLocale,
1219
1340
  systemMessage,
1220
- model: suggestionModel,
1341
+ suggestionModel,
1221
1342
  apiKey
1222
1343
  });
1223
1344
  return json3({
@@ -1235,7 +1356,8 @@ async function handleSuggestions(request) {
1235
1356
  }
1236
1357
  try {
1237
1358
  console.info("[angy] Suggestion request starting.", {
1238
- model: suggestionModel,
1359
+ model: suggestionModel.model,
1360
+ reasoning: suggestionModel.reasoning ?? null,
1239
1361
  sourceLocale,
1240
1362
  targetLocale,
1241
1363
  itemCount: items.length,
@@ -1247,48 +1369,14 @@ async function handleSuggestions(request) {
1247
1369
  Authorization: `Bearer ${apiKey}`,
1248
1370
  "content-type": "application/json"
1249
1371
  },
1250
- body: JSON.stringify({
1251
- model: suggestionModel,
1252
- reasoning: {
1253
- effort: "medium"
1254
- },
1255
- input: [
1256
- {
1257
- role: "system",
1258
- content: [{ type: "input_text", text: systemMessage }]
1259
- },
1260
- {
1261
- role: "user",
1262
- content: [{ type: "input_text", text: buildUserMessage(context, items) }]
1263
- }
1264
- ],
1265
- text: {
1266
- format: {
1267
- type: "json_schema",
1268
- name: "translation_suggestions",
1269
- schema: {
1270
- type: "object",
1271
- additionalProperties: false,
1272
- properties: {
1273
- items: {
1274
- type: "array",
1275
- items: {
1276
- type: "object",
1277
- additionalProperties: false,
1278
- properties: {
1279
- msgid: { type: "string" },
1280
- msgctxt: { type: ["string", "null"] },
1281
- suggestion: { type: "string" }
1282
- },
1283
- required: ["msgid", "msgctxt", "suggestion"]
1284
- }
1285
- }
1286
- },
1287
- required: ["items"]
1288
- }
1289
- }
1290
- }
1291
- })
1372
+ body: JSON.stringify(
1373
+ buildSuggestionRequestBody({
1374
+ context,
1375
+ items,
1376
+ modelConfig: suggestionModel,
1377
+ systemMessage
1378
+ })
1379
+ )
1292
1380
  });
1293
1381
  if (!response.ok) {
1294
1382
  const errorBody = await response.text().catch(() => "");
@@ -1369,12 +1457,14 @@ function createAngyHandler(options) {
1369
1457
  var handler = createAngyHandler();
1370
1458
  export {
1371
1459
  CatalogIntegrityError,
1460
+ buildSuggestionRequestBody,
1372
1461
  collectCatalogIntegrityIssues,
1373
1462
  collectRotationImpact,
1374
1463
  defineAngyConfig,
1375
1464
  getRotationPreflight,
1376
1465
  handleTranslationRequest,
1377
1466
  handler,
1467
+ normalizeSuggestionModelConfig,
1378
1468
  readCatalogPair,
1379
1469
  resolveTranslationState
1380
1470
  };
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/lib/server/route.ts", "../src/lib/server/commit.ts", "../src/lib/server/catalog.ts", "../src/lib/server/config.ts", "../src/lib/server/context.ts", "../src/lib/server/suggestions.ts"],
4
- "sourcesContent": ["import { json, type RequestHandler } from \"@sveltejs/kit\";\nimport {\n\thandleCommit,\n\thandleCommitBatch,\n\thandlePromoteWorkingPreview,\n\thandleRotateCatalogs,\n\thandleRotatePreflight\n} from \"./commit.ts\";\nimport {\n\tconfigureTranslationHelper,\n\tloadAngyConfigFromRoot,\n\tnormalizeTranslationHelperConfig,\n\ttype TranslationHelperConfig\n} from \"./config.ts\";\nimport { handleContext } from \"./context.ts\";\nimport { handleSuggestions } from \"./suggestions.ts\";\n\nexport async function handleTranslationRequest(\n\trequest: Request,\n\turl: URL,\n\toptions?: { devOnly?: boolean; config?: Partial<TranslationHelperConfig>; dev?: boolean }\n) {\n\tconst devOnly = options?.devOnly ?? true;\n\tconst runtimeDev =\n\t\ttypeof import.meta !== \"undefined\" &&\n\t\ttypeof import.meta.env !== \"undefined\" &&\n\t\timport.meta.env.DEV === true;\n\tconst isDev = options?.dev ?? runtimeDev;\n\n\tif (devOnly && !isDev) {\n\t\treturn json(\n\t\t\t{\n\t\t\t\tsuccess: false,\n\t\t\t\terror: \"Disabled outside dev\"\n\t\t\t},\n\t\t\t{ status: 404 }\n\t\t);\n\t}\n\n\tif (options?.config) {\n\t\tconfigureTranslationHelper(normalizeTranslationHelperConfig(process.cwd(), options.config));\n\t} else {\n\t\tawait loadAngyConfigFromRoot(process.cwd());\n\t}\n\n\tconst intent = url.searchParams.get(\"intent\") ?? \"commit\";\n\n\tif (intent === \"commit-batch\") {\n\t\treturn handleCommitBatch(request);\n\t}\n\n\tif (intent === \"context\") {\n\t\treturn handleContext(request);\n\t}\n\n\tif (intent === \"suggestions\") {\n\t\treturn handleSuggestions(request);\n\t}\n\n\tif (intent === \"rotate-catalogs\") {\n\t\treturn handleRotateCatalogs(request);\n\t}\n\n\tif (intent === \"rotate-preflight\") {\n\t\treturn handleRotatePreflight();\n\t}\n\n\tif (intent === \"promote-working-preview\") {\n\t\treturn handlePromoteWorkingPreview();\n\t}\n\n\treturn handleCommit(request);\n}\n\nfunction createAngyHandler(\n\toptions?: { devOnly?: boolean; config?: Partial<TranslationHelperConfig> }\n): RequestHandler {\n\treturn async ({ request, url }) => handleTranslationRequest(request, url, options);\n}\n\nexport const handler = createAngyHandler();\n\nexport {\n\tdefineAngyConfig,\n\ttype AngyConfigInput,\n\ttype SuggestionProvider,\n\ttype SuggestionProviderInput,\n\ttype SuggestionRequestItem,\n\ttype SuggestionResponseItem\n} from \"./config.ts\";\nexport {\n\tCatalogIntegrityError,\n\tcollectCatalogIntegrityIssues,\n\tcollectRotationImpact,\n\tgetRotationPreflight,\n\treadCatalogPair,\n\tresolveTranslationState\n} from \"./catalog.ts\";\n", "import { json } from \"@sveltejs/kit\";\nimport { basename } from \"node:path\";\nimport {\n\tCatalogIntegrityError,\n\tgetRotationPreflight,\n\tensureWorkingCatalog,\n\tpromoteWorkingDraftToRuntime,\n\treadCatalogPair,\n\treadParsedCatalog,\n\tremoveFuzzyFlag,\n\trotateCatalogs,\n\truntimeTranslations,\n\twriteWorkingCatalog\n} from \"./catalog.ts\";\nimport type { CommitBatchItem, PoTranslationEntry } from \"./types.ts\";\nimport { getTranslationHelperConfig, inferLocaleFromCatalogPath } from \"./config.ts\";\n\r\nfunction runtimeKey(msgid: string, msgctxt: string | null) {\r\n\treturn `${msgctxt ?? \"\"}::${msgid}`;\r\n}\r\n\r\nexport async function writeTranslationToWorkingCatalog(\n\tresolvedMsgid: string,\n\tresolvedMsgctxt: string | null,\n\ttranslationValue: string\n) {\n\tawait ensureWorkingCatalog();\n\n\tawait readCatalogPair({ ensureWorking: true });\n\n\tconst [baseParsed, workingParsed] = await Promise.all([readParsedCatalog(\"base\"), readParsedCatalog(\"working\")]);\n\n\tif (!baseParsed || !workingParsed) {\n\t\treturn { ok: false as const, error: \"Unable to load catalogs\" };\n\t}\n\r\n\tconst baseTranslations = baseParsed.translations ?? {};\r\n\tconst workingTranslations = workingParsed.translations ?? {};\r\n\r\n\tconst ctxKey = resolvedMsgctxt ?? \"\";\r\n\tconst baseGroup = baseTranslations[ctxKey];\r\n\tif (!baseGroup) {\r\n\t\treturn { ok: false as const, error: \"Context group not found in base catalog\" };\r\n\t}\r\n\r\n\tconst baseEntry = baseGroup[resolvedMsgid] as PoTranslationEntry | undefined;\r\n\tif (!baseEntry) {\r\n\t\treturn { ok: false as const, error: \"Target msgid not found in base catalog\" };\r\n\t}\r\n\r\n\tconst workingGroup = (workingTranslations[ctxKey] ??= {});\r\n\tconst entry = ((workingGroup[resolvedMsgid] as PoTranslationEntry | undefined) ??=\r\n\t\tcloneEntryForWorking(baseEntry, resolvedMsgid, resolvedMsgctxt));\r\n\r\n\tentry.msgstr = [translationValue];\r\n\tentry.comments ??= {};\r\n\tentry.comments.flag = removeFuzzyFlag(entry.comments.flag);\r\n\r\n\tawait writeWorkingCatalog(workingParsed);\r\n\r\n\truntimeTranslations.set(runtimeKey(resolvedMsgid, resolvedMsgctxt), translationValue);\n\n\tconst workingCatalogName = inferLocaleFromCatalogPath(getTranslationHelperConfig().workingPoPath) ?? \"working\";\n\n\treturn {\n\t\tok: true as const,\n\t\tmsgid: resolvedMsgid,\n\t\tmsgctxt: resolvedMsgctxt,\n\t\tworkingCatalog: workingCatalogName\n\t};\n}\n\r\nfunction cloneEntryForWorking(\r\n\tbaseEntry: PoTranslationEntry,\r\n\tmsgid: string,\r\n\tmsgctxt: string | null\r\n): PoTranslationEntry {\r\n\treturn {\r\n\t\tmsgid,\r\n\t\tmsgctxt: msgctxt ?? undefined,\r\n\t\tmsgid_plural: baseEntry.msgid_plural,\r\n\t\tmsgstr: Array.isArray(baseEntry.msgstr) ? [...baseEntry.msgstr] : [\"\"],\r\n\t\tobsolete: Boolean(baseEntry.obsolete),\r\n\t\tcomments: baseEntry.comments\r\n\t\t\t? {\r\n\t\t\t\t\treference: baseEntry.comments.reference,\r\n\t\t\t\t\textracted: baseEntry.comments.extracted,\r\n\t\t\t\t\tflag: baseEntry.comments.flag,\r\n\t\t\t\t\tprevious: baseEntry.comments.previous\r\n\t\t\t\t}\r\n\t\t\t: undefined\r\n\t};\r\n}\r\n\r\nexport async function handleCommitBatch(request: Request) {\n\tconst data = await request.json().catch(() => null);\n\tconst items = Array.isArray(data?.items) ? (data.items as CommitBatchItem[]) : [];\n\r\n\tif (!items.length) {\n\t\treturn json(\r\n\t\t\t{\r\n\t\t\t\tsuccess: false,\r\n\t\t\t\terror: \"No items to commit\"\r\n\t\t\t},\r\n\t\t\t{ status: 400 }\r\n\t\t);\r\n\t}\n\n\ttry {\n\t\tawait readCatalogPair({ ensureWorking: true });\n\t} catch (error) {\n\t\tif (error instanceof CatalogIntegrityError) {\n\t\t\treturn json(\n\t\t\t\t{\n\t\t\t\t\tsuccess: false,\n\t\t\t\t\terror: error.message,\n\t\t\t\t\tcode: \"catalog_integrity_error\",\n\t\t\t\t\tissues: error.issues\n\t\t\t\t},\n\t\t\t\t{ status: 409 }\n\t\t\t);\n\t\t}\n\n\t\tthrow error;\n\t}\n\n\tconst results: Array<{ msgid: string; msgctxt: string | null; ok: boolean; error?: string }> = [];\n\r\n\tfor (const item of items) {\r\n\t\tconst resolvedMsgid = item.resolvedMsgid?.trim();\r\n\t\tconst resolvedMsgctxt =\r\n\t\t\titem.resolvedMsgctxt && item.resolvedMsgctxt.trim().length > 0\r\n\t\t\t\t? item.resolvedMsgctxt.trim()\r\n\t\t\t\t: null;\r\n\t\tconst translationValue = item.translationValue?.trim();\r\n\r\n\t\tif (!resolvedMsgid || !translationValue) {\r\n\t\t\tresults.push({\r\n\t\t\t\tmsgid: resolvedMsgid ?? \"\",\r\n\t\t\t\tmsgctxt: resolvedMsgctxt,\r\n\t\t\t\tok: false,\r\n\t\t\t\terror: \"resolvedMsgid and translationValue are required\"\r\n\t\t\t});\r\n\t\t\tcontinue;\r\n\t\t}\r\n\r\n\t\tconst result = await writeTranslationToWorkingCatalog(\r\n\t\t\tresolvedMsgid,\r\n\t\t\tresolvedMsgctxt,\r\n\t\t\ttranslationValue\r\n\t\t);\r\n\r\n\t\tif (!result.ok) {\r\n\t\t\tresults.push({\r\n\t\t\t\tmsgid: resolvedMsgid,\r\n\t\t\t\tmsgctxt: resolvedMsgctxt,\r\n\t\t\t\tok: false,\r\n\t\t\t\terror: result.error\r\n\t\t\t});\r\n\t\t\tcontinue;\r\n\t\t}\r\n\r\n\t\tresults.push({\r\n\t\t\tmsgid: resolvedMsgid,\r\n\t\t\tmsgctxt: resolvedMsgctxt,\r\n\t\t\tok: true\r\n\t\t});\r\n\t}\r\n\r\n\tconst failed = results.filter((item) => !item.ok);\r\n\tif (failed.length) {\r\n\t\treturn json(\r\n\t\t\t{\r\n\t\t\t\tsuccess: false,\r\n\t\t\t\terror: \"Some translations failed to commit\",\r\n\t\t\t\tresults\r\n\t\t\t},\r\n\t\t\t{ status: 207 }\r\n\t\t);\r\n\t}\r\n\r\n\treturn json({\n\t\tsuccess: true,\n\t\tmessage: `Committed ${results.length} translations to Angy draft working catalog`,\n\t\tresults\n\t});\n}\n\r\nexport async function handleCommit(request: Request) {\n\tconst data = await request.formData();\r\n\tconst resolvedMsgid = data.get(\"resolvedMsgid\")?.toString().trim();\r\n\tconst resolvedMsgctxtRaw = data.get(\"resolvedMsgctxt\")?.toString();\r\n\tconst translationValue = data.get(\"translationValue\")?.toString().trim();\r\n\tconst resolvedMsgctxt =\r\n\t\tresolvedMsgctxtRaw && resolvedMsgctxtRaw.trim().length > 0\r\n\t\t\t? resolvedMsgctxtRaw.trim()\r\n\t\t\t: null;\r\n\r\n\tif (!resolvedMsgid || !translationValue) {\r\n\t\treturn json(\r\n\t\t\t{\r\n\t\t\t\tsuccess: false,\r\n\t\t\t\terror: \"resolvedMsgid and translationValue are required\"\r\n\t\t\t},\r\n\t\t\t{ status: 400 }\r\n\t\t);\r\n\t}\r\n\r\n\tconst result = await writeTranslationToWorkingCatalog(\n\t\tresolvedMsgid,\n\t\tresolvedMsgctxt,\n\t\ttranslationValue\n\t);\n\r\n\tif (!result.ok) {\r\n\t\treturn json(\r\n\t\t\t{\r\n\t\t\t\tsuccess: false,\r\n\t\t\t\terror: result.error\r\n\t\t\t},\r\n\t\t\t{ status: 404 }\r\n\t\t);\r\n\t}\r\n\r\n\treturn json({\n\t\tsuccess: true,\n\t\tmessage: \"Translation written to Angy draft working catalog\",\n\t\tmsgid: result.msgid,\n\t\tmsgctxt: result.msgctxt,\n\t\tworkingCatalog: result.workingCatalog\n\t});\n}\n\nexport async function handlePromoteWorkingPreview() {\n\ttry {\n\t\tawait readCatalogPair({ ensureWorking: true });\n\t\tawait promoteWorkingDraftToRuntime();\n\n\t\treturn json({\n\t\t\tsuccess: true,\n\t\t\tmessage: `Promoted Angy draft into ${basename(getTranslationHelperConfig().workingPoPath)}`\n\t\t});\n\t} catch (error) {\n\t\tif (error instanceof CatalogIntegrityError) {\n\t\t\treturn json(\n\t\t\t\t{\n\t\t\t\t\tsuccess: false,\n\t\t\t\t\terror: error.message,\n\t\t\t\t\tcode: \"catalog_integrity_error\",\n\t\t\t\t\tissues: error.issues\n\t\t\t\t},\n\t\t\t\t{ status: 409 }\n\t\t\t);\n\t\t}\n\n\t\tthrow error;\n\t}\n}\n\nexport async function handleRotatePreflight() {\n\ttry {\n\t\tconst preflight = await getRotationPreflight();\n\t\treturn json({\n\t\t\tsuccess: true,\n\t\t\tsafe: preflight.safe,\n\t\t\tstatus: preflight.status,\n\t\t\taffected: preflight.affected\n\t\t});\n\t} catch (error) {\n\t\tif (error instanceof CatalogIntegrityError) {\n\t\t\treturn json(\n\t\t\t\t{\n\t\t\t\t\tsuccess: false,\n\t\t\t\t\terror: error.message,\n\t\t\t\t\tcode: \"catalog_integrity_error\",\n\t\t\t\t\tissues: error.issues\n\t\t\t\t},\n\t\t\t\t{ status: 409 }\n\t\t\t);\n\t\t}\n\n\t\tthrow error;\n\t}\n}\n\nexport async function handleRotateCatalogs(request: Request) {\n\tconst data = await request.json().catch(() => null);\n\tconst confirmDestructive = data?.confirmDestructive === true;\n\tconst preflight = await getRotationPreflight();\n\n\tif (!preflight.safe && !confirmDestructive) {\n\t\treturn json(\n\t\t\t{\n\t\t\t\tsuccess: false,\n\t\t\t\terror: \"Catalog rotation would overwrite working-only translations. Confirm rotation explicitly to continue.\",\n\t\t\t\tcode: \"rotation_confirmation_required\",\n\t\t\t\tstatus: preflight.status,\n\t\t\t\taffected: preflight.affected\n\t\t\t},\n\t\t\t{ status: 409 }\n\t\t);\n\t}\n\n\tconst result = await rotateCatalogs({ allowOutOfSync: confirmDestructive });\n\n\tif (!result.ok) {\n\t\treturn json(\n\t\t\t{\n\t\t\t\tsuccess: false,\n\t\t\t\terror: result.error,\n\t\t\t\tstatus: \"status\" in result ? result.status : undefined,\n\t\t\t\taffected: \"affected\" in result ? result.affected : undefined\n\t\t\t},\n\t\t\t{ status: \"status\" in result && result.status === \"out_of_sync\" ? 409 : 400 }\n\t\t);\n\t}\n\n\treturn json({\n\t\tsuccess: true,\n\t\tmessage: \"Catalogs rotated\",\n\t\tbasePoPath: result.basePoPath,\n\t\tworkingPoPath: result.workingPoPath,\n\t\tbaseBackupPath: result.baseBackupPath,\n\t\tworkingBackupPath: result.workingBackupPath\n\t});\n}\n", "import { constants as fsConstants } from \"node:fs\";\nimport { access, copyFile, readFile, rename, rm, writeFile } from \"node:fs/promises\";\nimport { dirname, extname, join, basename } from \"node:path\";\nimport gettextParser from \"gettext-parser\";\nimport Fuse from \"fuse.js\";\nimport type {\n\tCatalogIntegrityIssue,\n\tNormalizedEntry,\n\tPoTranslationEntry,\n\tRotationImpactItem,\n\tTranslationOrigin,\n\tTranslationStatus\n} from \"./types.ts\";\nimport { getTranslationHelperConfig, suspendWorkingCatalogWatch } from \"./config.ts\";\n\nexport const runtimeTranslations = new Map<string, string>();\n\nexport class CatalogIntegrityError extends Error {\n\tissues: CatalogIntegrityIssue[];\n\n\tconstructor(message: string, issues: CatalogIntegrityIssue[]) {\n\t\tsuper(message);\n\t\tthis.name = \"CatalogIntegrityError\";\n\t\tthis.issues = issues;\n\t}\n}\n\ntype CatalogPair = {\n\tbaseEntries: NormalizedEntry[];\n\tworkingEntries: NormalizedEntry[];\n\tbaseMap: Map<string, NormalizedEntry>;\n\tworkingMap: Map<string, NormalizedEntry>;\n\trotationImpact: RotationImpactItem[];\n};\n\nexport function catalogEntryKey(msgid: string, msgctxt: string | null) {\n\treturn `${msgctxt ?? \"\"}::${msgid}`;\n}\n\r\nexport function normalizeWhitespace(value: string): string {\r\n\treturn value.replace(/\\s+/g, \" \").trim();\r\n}\r\n\r\nexport function normalizeForLookup(value: string): string {\r\n\treturn normalizeWhitespace(value)\r\n\t\t.replace(/<\\/?[a-z][^>]*>/gi, \"<x>\")\r\n\t\t.replace(/<\\/?\\d+>/g, \"<x>\")\r\n\t\t.replace(/\\{\\{?\\s*[^}]+\\s*\\}?\\}/g, \"{0}\")\r\n\t\t.replace(/\\{\\d+\\}/g, \"{0}\")\r\n\t\t.replace(/[\"\"'`\u00C2\u00B4]/g, \"'\")\r\n\t\t.toLowerCase();\r\n}\r\n\r\nexport function tokenizeForLookup(value: string): string[] {\r\n\treturn normalizeForLookup(value)\r\n\t\t.split(/[\\s,.;:!?()[\\]{}]+/)\r\n\t\t.map((token) => token.trim())\r\n\t\t.filter(Boolean);\r\n}\r\n\r\nexport function buildLookupVariants(raw: string): string[] {\r\n\tconst normalized = normalizeForLookup(raw);\r\n\tconst variants = new Set<string>([normalized]);\r\n\r\n\tvariants.add(normalized.replace(/<x>/g, \"<0>\"));\r\n\tvariants.add(normalized.replace(/<x>/g, \"\").replace(/\\s+/g, \" \").trim());\r\n\tvariants.add(normalized.replace(/\\{0\\}/g, \"\").replace(/\\s+/g, \" \").trim());\r\n\r\n\treturn [...variants].filter(Boolean);\r\n}\r\n\r\nexport function flattenPoEntries(parsed: any, translationOrigin: TranslationOrigin): NormalizedEntry[] {\r\n\tconst entries: NormalizedEntry[] = [];\r\n\tconst translations = parsed?.translations ?? {};\r\n\r\n\tfor (const [ctxKey, group] of Object.entries(translations)) {\r\n\t\tfor (const [msgidKey, raw] of Object.entries(group as Record<string, unknown>)) {\r\n\t\t\tif (!msgidKey) continue;\r\n\r\n\t\t\tconst entry = raw as PoTranslationEntry;\r\n\t\t\tconst msgid = entry.msgid ?? msgidKey;\r\n\t\t\tconst msgctxt = entry.msgctxt ?? (ctxKey === \"\" ? null : ctxKey);\r\n\t\t\tconst msgidPlural = entry.msgid_plural ?? null;\r\n\t\t\tconst msgstr = Array.isArray(entry.msgstr) ? entry.msgstr.filter(Boolean) : [];\r\n\t\t\tconst references = entry.comments?.reference\r\n\t\t\t\t? entry.comments.reference.split(\"\\n\").map((item) => item.trim()).filter(Boolean)\r\n\t\t\t\t: [];\r\n\t\t\tconst extractedComments = entry.comments?.extracted\r\n\t\t\t\t? entry.comments.extracted.split(\"\\n\").map((item) => item.trim()).filter(Boolean)\r\n\t\t\t\t: [];\r\n\t\t\tconst flags = entry.comments?.flag\r\n\t\t\t\t? entry.comments.flag.split(\",\").map((item) => item.trim()).filter(Boolean)\r\n\t\t\t\t: [];\r\n\t\t\tconst previous = entry.comments?.previous\r\n\t\t\t\t? entry.comments.previous.split(\"\\n\").map((item) => item.trim()).filter(Boolean)\r\n\t\t\t\t: [];\r\n\t\t\tconst translated = entry.msgstr?.some((item) => item.trim().length > 0) ?? false;\r\n\t\t\tconst fuzzy = entry.comments?.flag?.includes(\"fuzzy\");\r\n\r\n\t\t\tentries.push({\r\n\t\t\t\tmsgid,\r\n\t\t\t\tmsgctxt,\r\n\t\t\t\tmsgidPlural,\r\n\t\t\t\tmsgstr,\r\n\t\t\t\treferences,\r\n\t\t\t\textractedComments,\r\n\t\t\t\tflags,\r\n\t\t\t\tprevious,\r\n\t\t\t\tobsolete: Boolean(entry.obsolete),\r\n\t\t\t\tsearchText: normalizeForLookup(msgid),\r\n\t\t\t\tsearchTokens: tokenizeForLookup(msgid),\r\n\t\t\t\tisFuzzy: fuzzy,\r\n\t\t\t\thasTranslation: translated,\r\n\t\t\t\ttranslationOrigin\r\n\t\t\t});\r\n\t\t}\r\n\t}\r\n\r\n\treturn entries;\r\n}\r\n\r\nexport function createFuse(entries: NormalizedEntry[]) {\r\n\treturn new Fuse(entries, {\r\n\t\tincludeScore: true,\r\n\t\tignoreLocation: true,\r\n\t\tthreshold: 0.34,\r\n\t\tminMatchCharLength: 2,\r\n\t\tkeys: [\r\n\t\t\t{ name: \"msgid\", weight: 0.7 },\r\n\t\t\t{ name: \"searchText\", weight: 0.2 },\r\n\t\t\t{ name: \"references\", weight: 0.1 }\r\n\t\t]\r\n\t});\r\n}\r\n\r\nexport function buildEntryMap(entries: NormalizedEntry[]) {\n\treturn new Map(entries.map((entry) => [catalogEntryKey(entry.msgid, entry.msgctxt), entry]));\n}\n\nexport function getEntryValue(entry?: Pick<NormalizedEntry, \"msgstr\"> | null) {\n\tif (!entry?.msgstr?.length) return \"\";\n\treturn entry.msgstr.find((item) => item.trim().length > 0)?.trim() ?? \"\";\n}\n\nexport function applyWorkingState(\n\tbaseEntry: NormalizedEntry,\n\tworkingEntry?: NormalizedEntry\n): NormalizedEntry {\n\tif (!workingEntry) {\r\n\t\treturn baseEntry;\r\n\t}\r\n\r\n\treturn {\r\n\t\t...baseEntry,\r\n\t\tmsgstr: workingEntry.msgstr,\r\n\t\tflags: workingEntry.flags,\r\n\t\tprevious: workingEntry.previous,\r\n\t\tobsolete: workingEntry.obsolete,\r\n\t\thasTranslation: workingEntry.hasTranslation,\r\n\t\tisFuzzy: workingEntry.isFuzzy,\r\n\t\ttranslationOrigin: workingEntry.translationOrigin\r\n\t};\r\n}\r\n\r\nexport async function fileExists(path: string) {\r\n\ttry {\r\n\t\tawait access(path, fsConstants.F_OK);\r\n\t\treturn true;\r\n\t} catch {\r\n\t\treturn false;\r\n\t}\r\n}\r\n\r\nexport async function readCatIndex(catalog: TranslationOrigin) {\n\tconst { basePoPath } = getTranslationHelperConfig();\n\tlet path = \"\";\n\n\tif (catalog === \"base\") {\n\t\tpath = basePoPath;\n\t} else {\n\t\tpath = await getEffectiveWorkingPoPath();\n\t\tconst exists = await fileExists(path);\n\t\tif (!exists) return null;\n\t}\n\r\n\tconst raw = await readFile(path);\r\n\tconst parsed = gettextParser.po.parse(raw);\r\n\tconst entries = flattenPoEntries(parsed, catalog);\r\n\r\n\treturn {\r\n\t\tentries,\r\n\t\tfuse: createFuse(entries)\r\n\t};\r\n}\r\n\r\nexport async function readParsedCatalog(catalog: TranslationOrigin) {\n\tconst { basePoPath } = getTranslationHelperConfig();\n\tconst path = catalog === \"base\" ? basePoPath : await getEffectiveWorkingPoPath();\n\r\n\tif (catalog === \"working\") {\r\n\t\tconst exists = await fileExists(path);\r\n\t\tif (!exists) return null;\r\n\t}\r\n\r\n\tconst raw = await readFile(path);\r\n\treturn gettextParser.po.parse(raw);\r\n}\r\n\r\nexport async function ensureWorkingCatalog() {\n\tconst { basePoPath, workingPoPath } = getTranslationHelperConfig();\n\tconst exists = await fileExists(workingPoPath);\n\tif (!exists) {\n\t\tawait copyFile(basePoPath, workingPoPath);\n\t}\n}\n\nexport function getWorkingDraftPoPath() {\n\tconst { workingPoPath } = getTranslationHelperConfig();\n\tconst extension = extname(workingPoPath);\n\tconst stem = workingPoPath.slice(0, workingPoPath.length - extension.length);\n\treturn `${stem}.angy-draft${extension}`;\n}\n\nasync function getEffectiveWorkingPoPath() {\n\tconst draftPoPath = getWorkingDraftPoPath();\n\tif (await fileExists(draftPoPath)) {\n\t\treturn draftPoPath;\n\t}\n\n\treturn getTranslationHelperConfig().workingPoPath;\n}\n\nexport async function ensureWorkingDraftCatalog() {\n\tawait ensureWorkingCatalog();\n\n\tconst { workingPoPath } = getTranslationHelperConfig();\n\tconst draftPoPath = getWorkingDraftPoPath();\n\tconst exists = await fileExists(draftPoPath);\n\tif (!exists) {\n\t\tawait copyFile(workingPoPath, draftPoPath);\n\t}\n}\n\r\nexport function removeFuzzyFlag(flagString?: string) {\r\n\tif (!flagString) return \"\";\r\n\r\n\treturn flagString\r\n\t\t.split(\",\")\r\n\t\t.map((item) => item.trim())\r\n\t\t.filter(Boolean)\r\n\t\t.filter((item) => item !== \"fuzzy\")\r\n\t\t.join(\", \");\r\n}\r\n\r\nexport async function writeWorkingCatalog(parsed: any) {\n\tawait ensureWorkingDraftCatalog();\n\tconst draftPoPath = getWorkingDraftPoPath();\n\tconst compiled = gettextParser.po.compile(parsed);\n\tawait writeFile(draftPoPath, compiled);\n}\n\nexport async function promoteWorkingDraftToRuntime() {\n\tawait ensureWorkingDraftCatalog();\n\n\tconst { workingPoPath } = getTranslationHelperConfig();\n\tconst draftPoPath = getWorkingDraftPoPath();\n\tconst publishPath = `${workingPoPath}.angy-publish`;\n\tconst compiled = await readFile(draftPoPath);\n\n\tsuspendWorkingCatalogWatch(workingPoPath);\n\tawait writeFile(publishPath, compiled);\n\tawait rm(workingPoPath, { force: true });\n\tawait rename(publishPath, workingPoPath);\n}\n\nexport function collectCatalogIntegrityIssues(\n\tbaseEntries: NormalizedEntry[],\n\tworkingEntries: NormalizedEntry[]\n) {\n\tconst issues: CatalogIntegrityIssue[] = [];\n\tconst baseMap = buildEntryMap(baseEntries);\n\tconst workingMap = buildEntryMap(workingEntries);\n\n\tfor (const baseEntry of baseEntries) {\n\t\tconst key = catalogEntryKey(baseEntry.msgid, baseEntry.msgctxt);\n\t\tconst workingEntry = workingMap.get(key);\n\t\tif (!workingEntry) {\n\t\t\tissues.push({\n\t\t\t\ttype: \"key_mismatch\",\n\t\t\t\tmsgid: baseEntry.msgid,\n\t\t\t\tmsgctxt: baseEntry.msgctxt,\n\t\t\t\treason: \"missing_in_working\"\n\t\t\t});\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst baseValue = getEntryValue(baseEntry);\n\t\tconst workingValue = getEntryValue(workingEntry);\n\t\tif (baseValue && !workingValue) {\n\t\t\tissues.push({\n\t\t\t\ttype: \"missing_working_translation\",\n\t\t\t\tmsgid: baseEntry.msgid,\n\t\t\t\tmsgctxt: baseEntry.msgctxt,\n\t\t\t\tbaseValue\n\t\t\t});\n\t\t}\n\t}\n\n\tfor (const workingEntry of workingEntries) {\n\t\tconst key = catalogEntryKey(workingEntry.msgid, workingEntry.msgctxt);\n\t\tif (!baseMap.has(key)) {\n\t\t\tissues.push({\n\t\t\t\ttype: \"key_mismatch\",\n\t\t\t\tmsgid: workingEntry.msgid,\n\t\t\t\tmsgctxt: workingEntry.msgctxt,\n\t\t\t\treason: \"missing_in_base\"\n\t\t\t});\n\t\t}\n\t}\n\n\treturn issues;\n}\n\nexport function assertCatalogIntegrity(baseEntries: NormalizedEntry[], workingEntries: NormalizedEntry[]) {\n\tconst issues = collectCatalogIntegrityIssues(baseEntries, workingEntries);\n\tif (issues.length) {\n\t\tthrow new CatalogIntegrityError(\n\t\t\t\"Working catalog is out of sync with the base catalog. Regenerate or replace the working catalog before using Angy.\",\n\t\t\tissues\n\t\t);\n\t}\n}\n\nexport function collectRotationImpact(\n\tbaseEntries: NormalizedEntry[],\n\tworkingMap: Map<string, NormalizedEntry>\n) {\n\tconst impacted: RotationImpactItem[] = [];\n\n\tfor (const baseEntry of baseEntries) {\n\t\tconst workingEntry = workingMap.get(catalogEntryKey(baseEntry.msgid, baseEntry.msgctxt));\n\t\tif (!workingEntry) continue;\n\n\t\tconst baseValue = getEntryValue(baseEntry);\n\t\tconst workingValue = getEntryValue(workingEntry);\n\t\tif (!workingValue) continue;\n\t\tif (baseValue === workingValue) continue;\n\n\t\timpacted.push({\n\t\t\tmsgid: baseEntry.msgid,\n\t\t\tmsgctxt: baseEntry.msgctxt,\n\t\t\tbaseValue,\n\t\t\tworkingValue\n\t\t});\n\t}\n\n\treturn impacted;\n}\n\nexport function resolveTranslationState(\n\tbaseEntry: NormalizedEntry,\n\tworkingEntry?: NormalizedEntry\n) {\n\tconst baseValue = getEntryValue(baseEntry);\n\tconst workingValue = getEntryValue(workingEntry);\n\tconst activeEntry = workingEntry && workingValue ? workingEntry : baseEntry;\n\tconst fuzzy = Boolean(activeEntry.isFuzzy);\n\tlet translationOrigin: TranslationOrigin = \"base\";\n\tlet translationStatus: TranslationStatus = \"none\";\n\tlet effectiveEntry = baseEntry;\n\n\tif (workingEntry && workingValue) {\n\t\teffectiveEntry = workingEntry;\n\t\tif (!baseValue || workingValue !== baseValue) {\n\t\t\ttranslationOrigin = \"working\";\n\t\t\ttranslationStatus = \"working\";\n\t\t} else {\n\t\t\ttranslationOrigin = \"base\";\n\t\t\ttranslationStatus = \"base\";\n\t\t}\n\t} else if (baseValue) {\n\t\teffectiveEntry = baseEntry;\n\t\ttranslationOrigin = \"base\";\n\t\ttranslationStatus = \"base\";\n\t}\n\n\tif (fuzzy) {\n\t\ttranslationStatus = \"fuzzy\";\n\t}\n\n\treturn {\n\t\teffectiveEntry,\n\t\tbaseValue,\n\t\tworkingValue,\n\t\ttranslationOrigin,\n\t\ttranslationStatus,\n\t\thasTranslation: Boolean(getEntryValue(effectiveEntry)),\n\t\tisFuzzy: fuzzy\n\t};\n}\n\nexport async function readCatalogPair(options?: { ensureWorking?: boolean }): Promise<CatalogPair> {\n\tif (options?.ensureWorking) {\n\t\tawait ensureWorkingCatalog();\n\t}\n\n\tconst [baseParsed, workingParsed] = await Promise.all([\n\t\treadParsedCatalog(\"base\"),\n\t\treadParsedCatalog(\"working\")\n\t]);\n\n\tif (!baseParsed || !workingParsed) {\n\t\tthrow new Error(\"Unable to load base and working catalogs.\");\n\t}\n\n\tconst baseEntries = flattenPoEntries(baseParsed, \"base\");\n\tconst workingEntries = flattenPoEntries(workingParsed, \"working\");\n\tassertCatalogIntegrity(baseEntries, workingEntries);\n\n\tconst baseMap = buildEntryMap(baseEntries);\n\tconst workingMap = buildEntryMap(workingEntries);\n\tconst rotationImpact = collectRotationImpact(baseEntries, workingMap);\n\n\treturn {\n\t\tbaseEntries,\n\t\tworkingEntries,\n\t\tbaseMap,\n\t\tworkingMap,\n\t\trotationImpact\n\t};\n}\n\nexport async function getRotationPreflight() {\n\tconst { rotationImpact } = await readCatalogPair({ ensureWorking: true });\n\n\treturn {\n\t\tsafe: true as const,\n\t\tstatus: \"ok\" as const,\n\t\taffected: rotationImpact\n\t};\n}\n\nfunction timestampForBackup() {\n\treturn new Date().toISOString().replace(/[:.]/g, \"-\");\n}\n\nfunction buildBackupPath(path: string, label: string) {\n\tconst directory = dirname(path);\n\tconst extension = extname(path);\n\tconst stem = basename(path, extension);\n\treturn join(directory, `${stem}.${label}.${timestampForBackup()}${extension}`);\n}\n\nexport async function rotateCatalogs(options?: { allowOutOfSync?: boolean }) {\n\tconst { basePoPath, workingPoPath } = getTranslationHelperConfig();\n\tconst draftPoPath = getWorkingDraftPoPath();\n\tconst effectiveWorkingPoPath = await getEffectiveWorkingPoPath();\n\tconst workingExists = await fileExists(effectiveWorkingPoPath);\n\n\tif (!workingExists) {\n\t\treturn { ok: false as const, error: \"Working catalog does not exist\" };\n\t}\n\n\tconst baseBackupPath = buildBackupPath(basePoPath, \"base-backup\");\n\tconst workingBackupPath = buildBackupPath(workingPoPath, \"working-backup\");\n\n\tawait copyFile(basePoPath, baseBackupPath);\n\tawait copyFile(effectiveWorkingPoPath, workingBackupPath);\n\tawait copyFile(effectiveWorkingPoPath, basePoPath);\n\tawait copyFile(basePoPath, workingPoPath);\n\tawait copyFile(workingPoPath, draftPoPath);\n\n\treturn {\n\t\tok: true as const,\n\t\tbasePoPath,\n\t\tworkingPoPath,\n\t\tbaseBackupPath,\n\t\tworkingBackupPath\n\t};\n}\n", "import { readFile, unlink, writeFile } from \"node:fs/promises\";\nimport { basename, dirname, extname, isAbsolute, normalize, resolve } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { pathToFileURL } from \"node:url\";\nimport { transform } from \"esbuild\";\nimport type { TranslationContextResult } from \"../client/toggleQA.shared\";\n\nconst __dirname = dirname(fileURLToPath(import.meta.url));\nconst CONFIG_FILENAMES = [\n\t\"angy.config.ts\",\n\t\"angy.config.js\",\n\t\"angy.config.mjs\",\n\t\"angy.config.cjs\"\n];\n\nexport type TranslationHelperConfig = {\n\tbasePoPath: string;\n\tworkingPoPath: string;\n\tsourceLocale: string;\n\ttargetLocale: string;\n\troutePath: string;\n\tapiKey: string;\n\tsystemMessage: string;\n\tsuggestionModel: string;\n\twatchIgnore: string[];\n\tsuggestionProvider?: SuggestionProvider;\n};\n\nexport type SuggestionRequestItem = {\n\tmsgid: string;\n\tmsgctxt: string | null;\n};\n\nexport type SuggestionResponseItem = {\n\tmsgid: string;\n\tmsgctxt: string | null;\n\tsuggestion: string;\n};\n\nexport type SuggestionProviderInput = {\n\tcontext: TranslationContextResult;\n\titems: SuggestionRequestItem[];\n\tsourceLocale: string;\n\ttargetLocale: string;\n\tsystemMessage: string;\n\tmodel: string;\n\tapiKey: string | undefined;\n};\n\nexport type SuggestionProvider = (\n\tinput: SuggestionProviderInput\n) => Promise<SuggestionResponseItem[]>;\n\nexport type TranslationHelperUserConfig = Partial<TranslationHelperConfig>;\n\nexport type AngyConfigInput = {\n\tbasePoPath: string;\n\tworkingPoPath: string;\n\tsourceLocale: string;\n\ttargetLocale: string;\n\troutePath?: string;\n\tapiKey?: string;\n\tsystemMessage?: string;\n\tsuggestionModel?: string;\n\twatchIgnore?: string[];\n\tsuggestionProvider?: SuggestionProvider;\n};\n\nexport function buildDefaultSystemMessage(sourceLocale: string, targetLocale: string) {\n\treturn `You are an expert product localization assistant translating ${sourceLocale} UI copy into polished ${targetLocale}. Keep already-${targetLocale} text unchanged. Preserve placeholders like {0}, {1}, ICU fragments, HTML-like markers such as <0/> and <strong>, line breaks, punctuation, capitalization, and button-like brevity. Prefer terminology and syntax that stay close to the translated examples so the product voice remains cohesive. Do not invent extra context, do not expand abbreviations unless the examples already do, and avoid semantic drift. Return only high-confidence translation suggestions for the provided untranslated strings.`;\n}\n\nfunction assertNonEmptyString(value: unknown, key: string) {\n\tif (typeof value !== \"string\" || !value.trim()) {\n\t\tthrow new Error(`[angy] ${key} is required and must be a non-empty string.`);\n\t}\n}\n\nfunction validateRoutePath(routePath: unknown) {\n\tif (routePath == null || routePath === \"\") return;\n\tif (typeof routePath !== \"string\" || !routePath.startsWith(\"/\")) {\n\t\tthrow new Error(`[angy] routePath must be an absolute path starting with \"/\".`);\n\t}\n}\n\nfunction validateWatchIgnore(watchIgnore: unknown) {\n\tif (watchIgnore == null) return;\n\tif (!Array.isArray(watchIgnore) || watchIgnore.some((item) => typeof item !== \"string\")) {\n\t\tthrow new Error(`[angy] watchIgnore must be an array of strings.`);\n\t}\n}\n\nfunction validateSuggestionProvider(suggestionProvider: unknown) {\n\tif (suggestionProvider == null) return;\n\tif (typeof suggestionProvider !== \"function\") {\n\t\tthrow new Error(`[angy] suggestionProvider must be a function.`);\n\t}\n}\n\nconst config: TranslationHelperConfig = {\n\tbasePoPath: resolve(__dirname, \"../../locales/en.po\"),\n\tworkingPoPath: resolve(__dirname, \"../../locales/en-working.po\"),\n\tsourceLocale: \"sv\",\n\ttargetLocale: \"en\",\n\troutePath: \"/api/translations\",\n\tapiKey: \"\",\n\tsystemMessage: buildDefaultSystemMessage(\"sv\", \"en\"),\n\tsuggestionModel: \"gpt-4.1-mini\",\n\twatchIgnore: [\"**/en-working.po\"]\n};\n\nlet loadedConfigRoot: string | null = null;\nconst workingCatalogWatchControllers = new Set<(path: string, delayMs?: number) => void>();\n\nexport function configureTranslationHelper(next: Partial<TranslationHelperConfig>) {\n\tObject.assign(config, next);\n}\n\nexport function getTranslationHelperConfig() {\n\treturn config;\n}\n\nfunction normalizeFsPath(path: string) {\n\treturn normalize(path).replace(/\\\\/g, \"/\");\n}\n\nexport function inferLocaleFromCatalogPath(path: string) {\n\tconst fileName = basename(path);\n\tif (!fileName) return null;\n\treturn fileName.slice(0, fileName.length - extname(fileName).length) || null;\n}\n\nfunction resolveLocaleAlias(\n\tvalue: string,\n\tpaths: { basePoPath: string; workingPoPath: string }\n) {\n\tif (value === \"working\") {\n\t\tconst locale = inferLocaleFromCatalogPath(paths.workingPoPath);\n\t\tif (!locale) {\n\t\t\tthrow new Error(\"[angy] Unable to infer locale from workingPoPath.\");\n\t\t}\n\t\treturn locale;\n\t}\n\n\tif (value === \"base\") {\n\t\tconst locale = inferLocaleFromCatalogPath(paths.basePoPath);\n\t\tif (!locale) {\n\t\t\tthrow new Error(\"[angy] Unable to infer locale from basePoPath.\");\n\t\t}\n\t\treturn locale;\n\t}\n\n\treturn value;\n}\n\nfunction validateLocaleAliasUsage(sourceLocale: string, targetLocale: string) {\n\tif (sourceLocale === \"working\") {\n\t\tthrow new Error('[angy] sourceLocale cannot be \"working\". Use an explicit source locale.');\n\t}\n\n\tif (targetLocale === \"base\") {\n\t\tthrow new Error('[angy] targetLocale cannot be \"base\". Use an explicit target locale or \"working\".');\n\t}\n}\n\nfunction validateCatalogPathSemantics(next: TranslationHelperConfig) {\n\tconst baseLocale = inferLocaleFromCatalogPath(next.basePoPath);\n\tconst workingLocale = inferLocaleFromCatalogPath(next.workingPoPath);\n\tconst expectedWorkingLocale = `${next.targetLocale}-working`;\n\n\tif (baseLocale !== next.targetLocale) {\n\t\tthrow new Error(\n\t\t\t`[angy] basePoPath must point to the ${next.targetLocale}.po catalog. Received \"${baseLocale ?? \"unknown\"}\".`\n\t\t);\n\t}\n\n\tif (workingLocale !== expectedWorkingLocale) {\n\t\tthrow new Error(\n\t\t\t`[angy] workingPoPath must point to the ${expectedWorkingLocale}.po catalog. Received \"${workingLocale ?? \"unknown\"}\".`\n\t\t);\n\t}\n}\n\nexport function registerWorkingCatalogWatchController(\n\tcontroller: (path: string, delayMs?: number) => void\n) {\n\tworkingCatalogWatchControllers.add(controller);\n\treturn () => {\n\t\tworkingCatalogWatchControllers.delete(controller);\n\t};\n}\n\nexport function suspendWorkingCatalogWatch(path: string, delayMs = 800) {\n\tconst normalizedPath = normalizeFsPath(path);\n\tfor (const controller of workingCatalogWatchControllers) {\n\t\tcontroller(normalizedPath, delayMs);\n\t}\n}\n\nexport function normalizeTranslationHelperConfig(\n\troot: string,\n\tnext: TranslationHelperUserConfig\n): TranslationHelperUserConfig {\n\tconst normalized = { ...next };\n\n\tif (normalized.basePoPath && !isAbsolute(normalized.basePoPath)) {\n\t\tnormalized.basePoPath = resolve(root, normalized.basePoPath);\n\t}\n\n\tif (normalized.workingPoPath && !isAbsolute(normalized.workingPoPath)) {\n\t\tnormalized.workingPoPath = resolve(root, normalized.workingPoPath);\n\t}\n\n\treturn normalized;\n}\n\nexport function resolveConfiguredLocaleAliases(\n\tnext: TranslationHelperConfig\n): TranslationHelperConfig {\n\tconst rawSourceLocale = next.sourceLocale;\n\tconst rawTargetLocale = next.targetLocale;\n\tconst resolvedSourceLocale = resolveLocaleAlias(rawSourceLocale, next);\n\tconst resolvedTargetLocale = resolveLocaleAlias(rawTargetLocale, next);\n\tconst usesDefaultSystemMessage =\n\t\tnext.systemMessage === buildDefaultSystemMessage(rawSourceLocale, rawTargetLocale);\n\n\tconst resolved = {\n\t\t...next,\n\t\tsourceLocale: resolvedSourceLocale,\n\t\ttargetLocale: resolvedTargetLocale,\n\t\tsystemMessage: usesDefaultSystemMessage\n\t\t\t? buildDefaultSystemMessage(resolvedSourceLocale, resolvedTargetLocale)\n\t\t\t: next.systemMessage\n\t};\n\n\tvalidateCatalogPathSemantics(resolved);\n\treturn resolved;\n}\n\nexport function completeAngyConfig(input: AngyConfigInput): TranslationHelperConfig {\n\tassertNonEmptyString(input.basePoPath, \"basePoPath\");\n\tassertNonEmptyString(input.workingPoPath, \"workingPoPath\");\n\tassertNonEmptyString(input.sourceLocale, \"sourceLocale\");\n\tassertNonEmptyString(input.targetLocale, \"targetLocale\");\n\tvalidateLocaleAliasUsage(input.sourceLocale, input.targetLocale);\n\tif (typeof input.apiKey !== \"string\" && typeof input.apiKey !== \"undefined\") {\n\t\tthrow new Error(`[angy] apiKey must be a string when provided. Use an empty string to disable suggestions.`);\n\t}\n\tvalidateRoutePath(input.routePath);\n\tvalidateWatchIgnore(input.watchIgnore);\n\tvalidateSuggestionProvider(input.suggestionProvider);\n\n\treturn {\n\t\tbasePoPath: input.basePoPath,\n\t\tworkingPoPath: input.workingPoPath,\n\t\tsourceLocale: input.sourceLocale,\n\t\ttargetLocale: input.targetLocale,\n\t\troutePath: input.routePath ?? \"/api/translations\",\n\t\tapiKey: input.apiKey ?? \"\",\n\t\tsystemMessage:\n\t\t\tinput.systemMessage ??\n\t\t\tbuildDefaultSystemMessage(input.sourceLocale, input.targetLocale),\n\t\tsuggestionModel: input.suggestionModel ?? \"gpt-4.1-mini\",\n\t\twatchIgnore: input.watchIgnore ?? [\"**/en-working.po\"],\n\t\tsuggestionProvider: input.suggestionProvider\n\t};\n}\n\nexport function defineAngyConfig(config: AngyConfigInput) {\n\treturn resolveConfiguredLocaleAliases(completeAngyConfig(config));\n}\n\nasync function fileExists(path: string) {\n\ttry {\n\t\tawait readFile(path);\n\t\treturn true;\n\t} catch {\n\t\treturn false;\n\t}\n}\n\nasync function loadTsConfigModule(path: string) {\n\tconst source = await readFile(path, \"utf8\");\n\tconst transformed = await transform(source, {\n\t\tloader: \"ts\",\n\t\tformat: \"esm\",\n\t\ttarget: \"es2022\"\n\t});\n\tconst tempPath = `${path}.angy.tmp.mjs`;\n\tawait writeFile(tempPath, transformed.code, \"utf8\");\n\n\ttry {\n\t\treturn await import(/* @vite-ignore */ `${pathToFileURL(tempPath).href}?t=${Date.now()}`);\n\t} finally {\n\t\tawait unlink(tempPath).catch(() => undefined);\n\t}\n}\n\nasync function loadJsConfigModule(path: string) {\n\treturn import(/* @vite-ignore */ pathToFileURL(path).href);\n}\n\nexport async function loadAngyConfigFromRoot(root: string) {\n\tif (loadedConfigRoot === root) {\n\t\treturn config;\n\t}\n\n\tfor (const filename of CONFIG_FILENAMES) {\n\t\tconst fullPath = `${root}/${filename}`.replace(/\\\\/g, \"/\");\n\t\tif (!(await fileExists(fullPath))) continue;\n\n\t\tconst module =\n\t\t\tfilename.endsWith(\".ts\")\n\t\t\t\t? await loadTsConfigModule(fullPath)\n\t\t\t\t: await loadJsConfigModule(fullPath);\n\t\tconst raw = (module.default ?? module.config ?? {}) as TranslationHelperUserConfig;\n\t\tconst next = resolveConfiguredLocaleAliases(\n\t\t\tnormalizeTranslationHelperConfig(root, completeAngyConfig(raw as AngyConfigInput))\n\t\t);\n\t\tconfigureTranslationHelper(next);\n\t\tloadedConfigRoot = root;\n\t\treturn config;\n\t}\n\n\tloadedConfigRoot = root;\n\treturn config;\n}\n", "import { json } from \"@sveltejs/kit\";\nimport {\n\tbuildLookupVariants,\n\tcatalogEntryKey,\n\tcreateFuse,\n\tCatalogIntegrityError,\n\treadCatalogPair,\n\tresolveTranslationState\n} from \"./catalog.ts\";\nimport type { NormalizedEntry } from \"./types.ts\";\n\r\nconst DIRECT_MATCH_LIMIT = 5;\nconst MAX_ALTERNATIVES = 300;\nconst TRANSLATED_TARGET = Math.floor(MAX_ALTERNATIVES * 0.2);\nconst UNTRANSLATED_TARGET = MAX_ALTERNATIVES - TRANSLATED_TARGET;\nconst UNTRANSLATED_RANK_BONUS = 0.03;\n\r\ntype Candidate = {\r\n\tentry: NormalizedEntry;\r\n\tscore: number;\r\n\tvariant: string;\r\n};\r\n\r\ntype ScoredEntry = {\r\n\tentry: NormalizedEntry;\r\n\tscore: number;\r\n\tvariant: string;\r\n};\r\n\r\nexport function entryKey(msgid: string, msgctxt: string | null) {\r\n\treturn catalogEntryKey(msgid, msgctxt);\r\n}\r\n\r\nfunction normalizeCurrentPath(currentPath: string) {\r\n\ttry {\r\n\t\treturn new URL(currentPath, \"http://localhost\").pathname;\r\n\t} catch {\r\n\t\treturn currentPath;\r\n\t}\r\n}\r\n\r\nfunction inferPageReference(currentPath: string) {\r\n\tconst normalizedPath = normalizeCurrentPath(currentPath);\r\n\tif (normalizedPath === \"/\") {\r\n\t\treturn \"src/routes/+page.svelte\";\r\n\t}\r\n\r\n\tconst trimmedPath = normalizedPath.replace(/\\/+$/, \"\");\r\n\treturn `src/routes${trimmedPath}/+page.svelte`;\r\n}\r\n\r\nfunction routeLooksRelevant(routeReference: string, refs: string[], extractedComments: string[]) {\r\n\tif (!routeReference) return false;\r\n\r\n\tconst haystack = [...refs, ...extractedComments].join(\" \").toLowerCase();\r\n\treturn haystack.includes(routeReference.toLowerCase());\r\n}\r\n\r\nfunction dedupeCandidates(candidates: Candidate[]) {\r\n\tconst deduped = new Map<string, Candidate>();\r\n\r\n\tfor (const item of candidates) {\r\n\t\tconst id = entryKey(item.entry.msgid, item.entry.msgctxt);\r\n\t\tconst existing = deduped.get(id);\r\n\t\tif (!existing || item.score < existing.score) {\r\n\t\t\tdeduped.set(id, item);\r\n\t\t}\r\n\t}\r\n\r\n\treturn [...deduped.values()];\r\n}\r\n\r\nfunction rankDirectMatches(candidates: Candidate[], routeReference: string) {\n\treturn [...candidates].sort((left, right) => {\n\t\tconst leftRoute = routeLooksRelevant(\n\t\t\trouteReference,\n\t\t\tleft.entry.references,\r\n\t\t\tleft.entry.extractedComments\r\n\t\t)\r\n\t\t\t? -0.08\r\n\t\t\t: 0;\r\n\t\tconst rightRoute = routeLooksRelevant(\n\t\t\trouteReference,\n\t\t\tright.entry.references,\n\t\t\tright.entry.extractedComments\n\t\t)\n\t\t\t? -0.08\n\t\t\t: 0;\n\t\tconst leftUntranslated = !left.entry.hasTranslation ? -UNTRANSLATED_RANK_BONUS : 0;\n\t\tconst rightUntranslated = !right.entry.hasTranslation ? -UNTRANSLATED_RANK_BONUS : 0;\n\n\t\treturn left.score + leftRoute + leftUntranslated - (right.score + rightRoute + rightUntranslated);\n\t});\n}\n\r\nfunction getReferenceTokens(reference: string) {\r\n\treturn reference\r\n\t\t.toLowerCase()\r\n\t\t.split(/[\\/\\[\\].:_-]+/)\r\n\t\t.map((token) => token.trim())\r\n\t\t.filter(Boolean);\r\n}\r\n\r\nfunction getReferenceSimilarity(reference: string, routeReference: string) {\r\n\tconst referenceTokens = new Set(getReferenceTokens(reference));\r\n\tconst routeTokens = getReferenceTokens(routeReference);\r\n\r\n\tif (!routeTokens.length || !referenceTokens.size) return 0;\r\n\r\n\tlet matches = 0;\r\n\tfor (const token of routeTokens) {\r\n\t\tif (referenceTokens.has(token)) {\r\n\t\t\tmatches += 1;\r\n\t\t}\r\n\t}\r\n\r\n\treturn matches / routeTokens.length;\r\n}\r\n\r\nfunction collectSharedReferenceAlternatives(\n\tentries: NormalizedEntry[],\n\tbestEntry: NormalizedEntry,\n\trouteReference: string,\n\texcludedKeys: Set<string>\n) {\r\n\tconst sharedReferences = new Set(bestEntry.references.map((reference) => reference.toLowerCase()));\r\n\r\n\treturn entries\r\n\t\t.filter((entry) => !excludedKeys.has(entryKey(entry.msgid, entry.msgctxt)))\r\n\t\t.map((entry) => {\r\n\t\t\tconst referenceMatches = entry.references.filter((reference) =>\r\n\t\t\t\tsharedReferences.has(reference.toLowerCase())\r\n\t\t\t);\r\n\t\t\tconst routeMatch = entry.references.some((reference) =>\r\n\t\t\t\treference.toLowerCase().includes(routeReference.toLowerCase())\r\n\t\t\t);\r\n\r\n\t\t\tif (!referenceMatches.length && !routeMatch) {\r\n\t\t\t\treturn null;\r\n\t\t\t}\r\n\r\n\t\t\tconst referenceSimilarity = Math.max(\n\t\t\t\t0,\n\t\t\t\t...entry.references.map((reference) => getReferenceSimilarity(reference, routeReference))\n\t\t\t);\n\t\t\tconst untranslatedBonus = !entry.hasTranslation ? 0.35 : 0;\n\n\t\t\treturn {\n\t\t\t\tentry,\n\t\t\t\tscore:\n\t\t\t\t\treferenceMatches.length * 100 +\n\t\t\t\t\t(routeMatch ? 25 : 0) +\n\t\t\t\t\treferenceSimilarity +\n\t\t\t\t\tuntranslatedBonus,\n\t\t\t\tvariant: \"shared-reference\"\n\t\t\t};\n\t\t})\n\t\t.filter((item): item is ScoredEntry => Boolean(item))\r\n\t\t.sort((left, right) => right.score - left.score);\r\n}\r\n\r\nfunction collectRouteFillAlternatives(\n\tentries: NormalizedEntry[],\n\trouteReference: string,\n\texcludedKeys: Set<string>\n) {\n\treturn entries\r\n\t\t.filter((entry) => !excludedKeys.has(entryKey(entry.msgid, entry.msgctxt)))\r\n\t\t.map((entry) => {\r\n\t\t\tconst referenceSimilarity = Math.max(\r\n\t\t\t\t0,\r\n\t\t\t\t...entry.references.map((reference) => getReferenceSimilarity(reference, routeReference))\r\n\t\t\t);\r\n\r\n\t\t\tif (referenceSimilarity <= 0) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\tconst untranslatedBonus = !entry.hasTranslation ? 0.2 : 0;\n\n\t\t\treturn {\n\t\t\t\tentry,\n\t\t\t\tscore: referenceSimilarity + untranslatedBonus,\n\t\t\t\tvariant: \"route-reference\"\n\t\t\t};\n\t\t})\n\t\t.filter((item): item is ScoredEntry => Boolean(item))\r\n\t\t.sort((left, right) => right.score - left.score);\r\n}\r\n\r\nfunction collectTranslatedCohesionAlternatives(\r\n\tdirectMatches: Candidate[],\r\n\texcludedKeys: Set<string>\r\n) {\r\n\treturn directMatches\r\n\t\t.filter((item) => item.entry.hasTranslation)\r\n\t\t.filter((item) => !excludedKeys.has(entryKey(item.entry.msgid, item.entry.msgctxt)))\r\n\t\t.map((item) => ({\r\n\t\t\tentry: item.entry,\r\n\t\t\tscore: 1 - item.score,\r\n\t\t\tvariant: \"translated-cohesion\"\r\n\t\t}))\r\n\t\t.sort((left, right) => right.score - left.score);\r\n}\r\n\r\nfunction collectUntranslatedSimilarityAlternatives(\r\n\tdirectMatches: Candidate[],\r\n\texcludedKeys: Set<string>\r\n) {\r\n\treturn directMatches\r\n\t\t.filter((item) => !item.entry.hasTranslation)\r\n\t\t.filter((item) => !excludedKeys.has(entryKey(item.entry.msgid, item.entry.msgctxt)))\r\n\t\t.map((item) => ({\r\n\t\t\tentry: item.entry,\r\n\t\t\tscore: 1 - item.score,\r\n\t\t\tvariant: \"untranslated-similarity\"\r\n\t\t}))\r\n\t\t.sort((left, right) => right.score - left.score);\r\n}\r\n\r\nfunction buildAlternativePool(\r\n\ttopDirectAlternatives: Candidate[],\r\n\tsharedReferenceAlternatives: ScoredEntry[],\r\n\trouteFillAlternatives: ScoredEntry[],\r\n\ttranslatedCohesionAlternatives: ScoredEntry[],\r\n\tuntranslatedSimilarityAlternatives: ScoredEntry[]\r\n) {\r\n\tconst alternatives: ScoredEntry[] = [...topDirectAlternatives];\r\n\tconst selectedKeys = new Set<string>(\r\n\t\ttopDirectAlternatives.map((item) => entryKey(item.entry.msgid, item.entry.msgctxt))\r\n\t);\r\n\r\n\tlet translatedCount = topDirectAlternatives.filter((item) => item.entry.hasTranslation).length;\r\n\tlet untranslatedCount = topDirectAlternatives.length - translatedCount;\r\n\r\n\tconst tryAdd = (item: ScoredEntry, honorQuota = true) => {\r\n\t\tif (alternatives.length >= MAX_ALTERNATIVES) return false;\r\n\r\n\t\tconst id = entryKey(item.entry.msgid, item.entry.msgctxt);\r\n\t\tif (selectedKeys.has(id)) return false;\r\n\r\n\t\tif (honorQuota) {\r\n\t\t\tif (item.entry.hasTranslation && translatedCount >= TRANSLATED_TARGET) return false;\r\n\t\t\tif (!item.entry.hasTranslation && untranslatedCount >= UNTRANSLATED_TARGET) return false;\r\n\t\t}\r\n\r\n\t\tselectedKeys.add(id);\r\n\t\talternatives.push(item);\r\n\r\n\t\tif (item.entry.hasTranslation) {\r\n\t\t\ttranslatedCount += 1;\r\n\t\t} else {\r\n\t\t\tuntranslatedCount += 1;\r\n\t\t}\r\n\r\n\t\treturn true;\r\n\t};\r\n\r\n\tconst prioritizedSources = [\r\n\t\tsharedReferenceAlternatives,\r\n\t\trouteFillAlternatives,\r\n\t\tuntranslatedSimilarityAlternatives,\r\n\t\ttranslatedCohesionAlternatives\r\n\t];\r\n\r\n\tfor (const source of prioritizedSources) {\r\n\t\tfor (const item of source) {\r\n\t\t\ttryAdd(item, true);\r\n\t\t}\r\n\t}\r\n\r\n\tif (alternatives.length < MAX_ALTERNATIVES) {\r\n\t\tfor (const source of prioritizedSources) {\r\n\t\t\tfor (const item of source) {\r\n\t\t\t\ttryAdd(item, false);\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\r\n\treturn alternatives.slice(0, MAX_ALTERNATIVES);\r\n}\r\n\r\nexport async function findTranslationContext(\n\tkey: string,\n\tcurrentPath: string\n) {\n\tconst catalogPair = await readCatalogPair({ ensureWorking: true });\n\tconst effectiveEntries = catalogPair.baseEntries.map((entry) => {\n\t\tconst state = resolveTranslationState(entry, catalogPair.workingMap.get(entryKey(entry.msgid, entry.msgctxt)));\n\t\treturn {\n\t\t\t...state.effectiveEntry,\n\t\t\thasTranslation: state.hasTranslation,\n\t\t\tisFuzzy: state.isFuzzy,\n\t\t\ttranslationOrigin: state.translationOrigin\n\t\t};\n\t});\n\tconst effectiveIndex = {\n\t\tentries: effectiveEntries,\n\t\tfuse: createFuse(effectiveEntries)\n\t};\n\n\tconst routeReference = inferPageReference(currentPath);\n\tconst directMatches = rankDirectMatches(\n\t\tdedupeCandidates(\n\t\t\tbuildLookupVariants(key).flatMap((variant) =>\n\t\t\t\teffectiveIndex.fuse.search(variant, { limit: 180 }).map((hit) => ({\n\t\t\t\t\tentry: hit.item,\n\t\t\t\t\tscore: hit.score ?? 1,\n\t\t\t\t\tvariant\n\t\t\t\t}))\n\t\t\t)\n\t\t),\r\n\t\trouteReference\r\n\t);\r\n\r\n\tconst best = directMatches[0];\r\n\tif (!best || best.score > 0.42) {\n\t\treturn null;\n\t}\n\n\tconst effectiveDirectMatches = directMatches;\n\tconst bestEffective = effectiveDirectMatches[0];\n\tconst topDirectAlternatives = effectiveDirectMatches.slice(1, DIRECT_MATCH_LIMIT);\n\tconst excludedKeys = new Set<string>([\r\n\t\tentryKey(best.entry.msgid, best.entry.msgctxt),\r\n\t\t...topDirectAlternatives.map((item) => entryKey(item.entry.msgid, item.entry.msgctxt))\r\n\t]);\r\n\r\n\tconst sharedReferenceAlternatives = collectSharedReferenceAlternatives(\r\n\t\teffectiveEntries,\r\n\t\tbestEffective.entry,\r\n\t\trouteReference,\r\n\t\texcludedKeys\r\n\t);\r\n\r\n\tconst routeFillAlternatives = collectRouteFillAlternatives(\r\n\t\teffectiveEntries,\r\n\t\trouteReference,\r\n\t\texcludedKeys\r\n\t);\r\n\r\n\tconst translatedCohesionAlternatives = collectTranslatedCohesionAlternatives(\r\n\t\teffectiveDirectMatches.slice(DIRECT_MATCH_LIMIT),\r\n\t\texcludedKeys\r\n\t);\r\n\r\n\tconst untranslatedSimilarityAlternatives = collectUntranslatedSimilarityAlternatives(\r\n\t\teffectiveDirectMatches.slice(DIRECT_MATCH_LIMIT),\r\n\t\texcludedKeys\r\n\t);\r\n\r\n\treturn {\n\t\tbest,\n\t\talternatives: buildAlternativePool(\n\t\t\ttopDirectAlternatives,\r\n\t\t\tsharedReferenceAlternatives,\r\n\t\t\trouteFillAlternatives,\r\n\t\t\ttranslatedCohesionAlternatives,\r\n\t\t\tuntranslatedSimilarityAlternatives\n\t\t),\n\t\trouteReference,\n\t\tbaseMap: catalogPair.baseMap,\n\t\tworkingMap: catalogPair.workingMap,\n\t\tcatalogState: {\n\t\t\tstatus: \"ok\" as const,\n\t\t\taffectedCount: catalogPair.rotationImpact.length\n\t\t}\n\t};\n}\n\nexport function toPublicEntry(\n\tentry: NormalizedEntry,\n\tworkingEntry?: NormalizedEntry\n) {\n\tconst state = resolveTranslationState(entry, workingEntry);\n\n\treturn {\n\t\tmsgid: entry.msgid,\n\t\tmsgctxt: entry.msgctxt,\n\t\tmsgidPlural: entry.msgidPlural,\n\t\tmsgstr: state.effectiveEntry.msgstr,\n\t\treferences: entry.references,\n\t\textractedComments: entry.extractedComments,\n\t\tflags: state.effectiveEntry.flags,\n\t\tprevious: state.effectiveEntry.previous,\n\t\tobsolete: state.effectiveEntry.obsolete,\n\t\thasTranslation: state.hasTranslation,\n\t\tisFuzzy: state.isFuzzy,\n\t\tisCommittedToWorking: state.translationOrigin === \"working\",\n\t\tmatchesTargetTranslation: state.translationOrigin === \"base\" && state.hasTranslation,\n\t\ttranslationOrigin: state.translationOrigin,\n\t\ttranslationStatus: state.translationStatus\n\t};\n}\n\nexport function toPublicAlternative(\n\tentry: NormalizedEntry,\n\tscore: number,\n\tworkingEntry?: NormalizedEntry\n) {\n\tconst state = resolveTranslationState(entry, workingEntry);\n\n\treturn {\n\t\tmsgid: entry.msgid,\n\t\tmsgctxt: entry.msgctxt,\n\t\tscore,\n\t\treferences: entry.references,\n\t\tmsgstr: state.effectiveEntry.msgstr,\n\t\thasTranslation: state.hasTranslation,\n\t\tisFuzzy: state.isFuzzy,\n\t\tisCommittedToWorking: state.translationOrigin === \"working\",\n\t\tmatchesTargetTranslation: state.translationOrigin === \"base\" && state.hasTranslation,\n\t\ttranslationOrigin: state.translationOrigin,\n\t\ttranslationStatus: state.translationStatus\n\t};\n}\n\r\nexport async function handleContext(request: Request) {\r\n\tconst data = await request.formData();\r\n\tconst key = data.get(\"translationKey\")?.toString().trim();\r\n\tconst currentPath = data.get(\"currentPath\")?.toString().trim();\r\n\r\n\tif (!key || !currentPath) {\r\n\t\treturn json(\r\n\t\t\t{\r\n\t\t\t\tsuccess: false,\r\n\t\t\t\terror: \"translationKey and currentPath are required\"\r\n\t\t\t},\r\n\t\t\t{ status: 400 }\r\n\t\t);\r\n\t}\r\n\r\n\tlet baseFound;\n\ttry {\n\t\tbaseFound = await findTranslationContext(key, currentPath);\n\t} catch (error) {\n\t\tif (error instanceof CatalogIntegrityError) {\n\t\t\treturn json(\n\t\t\t\t{\n\t\t\t\t\tsuccess: false,\n\t\t\t\t\terror: error.message,\n\t\t\t\t\tcode: \"catalog_integrity_error\",\n\t\t\t\t\tissues: error.issues\n\t\t\t\t},\n\t\t\t\t{ status: 409 }\n\t\t\t);\n\t\t}\n\n\t\tthrow error;\n\t}\n\n\tif (!baseFound) {\n\t\treturn json(\n\t\t\t{\r\n\t\t\t\tsuccess: false,\r\n\t\t\t\terror: \"Translation key context not found\"\r\n\t\t\t},\r\n\t\t\t{ status: 404 }\r\n\t\t);\r\n\t}\r\n\r\n\tconst bestBase =\n\t\tbaseFound.baseMap.get(entryKey(baseFound.best.entry.msgid, baseFound.best.entry.msgctxt)) ??\n\t\tbaseFound.best.entry;\n\tconst bestWorking = baseFound.workingMap.get(entryKey(bestBase.msgid, bestBase.msgctxt));\n\r\n\treturn json({\n\t\tsuccess: true,\n\t\tmatch: {\n\t\t\tscore: baseFound.best.score,\n\t\t\tvia: baseFound.best.variant\n\t\t},\n\t\tcatalogState: baseFound.catalogState,\n\t\tentry: toPublicEntry(bestBase, bestWorking),\n\t\talternatives: baseFound.alternatives.map((item) => {\n\t\t\tconst baseAlt =\n\t\t\t\tbaseFound.baseMap.get(entryKey(item.entry.msgid, item.entry.msgctxt)) ?? item.entry;\n\t\t\tconst workingAlt = baseFound.workingMap.get(entryKey(baseAlt.msgid, baseAlt.msgctxt));\n\t\t\treturn toPublicAlternative(baseAlt, item.score, workingAlt);\n\t\t})\n\t});\n}\n", "import { json } from \"@sveltejs/kit\";\nimport type { TranslationContextResult } from \"../client/toggleQA.shared\";\nimport type { SuggestionRequestItem, SuggestionResponseItem } from \"./config.ts\";\nimport { getTranslationHelperConfig } from \"./config.ts\";\n\r\nfunction getEntriesForSuggestions(contextResult: TranslationContextResult) {\r\n\treturn [contextResult.entry, ...contextResult.alternatives];\r\n}\r\n\r\nfunction getTranslatedExamples(contextResult: TranslationContextResult) {\r\n\treturn getEntriesForSuggestions(contextResult)\r\n\t\t.filter((entry) => entry.hasTranslation && entry.msgstr?.[0])\r\n\t\t.slice(0, 30)\r\n\t\t.map((entry) => ({\r\n\t\t\tmsgid: entry.msgid,\r\n\t\t\tmsgctxt: entry.msgctxt,\r\n\t\t\ttranslation: entry.msgstr[0]\r\n\t\t}));\r\n}\r\n\r\nfunction buildUserMessage(\r\n\tcontextResult: TranslationContextResult,\r\n\titems: SuggestionRequestItem[]\r\n) {\r\n\treturn JSON.stringify(\r\n\t\t{\r\n\t\t\ttask: \"Return translation suggestions for the untranslated UI strings.\",\r\n\t\t\toutput_format: {\r\n\t\t\t\titems: [\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tmsgid: \"source string\",\r\n\t\t\t\t\t\tmsgctxt: \"optional context or null\",\r\n\t\t\t\t\t\tsuggestion: \"translated suggestion\"\r\n\t\t\t\t\t}\r\n\t\t\t\t]\r\n\t\t\t},\r\n\t\t\ttranslated_examples: getTranslatedExamples(contextResult),\r\n\t\t\tuntranslated_items: items\r\n\t\t},\r\n\t\tnull,\r\n\t\t2\r\n\t);\r\n}\r\n\r\nfunction parseSuggestions(responseText: string): SuggestionResponseItem[] {\r\n\ttry {\r\n\t\tconst parsed = JSON.parse(responseText);\r\n\t\tconst items = Array.isArray(parsed?.items) ? parsed.items : [];\r\n\t\treturn items.filter(\r\n\t\t\t(item): item is SuggestionResponseItem =>\r\n\t\t\t\ttypeof item?.msgid === \"string\" &&\r\n\t\t\t\t(item.msgctxt === null || typeof item.msgctxt === \"string\") &&\r\n\t\t\t\ttypeof item?.suggestion === \"string\"\r\n\t\t);\r\n\t} catch {\r\n\t\treturn [];\r\n\t}\r\n}\r\n\r\nexport async function handleSuggestions(request: Request) {\n\tconst {\n\t\tapiKey,\n\t\tsuggestionModel,\n\t\tsystemMessage,\n\t\tsourceLocale,\n\t\ttargetLocale,\n\t\tsuggestionProvider\n\t} = getTranslationHelperConfig();\n\n\tconst data = await request.json().catch(() => null);\n\tconst context = data?.context as TranslationContextResult | undefined;\n\tconst items = Array.isArray(data?.items) ? (data.items as SuggestionRequestItem[]) : [];\n\r\n\tif (!context || !items.length) {\r\n\t\treturn json(\r\n\t\t\t{\r\n\t\t\t\tsuccess: false,\r\n\t\t\t\terror: \"context and items are required\"\r\n\t\t\t},\r\n\t\t\t{ status: 400 }\n\t\t);\n\t}\n\n\tif (suggestionProvider) {\n\t\tconst providedItems = await suggestionProvider({\n\t\t\tcontext,\n\t\t\titems,\n\t\t\tsourceLocale,\n\t\t\ttargetLocale,\n\t\t\tsystemMessage,\n\t\t\tmodel: suggestionModel,\n\t\t\tapiKey\n\t\t});\n\n\t\treturn json({\n\t\t\tsuccess: true,\n\t\t\titems: providedItems\n\t\t});\n\t}\n\n\tif (!apiKey.trim()) {\n\t\tconsole.warn(\"[angy] Suggestions disabled because apiKey is empty.\");\n\t\treturn json({\n\t\t\tsuccess: true,\n\t\t\tdisabled: true,\n\t\t\titems: []\n\t\t});\n\t}\n\n\ttry {\n\t\tconsole.info(\"[angy] Suggestion request starting.\", {\n\t\t\tmodel: suggestionModel,\n\t\t\tsourceLocale,\n\t\t\ttargetLocale,\n\t\t\titemCount: items.length,\n\t\t\thasApiKey: Boolean(apiKey.trim())\n\t\t});\n\n\t\tconst response = await fetch(\"https://api.openai.com/v1/responses\", {\n\t\t\tmethod: \"POST\",\n\t\t\theaders: {\n\t\t\t\tAuthorization: `Bearer ${apiKey}`,\n\t\t\t\t\"content-type\": \"application/json\"\n\t\t\t},\n\t\t\tbody: JSON.stringify({\n\t\t\t\tmodel: suggestionModel,\n\t\t\t\treasoning: {\n\t\t\t\t\teffort: \"medium\"\n\t\t\t\t},\n\t\t\t\tinput: [\n\t\t\t\t\t{\n\t\t\t\t\t\trole: \"system\",\n\t\t\t\t\t\tcontent: [{ type: \"input_text\", text: systemMessage }]\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\trole: \"user\",\n\t\t\t\t\t\tcontent: [{ type: \"input_text\", text: buildUserMessage(context, items) }]\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\ttext: {\n\t\t\t\t\tformat: {\n\t\t\t\t\t\ttype: \"json_schema\",\n\t\t\t\t\t\tname: \"translation_suggestions\",\n\t\t\t\t\t\tschema: {\n\t\t\t\t\t\t\ttype: \"object\",\n\t\t\t\t\t\t\tadditionalProperties: false,\n\t\t\t\t\t\t\tproperties: {\n\t\t\t\t\t\t\t\titems: {\n\t\t\t\t\t\t\t\t\ttype: \"array\",\n\t\t\t\t\t\t\t\t\titems: {\n\t\t\t\t\t\t\t\t\t\ttype: \"object\",\n\t\t\t\t\t\t\t\t\t\tadditionalProperties: false,\n\t\t\t\t\t\t\t\t\t\tproperties: {\n\t\t\t\t\t\t\t\t\t\t\tmsgid: { type: \"string\" },\n\t\t\t\t\t\t\t\t\t\t\tmsgctxt: { type: [\"string\", \"null\"] },\n\t\t\t\t\t\t\t\t\t\t\tsuggestion: { type: \"string\" }\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\trequired: [\"msgid\", \"msgctxt\", \"suggestion\"]\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\trequired: [\"items\"]\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t})\n\t\t});\n\n\t\tif (!response.ok) {\n\t\t\tconst errorBody = await response.text().catch(() => \"\");\n\t\t\tconsole.error(\"[angy] Suggestion request failed.\", {\n\t\t\t\tstatus: response.status,\n\t\t\t\tstatusText: response.statusText,\n\t\t\t\tbody: errorBody\n\t\t\t});\n\n\t\t\treturn json(\n\t\t\t\t{\n\t\t\t\t\tsuccess: false,\n\t\t\t\t\terror: `Suggestion request failed: ${response.status}`\n\t\t\t\t},\n\t\t\t\t{ status: 502 }\n\t\t\t);\n\t\t}\n\n\t\tconst responseJson = await response.json();\n\tconst responseText =\n\t\tresponseJson?.output_text ??\n\t\tresponseJson?.output\n\t\t\t?.flatMap((item: any) => item?.content ?? [])\n\t\t\t.find((part: any) => part?.text)?.text ??\n\t\t\"\";\n\n\t\treturn json({\n\t\t\tsuccess: true,\n\t\t\titems: parseSuggestions(responseText)\n\t\t});\n\t} catch (error) {\n\t\tconsole.error(\"[angy] Suggestion request threw.\", error);\n\t\treturn json(\n\t\t\t{\n\t\t\t\tsuccess: false,\n\t\t\t\terror: \"Suggestion request failed before reaching the model\"\n\t\t\t},\n\t\t\t{ status: 502 }\n\t\t);\n\t}\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { json, type RequestHandler } from \"@sveltejs/kit\";\nimport {\n\thandleCommit,\n\thandleCommitBatch,\n\thandlePromoteWorkingPreview,\n\thandleRotateCatalogs,\n\thandleRotatePreflight\n} from \"./commit.ts\";\nimport {\n\tconfigureTranslationHelper,\n\tloadAngyConfigFromRoot,\n\tnormalizeTranslationHelperConfig,\n\ttype TranslationHelperConfig\n} from \"./config.ts\";\nimport { handleContext } from \"./context.ts\";\nimport { handleSuggestions } from \"./suggestions.ts\";\n\nexport async function handleTranslationRequest(\n\trequest: Request,\n\turl: URL,\n\toptions?: { devOnly?: boolean; config?: Partial<TranslationHelperConfig>; dev?: boolean }\n) {\n\tconst devOnly = options?.devOnly ?? true;\n\tconst runtimeDev =\n\t\ttypeof import.meta !== \"undefined\" &&\n\t\ttypeof import.meta.env !== \"undefined\" &&\n\t\timport.meta.env.DEV === true;\n\tconst isDev = options?.dev ?? runtimeDev;\n\n\tif (devOnly && !isDev) {\n\t\treturn json(\n\t\t\t{\n\t\t\t\tsuccess: false,\n\t\t\t\terror: \"Disabled outside dev\"\n\t\t\t},\n\t\t\t{ status: 404 }\n\t\t);\n\t}\n\n\tif (options?.config) {\n\t\tconfigureTranslationHelper(normalizeTranslationHelperConfig(process.cwd(), options.config));\n\t} else {\n\t\tawait loadAngyConfigFromRoot(process.cwd());\n\t}\n\n\tconst intent = url.searchParams.get(\"intent\") ?? \"commit\";\n\n\tif (intent === \"commit-batch\") {\n\t\treturn handleCommitBatch(request);\n\t}\n\n\tif (intent === \"context\") {\n\t\treturn handleContext(request);\n\t}\n\n\tif (intent === \"suggestions\") {\n\t\treturn handleSuggestions(request);\n\t}\n\n\tif (intent === \"rotate-catalogs\") {\n\t\treturn handleRotateCatalogs(request);\n\t}\n\n\tif (intent === \"rotate-preflight\") {\n\t\treturn handleRotatePreflight();\n\t}\n\n\tif (intent === \"promote-working-preview\") {\n\t\treturn handlePromoteWorkingPreview();\n\t}\n\n\treturn handleCommit(request);\n}\n\nfunction createAngyHandler(\n\toptions?: { devOnly?: boolean; config?: Partial<TranslationHelperConfig> }\n): RequestHandler {\n\treturn async ({ request, url }) => handleTranslationRequest(request, url, options);\n}\n\nexport const handler = createAngyHandler();\n\nexport {\n\tdefineAngyConfig,\n\tnormalizeSuggestionModelConfig,\n\ttype AngyConfigInput,\n\ttype SuggestionModelConfig,\n\ttype SuggestionProvider,\n\ttype SuggestionProviderInput,\n\ttype SuggestionRequestItem,\n\ttype SuggestionResponseItem\n} from \"./config.ts\";\nexport {\n\tCatalogIntegrityError,\n\tcollectCatalogIntegrityIssues,\n\tcollectRotationImpact,\n\tgetRotationPreflight,\n\treadCatalogPair,\n\tresolveTranslationState\n} from \"./catalog.ts\";\nexport { buildSuggestionRequestBody } from \"./suggestions.ts\";\n", "import { json } from \"@sveltejs/kit\";\nimport { basename } from \"node:path\";\nimport {\n\tCatalogIntegrityError,\n\tgetRotationPreflight,\n\tensureWorkingCatalog,\n\tpromoteWorkingDraftToRuntime,\n\treadCatalogPair,\n\treadParsedCatalog,\n\tremoveFuzzyFlag,\n\trotateCatalogs,\n\truntimeTranslations,\n\twriteWorkingCatalog\n} from \"./catalog.ts\";\nimport type { CommitBatchItem, PoTranslationEntry } from \"./types.ts\";\nimport { getTranslationHelperConfig, inferLocaleFromCatalogPath } from \"./config.ts\";\n\r\nfunction runtimeKey(msgid: string, msgctxt: string | null) {\r\n\treturn `${msgctxt ?? \"\"}::${msgid}`;\r\n}\r\n\r\nexport async function writeTranslationToWorkingCatalog(\n\tresolvedMsgid: string,\n\tresolvedMsgctxt: string | null,\n\ttranslationValue: string\n) {\n\tawait ensureWorkingCatalog();\n\n\tawait readCatalogPair({ ensureWorking: true });\n\n\tconst [baseParsed, workingParsed] = await Promise.all([readParsedCatalog(\"base\"), readParsedCatalog(\"working\")]);\n\n\tif (!baseParsed || !workingParsed) {\n\t\treturn { ok: false as const, error: \"Unable to load catalogs\" };\n\t}\n\r\n\tconst baseTranslations = baseParsed.translations ?? {};\r\n\tconst workingTranslations = workingParsed.translations ?? {};\r\n\r\n\tconst ctxKey = resolvedMsgctxt ?? \"\";\r\n\tconst baseGroup = baseTranslations[ctxKey];\r\n\tif (!baseGroup) {\r\n\t\treturn { ok: false as const, error: \"Context group not found in base catalog\" };\r\n\t}\r\n\r\n\tconst baseEntry = baseGroup[resolvedMsgid] as PoTranslationEntry | undefined;\r\n\tif (!baseEntry) {\r\n\t\treturn { ok: false as const, error: \"Target msgid not found in base catalog\" };\r\n\t}\r\n\r\n\tconst workingGroup = (workingTranslations[ctxKey] ??= {});\r\n\tconst entry = ((workingGroup[resolvedMsgid] as PoTranslationEntry | undefined) ??=\r\n\t\tcloneEntryForWorking(baseEntry, resolvedMsgid, resolvedMsgctxt));\r\n\r\n\tentry.msgstr = [translationValue];\r\n\tentry.comments ??= {};\r\n\tentry.comments.flag = removeFuzzyFlag(entry.comments.flag);\r\n\r\n\tawait writeWorkingCatalog(workingParsed);\r\n\r\n\truntimeTranslations.set(runtimeKey(resolvedMsgid, resolvedMsgctxt), translationValue);\n\n\tconst workingCatalogName = inferLocaleFromCatalogPath(getTranslationHelperConfig().workingPoPath) ?? \"working\";\n\n\treturn {\n\t\tok: true as const,\n\t\tmsgid: resolvedMsgid,\n\t\tmsgctxt: resolvedMsgctxt,\n\t\tworkingCatalog: workingCatalogName\n\t};\n}\n\r\nfunction cloneEntryForWorking(\r\n\tbaseEntry: PoTranslationEntry,\r\n\tmsgid: string,\r\n\tmsgctxt: string | null\r\n): PoTranslationEntry {\r\n\treturn {\r\n\t\tmsgid,\r\n\t\tmsgctxt: msgctxt ?? undefined,\r\n\t\tmsgid_plural: baseEntry.msgid_plural,\r\n\t\tmsgstr: Array.isArray(baseEntry.msgstr) ? [...baseEntry.msgstr] : [\"\"],\r\n\t\tobsolete: Boolean(baseEntry.obsolete),\r\n\t\tcomments: baseEntry.comments\r\n\t\t\t? {\r\n\t\t\t\t\treference: baseEntry.comments.reference,\r\n\t\t\t\t\textracted: baseEntry.comments.extracted,\r\n\t\t\t\t\tflag: baseEntry.comments.flag,\r\n\t\t\t\t\tprevious: baseEntry.comments.previous\r\n\t\t\t\t}\r\n\t\t\t: undefined\r\n\t};\r\n}\r\n\r\nexport async function handleCommitBatch(request: Request) {\n\tconst data = await request.json().catch(() => null);\n\tconst items = Array.isArray(data?.items) ? (data.items as CommitBatchItem[]) : [];\n\r\n\tif (!items.length) {\n\t\treturn json(\r\n\t\t\t{\r\n\t\t\t\tsuccess: false,\r\n\t\t\t\terror: \"No items to commit\"\r\n\t\t\t},\r\n\t\t\t{ status: 400 }\r\n\t\t);\r\n\t}\n\n\ttry {\n\t\tawait readCatalogPair({ ensureWorking: true });\n\t} catch (error) {\n\t\tif (error instanceof CatalogIntegrityError) {\n\t\t\treturn json(\n\t\t\t\t{\n\t\t\t\t\tsuccess: false,\n\t\t\t\t\terror: error.message,\n\t\t\t\t\tcode: \"catalog_integrity_error\",\n\t\t\t\t\tissues: error.issues\n\t\t\t\t},\n\t\t\t\t{ status: 409 }\n\t\t\t);\n\t\t}\n\n\t\tthrow error;\n\t}\n\n\tconst results: Array<{ msgid: string; msgctxt: string | null; ok: boolean; error?: string }> = [];\n\r\n\tfor (const item of items) {\r\n\t\tconst resolvedMsgid = item.resolvedMsgid?.trim();\r\n\t\tconst resolvedMsgctxt =\r\n\t\t\titem.resolvedMsgctxt && item.resolvedMsgctxt.trim().length > 0\r\n\t\t\t\t? item.resolvedMsgctxt.trim()\r\n\t\t\t\t: null;\r\n\t\tconst translationValue = item.translationValue?.trim();\r\n\r\n\t\tif (!resolvedMsgid || !translationValue) {\r\n\t\t\tresults.push({\r\n\t\t\t\tmsgid: resolvedMsgid ?? \"\",\r\n\t\t\t\tmsgctxt: resolvedMsgctxt,\r\n\t\t\t\tok: false,\r\n\t\t\t\terror: \"resolvedMsgid and translationValue are required\"\r\n\t\t\t});\r\n\t\t\tcontinue;\r\n\t\t}\r\n\r\n\t\tconst result = await writeTranslationToWorkingCatalog(\r\n\t\t\tresolvedMsgid,\r\n\t\t\tresolvedMsgctxt,\r\n\t\t\ttranslationValue\r\n\t\t);\r\n\r\n\t\tif (!result.ok) {\r\n\t\t\tresults.push({\r\n\t\t\t\tmsgid: resolvedMsgid,\r\n\t\t\t\tmsgctxt: resolvedMsgctxt,\r\n\t\t\t\tok: false,\r\n\t\t\t\terror: result.error\r\n\t\t\t});\r\n\t\t\tcontinue;\r\n\t\t}\r\n\r\n\t\tresults.push({\r\n\t\t\tmsgid: resolvedMsgid,\r\n\t\t\tmsgctxt: resolvedMsgctxt,\r\n\t\t\tok: true\r\n\t\t});\r\n\t}\r\n\r\n\tconst failed = results.filter((item) => !item.ok);\r\n\tif (failed.length) {\r\n\t\treturn json(\r\n\t\t\t{\r\n\t\t\t\tsuccess: false,\r\n\t\t\t\terror: \"Some translations failed to commit\",\r\n\t\t\t\tresults\r\n\t\t\t},\r\n\t\t\t{ status: 207 }\r\n\t\t);\r\n\t}\r\n\r\n\treturn json({\n\t\tsuccess: true,\n\t\tmessage: `Committed ${results.length} translations to Angy draft working catalog`,\n\t\tresults\n\t});\n}\n\r\nexport async function handleCommit(request: Request) {\n\tconst data = await request.formData();\r\n\tconst resolvedMsgid = data.get(\"resolvedMsgid\")?.toString().trim();\r\n\tconst resolvedMsgctxtRaw = data.get(\"resolvedMsgctxt\")?.toString();\r\n\tconst translationValue = data.get(\"translationValue\")?.toString().trim();\r\n\tconst resolvedMsgctxt =\r\n\t\tresolvedMsgctxtRaw && resolvedMsgctxtRaw.trim().length > 0\r\n\t\t\t? resolvedMsgctxtRaw.trim()\r\n\t\t\t: null;\r\n\r\n\tif (!resolvedMsgid || !translationValue) {\r\n\t\treturn json(\r\n\t\t\t{\r\n\t\t\t\tsuccess: false,\r\n\t\t\t\terror: \"resolvedMsgid and translationValue are required\"\r\n\t\t\t},\r\n\t\t\t{ status: 400 }\r\n\t\t);\r\n\t}\r\n\r\n\tconst result = await writeTranslationToWorkingCatalog(\n\t\tresolvedMsgid,\n\t\tresolvedMsgctxt,\n\t\ttranslationValue\n\t);\n\r\n\tif (!result.ok) {\r\n\t\treturn json(\r\n\t\t\t{\r\n\t\t\t\tsuccess: false,\r\n\t\t\t\terror: result.error\r\n\t\t\t},\r\n\t\t\t{ status: 404 }\r\n\t\t);\r\n\t}\r\n\r\n\treturn json({\n\t\tsuccess: true,\n\t\tmessage: \"Translation written to Angy draft working catalog\",\n\t\tmsgid: result.msgid,\n\t\tmsgctxt: result.msgctxt,\n\t\tworkingCatalog: result.workingCatalog\n\t});\n}\n\nexport async function handlePromoteWorkingPreview() {\n\ttry {\n\t\tawait readCatalogPair({ ensureWorking: true });\n\t\tawait promoteWorkingDraftToRuntime();\n\n\t\treturn json({\n\t\t\tsuccess: true,\n\t\t\tmessage: `Promoted Angy draft into ${basename(getTranslationHelperConfig().workingPoPath)}`\n\t\t});\n\t} catch (error) {\n\t\tif (error instanceof CatalogIntegrityError) {\n\t\t\treturn json(\n\t\t\t\t{\n\t\t\t\t\tsuccess: false,\n\t\t\t\t\terror: error.message,\n\t\t\t\t\tcode: \"catalog_integrity_error\",\n\t\t\t\t\tissues: error.issues\n\t\t\t\t},\n\t\t\t\t{ status: 409 }\n\t\t\t);\n\t\t}\n\n\t\tthrow error;\n\t}\n}\n\nexport async function handleRotatePreflight() {\n\ttry {\n\t\tconst preflight = await getRotationPreflight();\n\t\treturn json({\n\t\t\tsuccess: true,\n\t\t\tsafe: preflight.safe,\n\t\t\tstatus: preflight.status,\n\t\t\taffected: preflight.affected\n\t\t});\n\t} catch (error) {\n\t\tif (error instanceof CatalogIntegrityError) {\n\t\t\treturn json(\n\t\t\t\t{\n\t\t\t\t\tsuccess: false,\n\t\t\t\t\terror: error.message,\n\t\t\t\t\tcode: \"catalog_integrity_error\",\n\t\t\t\t\tissues: error.issues\n\t\t\t\t},\n\t\t\t\t{ status: 409 }\n\t\t\t);\n\t\t}\n\n\t\tthrow error;\n\t}\n}\n\nexport async function handleRotateCatalogs(request: Request) {\n\tconst data = await request.json().catch(() => null);\n\tconst confirmDestructive = data?.confirmDestructive === true;\n\tconst preflight = await getRotationPreflight();\n\n\tif (!preflight.safe && !confirmDestructive) {\n\t\treturn json(\n\t\t\t{\n\t\t\t\tsuccess: false,\n\t\t\t\terror: \"Catalog rotation would overwrite working-only translations. Confirm rotation explicitly to continue.\",\n\t\t\t\tcode: \"rotation_confirmation_required\",\n\t\t\t\tstatus: preflight.status,\n\t\t\t\taffected: preflight.affected\n\t\t\t},\n\t\t\t{ status: 409 }\n\t\t);\n\t}\n\n\tconst result = await rotateCatalogs({ allowOutOfSync: confirmDestructive });\n\n\tif (!result.ok) {\n\t\treturn json(\n\t\t\t{\n\t\t\t\tsuccess: false,\n\t\t\t\terror: result.error,\n\t\t\t\tstatus: \"status\" in result ? result.status : undefined,\n\t\t\t\taffected: \"affected\" in result ? result.affected : undefined\n\t\t\t},\n\t\t\t{ status: \"status\" in result && result.status === \"out_of_sync\" ? 409 : 400 }\n\t\t);\n\t}\n\n\treturn json({\n\t\tsuccess: true,\n\t\tmessage: \"Catalogs rotated\",\n\t\tbasePoPath: result.basePoPath,\n\t\tworkingPoPath: result.workingPoPath,\n\t\tbaseBackupPath: result.baseBackupPath,\n\t\tworkingBackupPath: result.workingBackupPath\n\t});\n}\n", "import { constants as fsConstants } from \"node:fs\";\nimport { access, copyFile, readFile, rename, rm, writeFile } from \"node:fs/promises\";\nimport { dirname, extname, join, basename } from \"node:path\";\nimport gettextParser from \"gettext-parser\";\nimport Fuse from \"fuse.js\";\nimport type {\n\tCatalogIntegrityIssue,\n\tNormalizedEntry,\n\tPoTranslationEntry,\n\tRotationImpactItem,\n\tTranslationOrigin,\n\tTranslationStatus\n} from \"./types.ts\";\nimport { getTranslationHelperConfig, suspendWorkingCatalogWatch } from \"./config.ts\";\n\nexport const runtimeTranslations = new Map<string, string>();\n\nexport class CatalogIntegrityError extends Error {\n\tissues: CatalogIntegrityIssue[];\n\n\tconstructor(message: string, issues: CatalogIntegrityIssue[]) {\n\t\tsuper(message);\n\t\tthis.name = \"CatalogIntegrityError\";\n\t\tthis.issues = issues;\n\t}\n}\n\ntype CatalogPair = {\n\tbaseEntries: NormalizedEntry[];\n\tworkingEntries: NormalizedEntry[];\n\tbaseMap: Map<string, NormalizedEntry>;\n\tworkingMap: Map<string, NormalizedEntry>;\n\trotationImpact: RotationImpactItem[];\n};\n\nexport function catalogEntryKey(msgid: string, msgctxt: string | null) {\n\treturn `${msgctxt ?? \"\"}::${msgid}`;\n}\n\r\nexport function normalizeWhitespace(value: string): string {\r\n\treturn value.replace(/\\s+/g, \" \").trim();\r\n}\r\n\r\nexport function normalizeForLookup(value: string): string {\r\n\treturn normalizeWhitespace(value)\r\n\t\t.replace(/<\\/?[a-z][^>]*>/gi, \"<x>\")\r\n\t\t.replace(/<\\/?\\d+>/g, \"<x>\")\r\n\t\t.replace(/\\{\\{?\\s*[^}]+\\s*\\}?\\}/g, \"{0}\")\r\n\t\t.replace(/\\{\\d+\\}/g, \"{0}\")\r\n\t\t.replace(/[\"\"'`\u00C2\u00B4]/g, \"'\")\r\n\t\t.toLowerCase();\r\n}\r\n\r\nexport function tokenizeForLookup(value: string): string[] {\r\n\treturn normalizeForLookup(value)\r\n\t\t.split(/[\\s,.;:!?()[\\]{}]+/)\r\n\t\t.map((token) => token.trim())\r\n\t\t.filter(Boolean);\r\n}\r\n\r\nexport function buildLookupVariants(raw: string): string[] {\r\n\tconst normalized = normalizeForLookup(raw);\r\n\tconst variants = new Set<string>([normalized]);\r\n\r\n\tvariants.add(normalized.replace(/<x>/g, \"<0>\"));\r\n\tvariants.add(normalized.replace(/<x>/g, \"\").replace(/\\s+/g, \" \").trim());\r\n\tvariants.add(normalized.replace(/\\{0\\}/g, \"\").replace(/\\s+/g, \" \").trim());\r\n\r\n\treturn [...variants].filter(Boolean);\r\n}\r\n\r\nexport function flattenPoEntries(parsed: any, translationOrigin: TranslationOrigin): NormalizedEntry[] {\r\n\tconst entries: NormalizedEntry[] = [];\r\n\tconst translations = parsed?.translations ?? {};\r\n\r\n\tfor (const [ctxKey, group] of Object.entries(translations)) {\r\n\t\tfor (const [msgidKey, raw] of Object.entries(group as Record<string, unknown>)) {\r\n\t\t\tif (!msgidKey) continue;\r\n\r\n\t\t\tconst entry = raw as PoTranslationEntry;\r\n\t\t\tconst msgid = entry.msgid ?? msgidKey;\r\n\t\t\tconst msgctxt = entry.msgctxt ?? (ctxKey === \"\" ? null : ctxKey);\r\n\t\t\tconst msgidPlural = entry.msgid_plural ?? null;\r\n\t\t\tconst msgstr = Array.isArray(entry.msgstr) ? entry.msgstr.filter(Boolean) : [];\r\n\t\t\tconst references = entry.comments?.reference\r\n\t\t\t\t? entry.comments.reference.split(\"\\n\").map((item) => item.trim()).filter(Boolean)\r\n\t\t\t\t: [];\r\n\t\t\tconst extractedComments = entry.comments?.extracted\r\n\t\t\t\t? entry.comments.extracted.split(\"\\n\").map((item) => item.trim()).filter(Boolean)\r\n\t\t\t\t: [];\r\n\t\t\tconst flags = entry.comments?.flag\r\n\t\t\t\t? entry.comments.flag.split(\",\").map((item) => item.trim()).filter(Boolean)\r\n\t\t\t\t: [];\r\n\t\t\tconst previous = entry.comments?.previous\r\n\t\t\t\t? entry.comments.previous.split(\"\\n\").map((item) => item.trim()).filter(Boolean)\r\n\t\t\t\t: [];\r\n\t\t\tconst translated = entry.msgstr?.some((item) => item.trim().length > 0) ?? false;\r\n\t\t\tconst fuzzy = entry.comments?.flag?.includes(\"fuzzy\");\r\n\r\n\t\t\tentries.push({\r\n\t\t\t\tmsgid,\r\n\t\t\t\tmsgctxt,\r\n\t\t\t\tmsgidPlural,\r\n\t\t\t\tmsgstr,\r\n\t\t\t\treferences,\r\n\t\t\t\textractedComments,\r\n\t\t\t\tflags,\r\n\t\t\t\tprevious,\r\n\t\t\t\tobsolete: Boolean(entry.obsolete),\r\n\t\t\t\tsearchText: normalizeForLookup(msgid),\r\n\t\t\t\tsearchTokens: tokenizeForLookup(msgid),\r\n\t\t\t\tisFuzzy: fuzzy,\r\n\t\t\t\thasTranslation: translated,\r\n\t\t\t\ttranslationOrigin\r\n\t\t\t});\r\n\t\t}\r\n\t}\r\n\r\n\treturn entries;\r\n}\r\n\r\nexport function createFuse(entries: NormalizedEntry[]) {\r\n\treturn new Fuse(entries, {\r\n\t\tincludeScore: true,\r\n\t\tignoreLocation: true,\r\n\t\tthreshold: 0.34,\r\n\t\tminMatchCharLength: 2,\r\n\t\tkeys: [\r\n\t\t\t{ name: \"msgid\", weight: 0.7 },\r\n\t\t\t{ name: \"searchText\", weight: 0.2 },\r\n\t\t\t{ name: \"references\", weight: 0.1 }\r\n\t\t]\r\n\t});\r\n}\r\n\r\nexport function buildEntryMap(entries: NormalizedEntry[]) {\n\treturn new Map(entries.map((entry) => [catalogEntryKey(entry.msgid, entry.msgctxt), entry]));\n}\n\nexport function getEntryValue(entry?: Pick<NormalizedEntry, \"msgstr\"> | null) {\n\tif (!entry?.msgstr?.length) return \"\";\n\treturn entry.msgstr.find((item) => item.trim().length > 0)?.trim() ?? \"\";\n}\n\nexport function applyWorkingState(\n\tbaseEntry: NormalizedEntry,\n\tworkingEntry?: NormalizedEntry\n): NormalizedEntry {\n\tif (!workingEntry) {\r\n\t\treturn baseEntry;\r\n\t}\r\n\r\n\treturn {\r\n\t\t...baseEntry,\r\n\t\tmsgstr: workingEntry.msgstr,\r\n\t\tflags: workingEntry.flags,\r\n\t\tprevious: workingEntry.previous,\r\n\t\tobsolete: workingEntry.obsolete,\r\n\t\thasTranslation: workingEntry.hasTranslation,\r\n\t\tisFuzzy: workingEntry.isFuzzy,\r\n\t\ttranslationOrigin: workingEntry.translationOrigin\r\n\t};\r\n}\r\n\r\nexport async function fileExists(path: string) {\r\n\ttry {\r\n\t\tawait access(path, fsConstants.F_OK);\r\n\t\treturn true;\r\n\t} catch {\r\n\t\treturn false;\r\n\t}\r\n}\r\n\r\nexport async function readCatIndex(catalog: TranslationOrigin) {\n\tconst { basePoPath } = getTranslationHelperConfig();\n\tlet path = \"\";\n\n\tif (catalog === \"base\") {\n\t\tpath = basePoPath;\n\t} else {\n\t\tpath = await getEffectiveWorkingPoPath();\n\t\tconst exists = await fileExists(path);\n\t\tif (!exists) return null;\n\t}\n\r\n\tconst raw = await readFile(path);\r\n\tconst parsed = gettextParser.po.parse(raw);\r\n\tconst entries = flattenPoEntries(parsed, catalog);\r\n\r\n\treturn {\r\n\t\tentries,\r\n\t\tfuse: createFuse(entries)\r\n\t};\r\n}\r\n\r\nexport async function readParsedCatalog(catalog: TranslationOrigin) {\n\tconst { basePoPath } = getTranslationHelperConfig();\n\tconst path = catalog === \"base\" ? basePoPath : await getEffectiveWorkingPoPath();\n\r\n\tif (catalog === \"working\") {\r\n\t\tconst exists = await fileExists(path);\r\n\t\tif (!exists) return null;\r\n\t}\r\n\r\n\tconst raw = await readFile(path);\r\n\treturn gettextParser.po.parse(raw);\r\n}\r\n\r\nexport async function ensureWorkingCatalog() {\n\tconst { basePoPath, workingPoPath } = getTranslationHelperConfig();\n\tconst exists = await fileExists(workingPoPath);\n\tif (!exists) {\n\t\tawait copyFile(basePoPath, workingPoPath);\n\t}\n}\n\nexport function getWorkingDraftPoPath() {\n\tconst { workingPoPath } = getTranslationHelperConfig();\n\tconst extension = extname(workingPoPath);\n\tconst stem = workingPoPath.slice(0, workingPoPath.length - extension.length);\n\treturn `${stem}.angy-draft${extension}`;\n}\n\nasync function getEffectiveWorkingPoPath() {\n\tconst draftPoPath = getWorkingDraftPoPath();\n\tif (await fileExists(draftPoPath)) {\n\t\treturn draftPoPath;\n\t}\n\n\treturn getTranslationHelperConfig().workingPoPath;\n}\n\nexport async function ensureWorkingDraftCatalog() {\n\tawait ensureWorkingCatalog();\n\n\tconst { workingPoPath } = getTranslationHelperConfig();\n\tconst draftPoPath = getWorkingDraftPoPath();\n\tconst exists = await fileExists(draftPoPath);\n\tif (!exists) {\n\t\tawait copyFile(workingPoPath, draftPoPath);\n\t}\n}\n\r\nexport function removeFuzzyFlag(flagString?: string) {\r\n\tif (!flagString) return \"\";\r\n\r\n\treturn flagString\r\n\t\t.split(\",\")\r\n\t\t.map((item) => item.trim())\r\n\t\t.filter(Boolean)\r\n\t\t.filter((item) => item !== \"fuzzy\")\r\n\t\t.join(\", \");\r\n}\r\n\r\nexport async function writeWorkingCatalog(parsed: any) {\n\tawait ensureWorkingDraftCatalog();\n\tconst draftPoPath = getWorkingDraftPoPath();\n\tconst compiled = gettextParser.po.compile(parsed);\n\tawait writeFile(draftPoPath, compiled);\n}\n\nexport async function promoteWorkingDraftToRuntime() {\n\tawait ensureWorkingDraftCatalog();\n\n\tconst { workingPoPath } = getTranslationHelperConfig();\n\tconst draftPoPath = getWorkingDraftPoPath();\n\tconst publishPath = `${workingPoPath}.angy-publish`;\n\tconst compiled = await readFile(draftPoPath);\n\n\tsuspendWorkingCatalogWatch(workingPoPath);\n\tawait writeFile(publishPath, compiled);\n\tawait rm(workingPoPath, { force: true });\n\tawait rename(publishPath, workingPoPath);\n}\n\nexport function collectCatalogIntegrityIssues(\n\tbaseEntries: NormalizedEntry[],\n\tworkingEntries: NormalizedEntry[]\n) {\n\tconst issues: CatalogIntegrityIssue[] = [];\n\tconst baseMap = buildEntryMap(baseEntries);\n\tconst workingMap = buildEntryMap(workingEntries);\n\n\tfor (const baseEntry of baseEntries) {\n\t\tconst key = catalogEntryKey(baseEntry.msgid, baseEntry.msgctxt);\n\t\tconst workingEntry = workingMap.get(key);\n\t\tif (!workingEntry) {\n\t\t\tissues.push({\n\t\t\t\ttype: \"key_mismatch\",\n\t\t\t\tmsgid: baseEntry.msgid,\n\t\t\t\tmsgctxt: baseEntry.msgctxt,\n\t\t\t\treason: \"missing_in_working\"\n\t\t\t});\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst baseValue = getEntryValue(baseEntry);\n\t\tconst workingValue = getEntryValue(workingEntry);\n\t\tif (baseValue && !workingValue) {\n\t\t\tissues.push({\n\t\t\t\ttype: \"missing_working_translation\",\n\t\t\t\tmsgid: baseEntry.msgid,\n\t\t\t\tmsgctxt: baseEntry.msgctxt,\n\t\t\t\tbaseValue\n\t\t\t});\n\t\t}\n\t}\n\n\tfor (const workingEntry of workingEntries) {\n\t\tconst key = catalogEntryKey(workingEntry.msgid, workingEntry.msgctxt);\n\t\tif (!baseMap.has(key)) {\n\t\t\tissues.push({\n\t\t\t\ttype: \"key_mismatch\",\n\t\t\t\tmsgid: workingEntry.msgid,\n\t\t\t\tmsgctxt: workingEntry.msgctxt,\n\t\t\t\treason: \"missing_in_base\"\n\t\t\t});\n\t\t}\n\t}\n\n\treturn issues;\n}\n\nexport function assertCatalogIntegrity(baseEntries: NormalizedEntry[], workingEntries: NormalizedEntry[]) {\n\tconst issues = collectCatalogIntegrityIssues(baseEntries, workingEntries);\n\tif (issues.length) {\n\t\tthrow new CatalogIntegrityError(\n\t\t\t\"Working catalog is out of sync with the base catalog. Regenerate or replace the working catalog before using Angy.\",\n\t\t\tissues\n\t\t);\n\t}\n}\n\nexport function collectRotationImpact(\n\tbaseEntries: NormalizedEntry[],\n\tworkingMap: Map<string, NormalizedEntry>\n) {\n\tconst impacted: RotationImpactItem[] = [];\n\n\tfor (const baseEntry of baseEntries) {\n\t\tconst workingEntry = workingMap.get(catalogEntryKey(baseEntry.msgid, baseEntry.msgctxt));\n\t\tif (!workingEntry) continue;\n\n\t\tconst baseValue = getEntryValue(baseEntry);\n\t\tconst workingValue = getEntryValue(workingEntry);\n\t\tif (!workingValue) continue;\n\t\tif (baseValue === workingValue) continue;\n\n\t\timpacted.push({\n\t\t\tmsgid: baseEntry.msgid,\n\t\t\tmsgctxt: baseEntry.msgctxt,\n\t\t\tbaseValue,\n\t\t\tworkingValue\n\t\t});\n\t}\n\n\treturn impacted;\n}\n\nexport function resolveTranslationState(\n\tbaseEntry: NormalizedEntry,\n\tworkingEntry?: NormalizedEntry\n) {\n\tconst baseValue = getEntryValue(baseEntry);\n\tconst workingValue = getEntryValue(workingEntry);\n\tconst activeEntry = workingEntry && workingValue ? workingEntry : baseEntry;\n\tconst fuzzy = Boolean(activeEntry.isFuzzy);\n\tlet translationOrigin: TranslationOrigin = \"base\";\n\tlet translationStatus: TranslationStatus = \"none\";\n\tlet effectiveEntry = baseEntry;\n\n\tif (workingEntry && workingValue) {\n\t\teffectiveEntry = workingEntry;\n\t\tif (!baseValue || workingValue !== baseValue) {\n\t\t\ttranslationOrigin = \"working\";\n\t\t\ttranslationStatus = \"working\";\n\t\t} else {\n\t\t\ttranslationOrigin = \"base\";\n\t\t\ttranslationStatus = \"base\";\n\t\t}\n\t} else if (baseValue) {\n\t\teffectiveEntry = baseEntry;\n\t\ttranslationOrigin = \"base\";\n\t\ttranslationStatus = \"base\";\n\t}\n\n\tif (fuzzy) {\n\t\ttranslationStatus = \"fuzzy\";\n\t}\n\n\treturn {\n\t\teffectiveEntry,\n\t\tbaseValue,\n\t\tworkingValue,\n\t\ttranslationOrigin,\n\t\ttranslationStatus,\n\t\thasTranslation: Boolean(getEntryValue(effectiveEntry)),\n\t\tisFuzzy: fuzzy\n\t};\n}\n\nexport async function readCatalogPair(options?: { ensureWorking?: boolean }): Promise<CatalogPair> {\n\tif (options?.ensureWorking) {\n\t\tawait ensureWorkingCatalog();\n\t}\n\n\tconst [baseParsed, workingParsed] = await Promise.all([\n\t\treadParsedCatalog(\"base\"),\n\t\treadParsedCatalog(\"working\")\n\t]);\n\n\tif (!baseParsed || !workingParsed) {\n\t\tthrow new Error(\"Unable to load base and working catalogs.\");\n\t}\n\n\tconst baseEntries = flattenPoEntries(baseParsed, \"base\");\n\tconst workingEntries = flattenPoEntries(workingParsed, \"working\");\n\tassertCatalogIntegrity(baseEntries, workingEntries);\n\n\tconst baseMap = buildEntryMap(baseEntries);\n\tconst workingMap = buildEntryMap(workingEntries);\n\tconst rotationImpact = collectRotationImpact(baseEntries, workingMap);\n\n\treturn {\n\t\tbaseEntries,\n\t\tworkingEntries,\n\t\tbaseMap,\n\t\tworkingMap,\n\t\trotationImpact\n\t};\n}\n\nexport async function getRotationPreflight() {\n\tconst { rotationImpact } = await readCatalogPair({ ensureWorking: true });\n\n\treturn {\n\t\tsafe: true as const,\n\t\tstatus: \"ok\" as const,\n\t\taffected: rotationImpact\n\t};\n}\n\nfunction timestampForBackup() {\n\treturn new Date().toISOString().replace(/[:.]/g, \"-\");\n}\n\nfunction buildBackupPath(path: string, label: string) {\n\tconst directory = dirname(path);\n\tconst extension = extname(path);\n\tconst stem = basename(path, extension);\n\treturn join(directory, `${stem}.${label}.${timestampForBackup()}${extension}`);\n}\n\nexport async function rotateCatalogs(options?: { allowOutOfSync?: boolean }) {\n\tconst { basePoPath, workingPoPath } = getTranslationHelperConfig();\n\tconst draftPoPath = getWorkingDraftPoPath();\n\tconst effectiveWorkingPoPath = await getEffectiveWorkingPoPath();\n\tconst workingExists = await fileExists(effectiveWorkingPoPath);\n\n\tif (!workingExists) {\n\t\treturn { ok: false as const, error: \"Working catalog does not exist\" };\n\t}\n\n\tconst baseBackupPath = buildBackupPath(basePoPath, \"base-backup\");\n\tconst workingBackupPath = buildBackupPath(workingPoPath, \"working-backup\");\n\n\tawait copyFile(basePoPath, baseBackupPath);\n\tawait copyFile(effectiveWorkingPoPath, workingBackupPath);\n\tawait copyFile(effectiveWorkingPoPath, basePoPath);\n\tawait copyFile(basePoPath, workingPoPath);\n\tawait copyFile(workingPoPath, draftPoPath);\n\n\treturn {\n\t\tok: true as const,\n\t\tbasePoPath,\n\t\tworkingPoPath,\n\t\tbaseBackupPath,\n\t\tworkingBackupPath\n\t};\n}\n", "import { readFile, unlink, writeFile } from \"node:fs/promises\";\nimport { basename, dirname, extname, isAbsolute, normalize, resolve } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { pathToFileURL } from \"node:url\";\nimport { transform } from \"esbuild\";\nimport type { TranslationContextResult } from \"../client/toggleQA.shared\";\n\nconst __dirname = dirname(fileURLToPath(import.meta.url));\nconst CONFIG_FILENAMES = [\n\t\"angy.config.ts\",\n\t\"angy.config.js\",\n\t\"angy.config.mjs\",\n\t\"angy.config.cjs\"\n];\n\nexport type TranslationHelperConfig = {\n\tbasePoPath: string;\n\tworkingPoPath: string;\n\tsourceLocale: string;\n\ttargetLocale: string;\n\troutePath: string;\n\tapiKey: string;\n\tsystemMessage: string;\n\tsuggestionModel: SuggestionModelConfig;\n\twatchIgnore: string[];\n\tsuggestionProvider?: SuggestionProvider;\n};\n\nexport type SuggestionRequestItem = {\n\tmsgid: string;\n\tmsgctxt: string | null;\n};\n\nexport type SuggestionResponseItem = {\n\tmsgid: string;\n\tmsgctxt: string | null;\n\tsuggestion: string;\n};\n\nexport type SuggestionProviderInput = {\n\tcontext: TranslationContextResult;\n\titems: SuggestionRequestItem[];\n\tsourceLocale: string;\n\ttargetLocale: string;\n\tsystemMessage: string;\n\tsuggestionModel: SuggestionModelConfig;\n\tapiKey: string | undefined;\n};\n\nexport type SuggestionProvider = (\n\tinput: SuggestionProviderInput\n) => Promise<SuggestionResponseItem[]>;\n\nexport type TranslationHelperUserConfig = Partial<TranslationHelperConfig>;\n\nconst NON_REASONING_SUGGESTION_MODELS = [\n\t\"gpt-4.1\",\n\t\"gpt-4.1-mini\",\n\t\"gpt-4.1-nano\"\n] as const;\nconst GPT54_REASONING_EFFORTS = [\"none\", \"low\", \"medium\", \"high\", \"xhigh\"] as const;\nconst GPT51_REASONING_EFFORTS = [\"none\", \"low\", \"medium\", \"high\"] as const;\nconst GPT5_REASONING_EFFORTS = [\"minimal\", \"low\", \"medium\", \"high\"] as const;\nconst GPT5_PRO_REASONING_EFFORTS = [\"high\"] as const;\nconst GPT5X_PRO_REASONING_EFFORTS = [\"medium\", \"high\", \"xhigh\"] as const;\n\ntype NonReasoningSuggestionModelName = (typeof NON_REASONING_SUGGESTION_MODELS)[number];\nexport type SuggestionReasoningEffort =\n\t| (typeof GPT54_REASONING_EFFORTS)[number]\n\t| (typeof GPT51_REASONING_EFFORTS)[number]\n\t| (typeof GPT5_REASONING_EFFORTS)[number];\n\ntype NonReasoningSuggestionModel = {\n\tmodel: NonReasoningSuggestionModelName;\n\treasoning?: null;\n};\n\ntype SuggestionModelWithReasoning<\n\tTModel extends string,\n\tTEffort extends readonly string[]\n> = {\n\tmodel: TModel;\n\treasoning?: TEffort[number];\n};\n\nexport type SuggestionModelConfig =\n\t| NonReasoningSuggestionModel\n\t| SuggestionModelWithReasoning<\"gpt-5.4\", typeof GPT54_REASONING_EFFORTS>\n\t| SuggestionModelWithReasoning<\"gpt-5.4-mini\", typeof GPT54_REASONING_EFFORTS>\n\t| SuggestionModelWithReasoning<\"gpt-5.4-nano\", typeof GPT54_REASONING_EFFORTS>\n\t| SuggestionModelWithReasoning<\"gpt-5.2\", typeof GPT54_REASONING_EFFORTS>\n\t| SuggestionModelWithReasoning<\"gpt-5.1\", typeof GPT51_REASONING_EFFORTS>\n\t| SuggestionModelWithReasoning<\"gpt-5\", typeof GPT5_REASONING_EFFORTS>\n\t| SuggestionModelWithReasoning<\"gpt-5-pro\", typeof GPT5_PRO_REASONING_EFFORTS>\n\t| SuggestionModelWithReasoning<\"gpt-5.2-pro\", typeof GPT5X_PRO_REASONING_EFFORTS>\n\t| SuggestionModelWithReasoning<\"gpt-5.4-pro\", typeof GPT5X_PRO_REASONING_EFFORTS>;\n\nconst REASONING_EFFORTS_BY_MODEL = {\n\t\"gpt-5.4\": GPT54_REASONING_EFFORTS,\n\t\"gpt-5.4-mini\": GPT54_REASONING_EFFORTS,\n\t\"gpt-5.4-nano\": GPT54_REASONING_EFFORTS,\n\t\"gpt-5.2\": GPT54_REASONING_EFFORTS,\n\t\"gpt-5.1\": GPT51_REASONING_EFFORTS,\n\t\"gpt-5\": GPT5_REASONING_EFFORTS,\n\t\"gpt-5-pro\": GPT5_PRO_REASONING_EFFORTS,\n\t\"gpt-5.2-pro\": GPT5X_PRO_REASONING_EFFORTS,\n\t\"gpt-5.4-pro\": GPT5X_PRO_REASONING_EFFORTS\n} as const;\n\nconst DEFAULT_SUGGESTION_MODEL: SuggestionModelConfig = {\n\tmodel: \"gpt-4.1-mini\"\n};\n\nexport type AngyConfigInput = {\n\tbasePoPath: string;\n\tworkingPoPath: string;\n\tsourceLocale: string;\n\ttargetLocale: string;\n\troutePath?: string;\n\tapiKey?: string;\n\tsystemMessage?: string;\n\tsuggestionModel?: SuggestionModelConfig;\n\twatchIgnore?: string[];\n\tsuggestionProvider?: SuggestionProvider;\n};\n\nexport function buildDefaultSystemMessage(sourceLocale: string, targetLocale: string) {\n\treturn `You are an expert product localization assistant translating ${sourceLocale} UI copy into polished ${targetLocale}. Keep already-${targetLocale} text unchanged. Preserve placeholders like {0}, {1}, ICU fragments, HTML-like markers such as <0/> and <strong>, line breaks, punctuation, capitalization, and button-like brevity. Prefer terminology and syntax that stay close to the translated examples so the product voice remains cohesive. Do not invent extra context, do not expand abbreviations unless the examples already do, and avoid semantic drift. Return only high-confidence translation suggestions for the provided untranslated strings.`;\n}\n\nfunction assertNonEmptyString(value: unknown, key: string) {\n\tif (typeof value !== \"string\" || !value.trim()) {\n\t\tthrow new Error(`[angy] ${key} is required and must be a non-empty string.`);\n\t}\n}\n\nfunction validateRoutePath(routePath: unknown) {\n\tif (routePath == null || routePath === \"\") return;\n\tif (typeof routePath !== \"string\" || !routePath.startsWith(\"/\")) {\n\t\tthrow new Error(`[angy] routePath must be an absolute path starting with \"/\".`);\n\t}\n}\n\nfunction validateWatchIgnore(watchIgnore: unknown) {\n\tif (watchIgnore == null) return;\n\tif (!Array.isArray(watchIgnore) || watchIgnore.some((item) => typeof item !== \"string\")) {\n\t\tthrow new Error(`[angy] watchIgnore must be an array of strings.`);\n\t}\n}\n\nfunction validateSuggestionProvider(suggestionProvider: unknown) {\n\tif (suggestionProvider == null) return;\n\tif (typeof suggestionProvider !== \"function\") {\n\t\tthrow new Error(`[angy] suggestionProvider must be a function.`);\n\t}\n}\n\nexport function isNonReasoningSuggestionModel(\n\tmodel: string\n): model is NonReasoningSuggestionModelName {\n\treturn NON_REASONING_SUGGESTION_MODELS.includes(model as NonReasoningSuggestionModelName);\n}\n\nfunction isSupportedSuggestionModel(model: string): model is SuggestionModelConfig[\"model\"] {\n\treturn (\n\t\tisNonReasoningSuggestionModel(model) ||\n\t\tObject.prototype.hasOwnProperty.call(REASONING_EFFORTS_BY_MODEL, model)\n\t);\n}\n\nexport function normalizeSuggestionModelConfig(\n\tinput: SuggestionModelConfig | undefined\n): SuggestionModelConfig {\n\tconst suggestionModel = input ?? DEFAULT_SUGGESTION_MODEL;\n\n\tif (typeof suggestionModel !== \"object\" || suggestionModel == null || Array.isArray(suggestionModel)) {\n\t\tthrow new Error(\n\t\t\t`[angy] suggestionModel must be an object like { model: \"gpt-4.1-mini\" }.`\n\t\t);\n\t}\n\n\tif (typeof suggestionModel.model !== \"string\" || !suggestionModel.model.trim()) {\n\t\tthrow new Error(`[angy] suggestionModel.model is required and must be a non-empty string.`);\n\t}\n\n\tif (!isSupportedSuggestionModel(suggestionModel.model)) {\n\t\tthrow new Error(`[angy] Unsupported suggestionModel.model \"${suggestionModel.model}\".`);\n\t}\n\n\tif (isNonReasoningSuggestionModel(suggestionModel.model)) {\n\t\tif (\n\t\t\t!(\"reasoning\" in suggestionModel) ||\n\t\t\tsuggestionModel.reasoning === undefined ||\n\t\t\tsuggestionModel.reasoning === null\n\t\t) {\n\t\t\treturn { model: suggestionModel.model, reasoning: null };\n\t\t}\n\n\t\tthrow new Error(\n\t\t\t`[angy] suggestionModel.reasoning is not supported for ${suggestionModel.model}.`\n\t\t);\n\t}\n\n\tconst allowedReasoning = REASONING_EFFORTS_BY_MODEL[suggestionModel.model];\n\tconst reasoning = suggestionModel.reasoning;\n\n\tif (reasoning == null) {\n\t\tif (suggestionModel.model === \"gpt-5-pro\") {\n\t\t\treturn { model: suggestionModel.model, reasoning: \"high\" };\n\t\t}\n\n\t\treturn { model: suggestionModel.model };\n\t}\n\n\tif (!allowedReasoning.includes(reasoning as (typeof allowedReasoning)[number])) {\n\t\tthrow new Error(\n\t\t\t`[angy] suggestionModel.reasoning \"${reasoning}\" is not supported for ${suggestionModel.model}.`\n\t\t);\n\t}\n\n\treturn {\n\t\tmodel: suggestionModel.model,\n\t\treasoning\n\t} as SuggestionModelConfig;\n}\n\nconst config: TranslationHelperConfig = {\n\tbasePoPath: resolve(__dirname, \"../../locales/en.po\"),\n\tworkingPoPath: resolve(__dirname, \"../../locales/en-working.po\"),\n\tsourceLocale: \"sv\",\n\ttargetLocale: \"en\",\n\troutePath: \"/api/translations\",\n\tapiKey: \"\",\n\tsystemMessage: buildDefaultSystemMessage(\"sv\", \"en\"),\n\tsuggestionModel: DEFAULT_SUGGESTION_MODEL,\n\twatchIgnore: [\"**/en-working.po\"]\n};\n\nlet loadedConfigRoot: string | null = null;\nconst workingCatalogWatchControllers = new Set<(path: string, delayMs?: number) => void>();\n\nexport function configureTranslationHelper(next: Partial<TranslationHelperConfig>) {\n\tObject.assign(config, next);\n}\n\nexport function getTranslationHelperConfig() {\n\treturn config;\n}\n\nfunction normalizeFsPath(path: string) {\n\treturn normalize(path).replace(/\\\\/g, \"/\");\n}\n\nexport function inferLocaleFromCatalogPath(path: string) {\n\tconst fileName = basename(path);\n\tif (!fileName) return null;\n\treturn fileName.slice(0, fileName.length - extname(fileName).length) || null;\n}\n\nfunction resolveLocaleAlias(\n\tvalue: string,\n\tpaths: { basePoPath: string; workingPoPath: string }\n) {\n\tif (value === \"working\") {\n\t\tconst locale = inferLocaleFromCatalogPath(paths.workingPoPath);\n\t\tif (!locale) {\n\t\t\tthrow new Error(\"[angy] Unable to infer locale from workingPoPath.\");\n\t\t}\n\t\treturn locale;\n\t}\n\n\tif (value === \"base\") {\n\t\tconst locale = inferLocaleFromCatalogPath(paths.basePoPath);\n\t\tif (!locale) {\n\t\t\tthrow new Error(\"[angy] Unable to infer locale from basePoPath.\");\n\t\t}\n\t\treturn locale;\n\t}\n\n\treturn value;\n}\n\nfunction validateLocaleAliasUsage(sourceLocale: string, targetLocale: string) {\n\tif (sourceLocale === \"working\") {\n\t\tthrow new Error('[angy] sourceLocale cannot be \"working\". Use an explicit source locale.');\n\t}\n\n\tif (targetLocale === \"base\") {\n\t\tthrow new Error('[angy] targetLocale cannot be \"base\". Use an explicit target locale or \"working\".');\n\t}\n}\n\nfunction validateCatalogPathSemantics(next: TranslationHelperConfig) {\n\tconst baseLocale = inferLocaleFromCatalogPath(next.basePoPath);\n\tconst workingLocale = inferLocaleFromCatalogPath(next.workingPoPath);\n\tconst expectedWorkingLocale = `${next.targetLocale}-working`;\n\n\tif (baseLocale !== next.targetLocale) {\n\t\tthrow new Error(\n\t\t\t`[angy] basePoPath must point to the ${next.targetLocale}.po catalog. Received \"${baseLocale ?? \"unknown\"}\".`\n\t\t);\n\t}\n\n\tif (workingLocale !== expectedWorkingLocale) {\n\t\tthrow new Error(\n\t\t\t`[angy] workingPoPath must point to the ${expectedWorkingLocale}.po catalog. Received \"${workingLocale ?? \"unknown\"}\".`\n\t\t);\n\t}\n}\n\nexport function registerWorkingCatalogWatchController(\n\tcontroller: (path: string, delayMs?: number) => void\n) {\n\tworkingCatalogWatchControllers.add(controller);\n\treturn () => {\n\t\tworkingCatalogWatchControllers.delete(controller);\n\t};\n}\n\nexport function suspendWorkingCatalogWatch(path: string, delayMs = 800) {\n\tconst normalizedPath = normalizeFsPath(path);\n\tfor (const controller of workingCatalogWatchControllers) {\n\t\tcontroller(normalizedPath, delayMs);\n\t}\n}\n\nexport function normalizeTranslationHelperConfig(\n\troot: string,\n\tnext: TranslationHelperUserConfig\n): TranslationHelperUserConfig {\n\tconst normalized = { ...next };\n\n\tif (normalized.basePoPath && !isAbsolute(normalized.basePoPath)) {\n\t\tnormalized.basePoPath = resolve(root, normalized.basePoPath);\n\t}\n\n\tif (normalized.workingPoPath && !isAbsolute(normalized.workingPoPath)) {\n\t\tnormalized.workingPoPath = resolve(root, normalized.workingPoPath);\n\t}\n\n\treturn normalized;\n}\n\nexport function resolveConfiguredLocaleAliases(\n\tnext: TranslationHelperConfig\n): TranslationHelperConfig {\n\tconst rawSourceLocale = next.sourceLocale;\n\tconst rawTargetLocale = next.targetLocale;\n\tconst resolvedSourceLocale = resolveLocaleAlias(rawSourceLocale, next);\n\tconst resolvedTargetLocale = resolveLocaleAlias(rawTargetLocale, next);\n\tconst usesDefaultSystemMessage =\n\t\tnext.systemMessage === buildDefaultSystemMessage(rawSourceLocale, rawTargetLocale);\n\n\tconst resolved = {\n\t\t...next,\n\t\tsourceLocale: resolvedSourceLocale,\n\t\ttargetLocale: resolvedTargetLocale,\n\t\tsystemMessage: usesDefaultSystemMessage\n\t\t\t? buildDefaultSystemMessage(resolvedSourceLocale, resolvedTargetLocale)\n\t\t\t: next.systemMessage\n\t};\n\n\tvalidateCatalogPathSemantics(resolved);\n\treturn resolved;\n}\n\nexport function completeAngyConfig(input: AngyConfigInput): TranslationHelperConfig {\n\tassertNonEmptyString(input.basePoPath, \"basePoPath\");\n\tassertNonEmptyString(input.workingPoPath, \"workingPoPath\");\n\tassertNonEmptyString(input.sourceLocale, \"sourceLocale\");\n\tassertNonEmptyString(input.targetLocale, \"targetLocale\");\n\tvalidateLocaleAliasUsage(input.sourceLocale, input.targetLocale);\n\tif (typeof input.apiKey !== \"string\" && typeof input.apiKey !== \"undefined\") {\n\t\tthrow new Error(`[angy] apiKey must be a string when provided. Use an empty string to disable suggestions.`);\n\t}\n\tvalidateRoutePath(input.routePath);\n\tvalidateWatchIgnore(input.watchIgnore);\n\tvalidateSuggestionProvider(input.suggestionProvider);\n\n\treturn {\n\t\tbasePoPath: input.basePoPath,\n\t\tworkingPoPath: input.workingPoPath,\n\t\tsourceLocale: input.sourceLocale,\n\t\ttargetLocale: input.targetLocale,\n\t\troutePath: input.routePath ?? \"/api/translations\",\n\t\tapiKey: input.apiKey ?? \"\",\n\t\tsystemMessage:\n\t\t\tinput.systemMessage ??\n\t\t\tbuildDefaultSystemMessage(input.sourceLocale, input.targetLocale),\n\t\tsuggestionModel: normalizeSuggestionModelConfig(input.suggestionModel),\n\t\twatchIgnore: input.watchIgnore ?? [\"**/en-working.po\"],\n\t\tsuggestionProvider: input.suggestionProvider\n\t};\n}\n\nexport function defineAngyConfig(config: AngyConfigInput) {\n\treturn resolveConfiguredLocaleAliases(completeAngyConfig(config));\n}\n\nasync function fileExists(path: string) {\n\ttry {\n\t\tawait readFile(path);\n\t\treturn true;\n\t} catch {\n\t\treturn false;\n\t}\n}\n\nasync function loadTsConfigModule(path: string) {\n\tconst source = await readFile(path, \"utf8\");\n\tconst transformed = await transform(source, {\n\t\tloader: \"ts\",\n\t\tformat: \"esm\",\n\t\ttarget: \"es2022\"\n\t});\n\tconst tempPath = `${path}.angy.tmp.mjs`;\n\tawait writeFile(tempPath, transformed.code, \"utf8\");\n\n\ttry {\n\t\treturn await import(/* @vite-ignore */ `${pathToFileURL(tempPath).href}?t=${Date.now()}`);\n\t} finally {\n\t\tawait unlink(tempPath).catch(() => undefined);\n\t}\n}\n\nasync function loadJsConfigModule(path: string) {\n\treturn import(/* @vite-ignore */ pathToFileURL(path).href);\n}\n\nexport async function loadAngyConfigFromRoot(root: string) {\n\tif (loadedConfigRoot === root) {\n\t\treturn config;\n\t}\n\n\tfor (const filename of CONFIG_FILENAMES) {\n\t\tconst fullPath = `${root}/${filename}`.replace(/\\\\/g, \"/\");\n\t\tif (!(await fileExists(fullPath))) continue;\n\n\t\tconst module =\n\t\t\tfilename.endsWith(\".ts\")\n\t\t\t\t? await loadTsConfigModule(fullPath)\n\t\t\t\t: await loadJsConfigModule(fullPath);\n\t\tconst raw = (module.default ?? module.config ?? {}) as TranslationHelperUserConfig;\n\t\tconst next = resolveConfiguredLocaleAliases(\n\t\t\tnormalizeTranslationHelperConfig(root, completeAngyConfig(raw as AngyConfigInput))\n\t\t);\n\t\tconfigureTranslationHelper(next);\n\t\tloadedConfigRoot = root;\n\t\treturn config;\n\t}\n\n\tloadedConfigRoot = root;\n\treturn config;\n}\n", "import { json } from \"@sveltejs/kit\";\nimport {\n\tbuildLookupVariants,\n\tcatalogEntryKey,\n\tcreateFuse,\n\tCatalogIntegrityError,\n\treadCatalogPair,\n\tresolveTranslationState\n} from \"./catalog.ts\";\nimport type { NormalizedEntry } from \"./types.ts\";\n\r\nconst DIRECT_MATCH_LIMIT = 5;\nconst MAX_ALTERNATIVES = 300;\nconst TRANSLATED_TARGET = Math.floor(MAX_ALTERNATIVES * 0.2);\nconst UNTRANSLATED_TARGET = MAX_ALTERNATIVES - TRANSLATED_TARGET;\nconst UNTRANSLATED_RANK_BONUS = 0.03;\n\r\ntype Candidate = {\r\n\tentry: NormalizedEntry;\r\n\tscore: number;\r\n\tvariant: string;\r\n};\r\n\r\ntype ScoredEntry = {\r\n\tentry: NormalizedEntry;\r\n\tscore: number;\r\n\tvariant: string;\r\n};\r\n\r\nexport function entryKey(msgid: string, msgctxt: string | null) {\r\n\treturn catalogEntryKey(msgid, msgctxt);\r\n}\r\n\r\nfunction normalizeCurrentPath(currentPath: string) {\r\n\ttry {\r\n\t\treturn new URL(currentPath, \"http://localhost\").pathname;\r\n\t} catch {\r\n\t\treturn currentPath;\r\n\t}\r\n}\r\n\r\nfunction inferPageReference(currentPath: string) {\r\n\tconst normalizedPath = normalizeCurrentPath(currentPath);\r\n\tif (normalizedPath === \"/\") {\r\n\t\treturn \"src/routes/+page.svelte\";\r\n\t}\r\n\r\n\tconst trimmedPath = normalizedPath.replace(/\\/+$/, \"\");\r\n\treturn `src/routes${trimmedPath}/+page.svelte`;\r\n}\r\n\r\nfunction routeLooksRelevant(routeReference: string, refs: string[], extractedComments: string[]) {\r\n\tif (!routeReference) return false;\r\n\r\n\tconst haystack = [...refs, ...extractedComments].join(\" \").toLowerCase();\r\n\treturn haystack.includes(routeReference.toLowerCase());\r\n}\r\n\r\nfunction dedupeCandidates(candidates: Candidate[]) {\r\n\tconst deduped = new Map<string, Candidate>();\r\n\r\n\tfor (const item of candidates) {\r\n\t\tconst id = entryKey(item.entry.msgid, item.entry.msgctxt);\r\n\t\tconst existing = deduped.get(id);\r\n\t\tif (!existing || item.score < existing.score) {\r\n\t\t\tdeduped.set(id, item);\r\n\t\t}\r\n\t}\r\n\r\n\treturn [...deduped.values()];\r\n}\r\n\r\nfunction rankDirectMatches(candidates: Candidate[], routeReference: string) {\n\treturn [...candidates].sort((left, right) => {\n\t\tconst leftRoute = routeLooksRelevant(\n\t\t\trouteReference,\n\t\t\tleft.entry.references,\r\n\t\t\tleft.entry.extractedComments\r\n\t\t)\r\n\t\t\t? -0.08\r\n\t\t\t: 0;\r\n\t\tconst rightRoute = routeLooksRelevant(\n\t\t\trouteReference,\n\t\t\tright.entry.references,\n\t\t\tright.entry.extractedComments\n\t\t)\n\t\t\t? -0.08\n\t\t\t: 0;\n\t\tconst leftUntranslated = !left.entry.hasTranslation ? -UNTRANSLATED_RANK_BONUS : 0;\n\t\tconst rightUntranslated = !right.entry.hasTranslation ? -UNTRANSLATED_RANK_BONUS : 0;\n\n\t\treturn left.score + leftRoute + leftUntranslated - (right.score + rightRoute + rightUntranslated);\n\t});\n}\n\r\nfunction getReferenceTokens(reference: string) {\r\n\treturn reference\r\n\t\t.toLowerCase()\r\n\t\t.split(/[\\/\\[\\].:_-]+/)\r\n\t\t.map((token) => token.trim())\r\n\t\t.filter(Boolean);\r\n}\r\n\r\nfunction getReferenceSimilarity(reference: string, routeReference: string) {\r\n\tconst referenceTokens = new Set(getReferenceTokens(reference));\r\n\tconst routeTokens = getReferenceTokens(routeReference);\r\n\r\n\tif (!routeTokens.length || !referenceTokens.size) return 0;\r\n\r\n\tlet matches = 0;\r\n\tfor (const token of routeTokens) {\r\n\t\tif (referenceTokens.has(token)) {\r\n\t\t\tmatches += 1;\r\n\t\t}\r\n\t}\r\n\r\n\treturn matches / routeTokens.length;\r\n}\r\n\r\nfunction collectSharedReferenceAlternatives(\n\tentries: NormalizedEntry[],\n\tbestEntry: NormalizedEntry,\n\trouteReference: string,\n\texcludedKeys: Set<string>\n) {\r\n\tconst sharedReferences = new Set(bestEntry.references.map((reference) => reference.toLowerCase()));\r\n\r\n\treturn entries\r\n\t\t.filter((entry) => !excludedKeys.has(entryKey(entry.msgid, entry.msgctxt)))\r\n\t\t.map((entry) => {\r\n\t\t\tconst referenceMatches = entry.references.filter((reference) =>\r\n\t\t\t\tsharedReferences.has(reference.toLowerCase())\r\n\t\t\t);\r\n\t\t\tconst routeMatch = entry.references.some((reference) =>\r\n\t\t\t\treference.toLowerCase().includes(routeReference.toLowerCase())\r\n\t\t\t);\r\n\r\n\t\t\tif (!referenceMatches.length && !routeMatch) {\r\n\t\t\t\treturn null;\r\n\t\t\t}\r\n\r\n\t\t\tconst referenceSimilarity = Math.max(\n\t\t\t\t0,\n\t\t\t\t...entry.references.map((reference) => getReferenceSimilarity(reference, routeReference))\n\t\t\t);\n\t\t\tconst untranslatedBonus = !entry.hasTranslation ? 0.35 : 0;\n\n\t\t\treturn {\n\t\t\t\tentry,\n\t\t\t\tscore:\n\t\t\t\t\treferenceMatches.length * 100 +\n\t\t\t\t\t(routeMatch ? 25 : 0) +\n\t\t\t\t\treferenceSimilarity +\n\t\t\t\t\tuntranslatedBonus,\n\t\t\t\tvariant: \"shared-reference\"\n\t\t\t};\n\t\t})\n\t\t.filter((item): item is ScoredEntry => Boolean(item))\r\n\t\t.sort((left, right) => right.score - left.score);\r\n}\r\n\r\nfunction collectRouteFillAlternatives(\n\tentries: NormalizedEntry[],\n\trouteReference: string,\n\texcludedKeys: Set<string>\n) {\n\treturn entries\r\n\t\t.filter((entry) => !excludedKeys.has(entryKey(entry.msgid, entry.msgctxt)))\r\n\t\t.map((entry) => {\r\n\t\t\tconst referenceSimilarity = Math.max(\r\n\t\t\t\t0,\r\n\t\t\t\t...entry.references.map((reference) => getReferenceSimilarity(reference, routeReference))\r\n\t\t\t);\r\n\r\n\t\t\tif (referenceSimilarity <= 0) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\tconst untranslatedBonus = !entry.hasTranslation ? 0.2 : 0;\n\n\t\t\treturn {\n\t\t\t\tentry,\n\t\t\t\tscore: referenceSimilarity + untranslatedBonus,\n\t\t\t\tvariant: \"route-reference\"\n\t\t\t};\n\t\t})\n\t\t.filter((item): item is ScoredEntry => Boolean(item))\r\n\t\t.sort((left, right) => right.score - left.score);\r\n}\r\n\r\nfunction collectTranslatedCohesionAlternatives(\r\n\tdirectMatches: Candidate[],\r\n\texcludedKeys: Set<string>\r\n) {\r\n\treturn directMatches\r\n\t\t.filter((item) => item.entry.hasTranslation)\r\n\t\t.filter((item) => !excludedKeys.has(entryKey(item.entry.msgid, item.entry.msgctxt)))\r\n\t\t.map((item) => ({\r\n\t\t\tentry: item.entry,\r\n\t\t\tscore: 1 - item.score,\r\n\t\t\tvariant: \"translated-cohesion\"\r\n\t\t}))\r\n\t\t.sort((left, right) => right.score - left.score);\r\n}\r\n\r\nfunction collectUntranslatedSimilarityAlternatives(\r\n\tdirectMatches: Candidate[],\r\n\texcludedKeys: Set<string>\r\n) {\r\n\treturn directMatches\r\n\t\t.filter((item) => !item.entry.hasTranslation)\r\n\t\t.filter((item) => !excludedKeys.has(entryKey(item.entry.msgid, item.entry.msgctxt)))\r\n\t\t.map((item) => ({\r\n\t\t\tentry: item.entry,\r\n\t\t\tscore: 1 - item.score,\r\n\t\t\tvariant: \"untranslated-similarity\"\r\n\t\t}))\r\n\t\t.sort((left, right) => right.score - left.score);\r\n}\r\n\r\nfunction buildAlternativePool(\r\n\ttopDirectAlternatives: Candidate[],\r\n\tsharedReferenceAlternatives: ScoredEntry[],\r\n\trouteFillAlternatives: ScoredEntry[],\r\n\ttranslatedCohesionAlternatives: ScoredEntry[],\r\n\tuntranslatedSimilarityAlternatives: ScoredEntry[]\r\n) {\r\n\tconst alternatives: ScoredEntry[] = [...topDirectAlternatives];\r\n\tconst selectedKeys = new Set<string>(\r\n\t\ttopDirectAlternatives.map((item) => entryKey(item.entry.msgid, item.entry.msgctxt))\r\n\t);\r\n\r\n\tlet translatedCount = topDirectAlternatives.filter((item) => item.entry.hasTranslation).length;\r\n\tlet untranslatedCount = topDirectAlternatives.length - translatedCount;\r\n\r\n\tconst tryAdd = (item: ScoredEntry, honorQuota = true) => {\r\n\t\tif (alternatives.length >= MAX_ALTERNATIVES) return false;\r\n\r\n\t\tconst id = entryKey(item.entry.msgid, item.entry.msgctxt);\r\n\t\tif (selectedKeys.has(id)) return false;\r\n\r\n\t\tif (honorQuota) {\r\n\t\t\tif (item.entry.hasTranslation && translatedCount >= TRANSLATED_TARGET) return false;\r\n\t\t\tif (!item.entry.hasTranslation && untranslatedCount >= UNTRANSLATED_TARGET) return false;\r\n\t\t}\r\n\r\n\t\tselectedKeys.add(id);\r\n\t\talternatives.push(item);\r\n\r\n\t\tif (item.entry.hasTranslation) {\r\n\t\t\ttranslatedCount += 1;\r\n\t\t} else {\r\n\t\t\tuntranslatedCount += 1;\r\n\t\t}\r\n\r\n\t\treturn true;\r\n\t};\r\n\r\n\tconst prioritizedSources = [\r\n\t\tsharedReferenceAlternatives,\r\n\t\trouteFillAlternatives,\r\n\t\tuntranslatedSimilarityAlternatives,\r\n\t\ttranslatedCohesionAlternatives\r\n\t];\r\n\r\n\tfor (const source of prioritizedSources) {\r\n\t\tfor (const item of source) {\r\n\t\t\ttryAdd(item, true);\r\n\t\t}\r\n\t}\r\n\r\n\tif (alternatives.length < MAX_ALTERNATIVES) {\r\n\t\tfor (const source of prioritizedSources) {\r\n\t\t\tfor (const item of source) {\r\n\t\t\t\ttryAdd(item, false);\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\r\n\treturn alternatives.slice(0, MAX_ALTERNATIVES);\r\n}\r\n\r\nexport async function findTranslationContext(\n\tkey: string,\n\tcurrentPath: string\n) {\n\tconst catalogPair = await readCatalogPair({ ensureWorking: true });\n\tconst effectiveEntries = catalogPair.baseEntries.map((entry) => {\n\t\tconst state = resolveTranslationState(entry, catalogPair.workingMap.get(entryKey(entry.msgid, entry.msgctxt)));\n\t\treturn {\n\t\t\t...state.effectiveEntry,\n\t\t\thasTranslation: state.hasTranslation,\n\t\t\tisFuzzy: state.isFuzzy,\n\t\t\ttranslationOrigin: state.translationOrigin\n\t\t};\n\t});\n\tconst effectiveIndex = {\n\t\tentries: effectiveEntries,\n\t\tfuse: createFuse(effectiveEntries)\n\t};\n\n\tconst routeReference = inferPageReference(currentPath);\n\tconst directMatches = rankDirectMatches(\n\t\tdedupeCandidates(\n\t\t\tbuildLookupVariants(key).flatMap((variant) =>\n\t\t\t\teffectiveIndex.fuse.search(variant, { limit: 180 }).map((hit) => ({\n\t\t\t\t\tentry: hit.item,\n\t\t\t\t\tscore: hit.score ?? 1,\n\t\t\t\t\tvariant\n\t\t\t\t}))\n\t\t\t)\n\t\t),\r\n\t\trouteReference\r\n\t);\r\n\r\n\tconst best = directMatches[0];\r\n\tif (!best || best.score > 0.42) {\n\t\treturn null;\n\t}\n\n\tconst effectiveDirectMatches = directMatches;\n\tconst bestEffective = effectiveDirectMatches[0];\n\tconst topDirectAlternatives = effectiveDirectMatches.slice(1, DIRECT_MATCH_LIMIT);\n\tconst excludedKeys = new Set<string>([\r\n\t\tentryKey(best.entry.msgid, best.entry.msgctxt),\r\n\t\t...topDirectAlternatives.map((item) => entryKey(item.entry.msgid, item.entry.msgctxt))\r\n\t]);\r\n\r\n\tconst sharedReferenceAlternatives = collectSharedReferenceAlternatives(\r\n\t\teffectiveEntries,\r\n\t\tbestEffective.entry,\r\n\t\trouteReference,\r\n\t\texcludedKeys\r\n\t);\r\n\r\n\tconst routeFillAlternatives = collectRouteFillAlternatives(\r\n\t\teffectiveEntries,\r\n\t\trouteReference,\r\n\t\texcludedKeys\r\n\t);\r\n\r\n\tconst translatedCohesionAlternatives = collectTranslatedCohesionAlternatives(\r\n\t\teffectiveDirectMatches.slice(DIRECT_MATCH_LIMIT),\r\n\t\texcludedKeys\r\n\t);\r\n\r\n\tconst untranslatedSimilarityAlternatives = collectUntranslatedSimilarityAlternatives(\r\n\t\teffectiveDirectMatches.slice(DIRECT_MATCH_LIMIT),\r\n\t\texcludedKeys\r\n\t);\r\n\r\n\treturn {\n\t\tbest,\n\t\talternatives: buildAlternativePool(\n\t\t\ttopDirectAlternatives,\r\n\t\t\tsharedReferenceAlternatives,\r\n\t\t\trouteFillAlternatives,\r\n\t\t\ttranslatedCohesionAlternatives,\r\n\t\t\tuntranslatedSimilarityAlternatives\n\t\t),\n\t\trouteReference,\n\t\tbaseMap: catalogPair.baseMap,\n\t\tworkingMap: catalogPair.workingMap,\n\t\tcatalogState: {\n\t\t\tstatus: \"ok\" as const,\n\t\t\taffectedCount: catalogPair.rotationImpact.length\n\t\t}\n\t};\n}\n\nexport function toPublicEntry(\n\tentry: NormalizedEntry,\n\tworkingEntry?: NormalizedEntry\n) {\n\tconst state = resolveTranslationState(entry, workingEntry);\n\n\treturn {\n\t\tmsgid: entry.msgid,\n\t\tmsgctxt: entry.msgctxt,\n\t\tmsgidPlural: entry.msgidPlural,\n\t\tmsgstr: state.effectiveEntry.msgstr,\n\t\treferences: entry.references,\n\t\textractedComments: entry.extractedComments,\n\t\tflags: state.effectiveEntry.flags,\n\t\tprevious: state.effectiveEntry.previous,\n\t\tobsolete: state.effectiveEntry.obsolete,\n\t\thasTranslation: state.hasTranslation,\n\t\tisFuzzy: state.isFuzzy,\n\t\tisCommittedToWorking: state.translationOrigin === \"working\",\n\t\tmatchesTargetTranslation: state.translationOrigin === \"base\" && state.hasTranslation,\n\t\ttranslationOrigin: state.translationOrigin,\n\t\ttranslationStatus: state.translationStatus\n\t};\n}\n\nexport function toPublicAlternative(\n\tentry: NormalizedEntry,\n\tscore: number,\n\tworkingEntry?: NormalizedEntry\n) {\n\tconst state = resolveTranslationState(entry, workingEntry);\n\n\treturn {\n\t\tmsgid: entry.msgid,\n\t\tmsgctxt: entry.msgctxt,\n\t\tscore,\n\t\treferences: entry.references,\n\t\tmsgstr: state.effectiveEntry.msgstr,\n\t\thasTranslation: state.hasTranslation,\n\t\tisFuzzy: state.isFuzzy,\n\t\tisCommittedToWorking: state.translationOrigin === \"working\",\n\t\tmatchesTargetTranslation: state.translationOrigin === \"base\" && state.hasTranslation,\n\t\ttranslationOrigin: state.translationOrigin,\n\t\ttranslationStatus: state.translationStatus\n\t};\n}\n\r\nexport async function handleContext(request: Request) {\r\n\tconst data = await request.formData();\r\n\tconst key = data.get(\"translationKey\")?.toString().trim();\r\n\tconst currentPath = data.get(\"currentPath\")?.toString().trim();\r\n\r\n\tif (!key || !currentPath) {\r\n\t\treturn json(\r\n\t\t\t{\r\n\t\t\t\tsuccess: false,\r\n\t\t\t\terror: \"translationKey and currentPath are required\"\r\n\t\t\t},\r\n\t\t\t{ status: 400 }\r\n\t\t);\r\n\t}\r\n\r\n\tlet baseFound;\n\ttry {\n\t\tbaseFound = await findTranslationContext(key, currentPath);\n\t} catch (error) {\n\t\tif (error instanceof CatalogIntegrityError) {\n\t\t\treturn json(\n\t\t\t\t{\n\t\t\t\t\tsuccess: false,\n\t\t\t\t\terror: error.message,\n\t\t\t\t\tcode: \"catalog_integrity_error\",\n\t\t\t\t\tissues: error.issues\n\t\t\t\t},\n\t\t\t\t{ status: 409 }\n\t\t\t);\n\t\t}\n\n\t\tthrow error;\n\t}\n\n\tif (!baseFound) {\n\t\treturn json(\n\t\t\t{\r\n\t\t\t\tsuccess: false,\r\n\t\t\t\terror: \"Translation key context not found\"\r\n\t\t\t},\r\n\t\t\t{ status: 404 }\r\n\t\t);\r\n\t}\r\n\r\n\tconst bestBase =\n\t\tbaseFound.baseMap.get(entryKey(baseFound.best.entry.msgid, baseFound.best.entry.msgctxt)) ??\n\t\tbaseFound.best.entry;\n\tconst bestWorking = baseFound.workingMap.get(entryKey(bestBase.msgid, bestBase.msgctxt));\n\r\n\treturn json({\n\t\tsuccess: true,\n\t\tmatch: {\n\t\t\tscore: baseFound.best.score,\n\t\t\tvia: baseFound.best.variant\n\t\t},\n\t\tcatalogState: baseFound.catalogState,\n\t\tentry: toPublicEntry(bestBase, bestWorking),\n\t\talternatives: baseFound.alternatives.map((item) => {\n\t\t\tconst baseAlt =\n\t\t\t\tbaseFound.baseMap.get(entryKey(item.entry.msgid, item.entry.msgctxt)) ?? item.entry;\n\t\t\tconst workingAlt = baseFound.workingMap.get(entryKey(baseAlt.msgid, baseAlt.msgctxt));\n\t\t\treturn toPublicAlternative(baseAlt, item.score, workingAlt);\n\t\t})\n\t});\n}\n", "import { json } from \"@sveltejs/kit\";\nimport type { TranslationContextResult } from \"../client/toggleQA.shared\";\nimport type {\n\tSuggestionModelConfig,\n\tSuggestionRequestItem,\n\tSuggestionResponseItem\n} from \"./config.ts\";\nimport { getTranslationHelperConfig } from \"./config.ts\";\n\r\nfunction getEntriesForSuggestions(contextResult: TranslationContextResult) {\r\n\treturn [contextResult.entry, ...contextResult.alternatives];\r\n}\r\n\r\nfunction getTranslatedExamples(contextResult: TranslationContextResult) {\r\n\treturn getEntriesForSuggestions(contextResult)\r\n\t\t.filter((entry) => entry.hasTranslation && entry.msgstr?.[0])\r\n\t\t.slice(0, 30)\r\n\t\t.map((entry) => ({\r\n\t\t\tmsgid: entry.msgid,\r\n\t\t\tmsgctxt: entry.msgctxt,\r\n\t\t\ttranslation: entry.msgstr[0]\r\n\t\t}));\r\n}\r\n\r\nfunction buildUserMessage(\r\n\tcontextResult: TranslationContextResult,\r\n\titems: SuggestionRequestItem[]\r\n) {\r\n\treturn JSON.stringify(\r\n\t\t{\r\n\t\t\ttask: \"Return translation suggestions for the untranslated UI strings.\",\r\n\t\t\toutput_format: {\r\n\t\t\t\titems: [\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tmsgid: \"source string\",\r\n\t\t\t\t\t\tmsgctxt: \"optional context or null\",\r\n\t\t\t\t\t\tsuggestion: \"translated suggestion\"\r\n\t\t\t\t\t}\r\n\t\t\t\t]\r\n\t\t\t},\r\n\t\t\ttranslated_examples: getTranslatedExamples(contextResult),\r\n\t\t\tuntranslated_items: items\r\n\t\t},\r\n\t\tnull,\r\n\t\t2\r\n\t);\r\n}\r\n\r\nfunction parseSuggestions(responseText: string): SuggestionResponseItem[] {\n\ttry {\r\n\t\tconst parsed = JSON.parse(responseText);\r\n\t\tconst items = Array.isArray(parsed?.items) ? parsed.items : [];\r\n\t\treturn items.filter(\r\n\t\t\t(item): item is SuggestionResponseItem =>\r\n\t\t\t\ttypeof item?.msgid === \"string\" &&\r\n\t\t\t\t(item.msgctxt === null || typeof item.msgctxt === \"string\") &&\r\n\t\t\t\ttypeof item?.suggestion === \"string\"\r\n\t\t);\r\n\t} catch {\r\n\t\treturn [];\r\n\t}\n}\n\nexport function buildSuggestionRequestBody({\n\tcontext,\n\titems,\n\tmodelConfig,\n\tsystemMessage\n}: {\n\tcontext: TranslationContextResult;\n\titems: SuggestionRequestItem[];\n\tmodelConfig: SuggestionModelConfig;\n\tsystemMessage: string;\n}) {\n\tconst body: Record<string, unknown> = {\n\t\tmodel: modelConfig.model,\n\t\tinput: [\n\t\t\t{\n\t\t\t\trole: \"system\",\n\t\t\t\tcontent: [{ type: \"input_text\", text: systemMessage }]\n\t\t\t},\n\t\t\t{\n\t\t\t\trole: \"user\",\n\t\t\t\tcontent: [{ type: \"input_text\", text: buildUserMessage(context, items) }]\n\t\t\t}\n\t\t],\n\t\ttext: {\n\t\t\tformat: {\n\t\t\t\ttype: \"json_schema\",\n\t\t\t\tname: \"translation_suggestions\",\n\t\t\t\tschema: {\n\t\t\t\t\ttype: \"object\",\n\t\t\t\t\tadditionalProperties: false,\n\t\t\t\t\tproperties: {\n\t\t\t\t\t\titems: {\n\t\t\t\t\t\t\ttype: \"array\",\n\t\t\t\t\t\t\titems: {\n\t\t\t\t\t\t\t\ttype: \"object\",\n\t\t\t\t\t\t\t\tadditionalProperties: false,\n\t\t\t\t\t\t\t\tproperties: {\n\t\t\t\t\t\t\t\t\tmsgid: { type: \"string\" },\n\t\t\t\t\t\t\t\t\tmsgctxt: { type: [\"string\", \"null\"] },\n\t\t\t\t\t\t\t\t\tsuggestion: { type: \"string\" }\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\trequired: [\"msgid\", \"msgctxt\", \"suggestion\"]\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t},\n\t\t\t\t\trequired: [\"items\"]\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t};\n\n\tif (modelConfig.reasoning && modelConfig.reasoning !== \"none\") {\n\t\tbody.reasoning = {\n\t\t\teffort: modelConfig.reasoning\n\t\t};\n\t}\n\n\treturn body;\n}\n\nexport async function handleSuggestions(request: Request) {\n\tconst {\n\t\tapiKey,\n\t\tsuggestionModel,\n\t\tsystemMessage,\n\t\tsourceLocale,\n\t\ttargetLocale,\n\t\tsuggestionProvider\n\t} = getTranslationHelperConfig();\n\n\tconst data = await request.json().catch(() => null);\n\tconst context = data?.context as TranslationContextResult | undefined;\n\tconst items = Array.isArray(data?.items) ? (data.items as SuggestionRequestItem[]) : [];\n\r\n\tif (!context || !items.length) {\r\n\t\treturn json(\r\n\t\t\t{\r\n\t\t\t\tsuccess: false,\r\n\t\t\t\terror: \"context and items are required\"\r\n\t\t\t},\r\n\t\t\t{ status: 400 }\n\t\t);\n\t}\n\n\tif (suggestionProvider) {\n\t\tconst providedItems = await suggestionProvider({\n\t\t\tcontext,\n\t\t\titems,\n\t\t\tsourceLocale,\n\t\t\ttargetLocale,\n\t\t\tsystemMessage,\n\t\t\tsuggestionModel,\n\t\t\tapiKey\n\t\t});\n\n\t\treturn json({\n\t\t\tsuccess: true,\n\t\t\titems: providedItems\n\t\t});\n\t}\n\n\tif (!apiKey.trim()) {\n\t\tconsole.warn(\"[angy] Suggestions disabled because apiKey is empty.\");\n\t\treturn json({\n\t\t\tsuccess: true,\n\t\t\tdisabled: true,\n\t\t\titems: []\n\t\t});\n\t}\n\n\ttry {\n\t\tconsole.info(\"[angy] Suggestion request starting.\", {\n\t\t\tmodel: suggestionModel.model,\n\t\t\treasoning: suggestionModel.reasoning ?? null,\n\t\t\tsourceLocale,\n\t\t\ttargetLocale,\n\t\t\titemCount: items.length,\n\t\t\thasApiKey: Boolean(apiKey.trim())\n\t\t});\n\n\t\tconst response = await fetch(\"https://api.openai.com/v1/responses\", {\n\t\t\tmethod: \"POST\",\n\t\t\theaders: {\n\t\t\t\tAuthorization: `Bearer ${apiKey}`,\n\t\t\t\t\"content-type\": \"application/json\"\n\t\t\t},\n\t\t\tbody: JSON.stringify(\n\t\t\t\tbuildSuggestionRequestBody({\n\t\t\t\t\tcontext,\n\t\t\t\t\titems,\n\t\t\t\t\tmodelConfig: suggestionModel,\n\t\t\t\t\tsystemMessage\n\t\t\t\t})\n\t\t\t)\n\t\t});\n\n\t\tif (!response.ok) {\n\t\t\tconst errorBody = await response.text().catch(() => \"\");\n\t\t\tconsole.error(\"[angy] Suggestion request failed.\", {\n\t\t\t\tstatus: response.status,\n\t\t\t\tstatusText: response.statusText,\n\t\t\t\tbody: errorBody\n\t\t\t});\n\n\t\t\treturn json(\n\t\t\t\t{\n\t\t\t\t\tsuccess: false,\n\t\t\t\t\terror: `Suggestion request failed: ${response.status}`\n\t\t\t\t},\n\t\t\t\t{ status: 502 }\n\t\t\t);\n\t\t}\n\n\t\tconst responseJson = await response.json();\n\tconst responseText =\n\t\tresponseJson?.output_text ??\n\t\tresponseJson?.output\n\t\t\t?.flatMap((item: any) => item?.content ?? [])\n\t\t\t.find((part: any) => part?.text)?.text ??\n\t\t\"\";\n\n\t\treturn json({\n\t\t\tsuccess: true,\n\t\t\titems: parseSuggestions(responseText)\n\t\t});\n\t} catch (error) {\n\t\tconsole.error(\"[angy] Suggestion request threw.\", error);\n\t\treturn json(\n\t\t\t{\n\t\t\t\tsuccess: false,\n\t\t\t\terror: \"Suggestion request failed before reaching the model\"\n\t\t\t},\n\t\t\t{ status: 502 }\n\t\t);\n\t}\n}\n"],
5
+ "mappings": ";AAAA,SAAS,QAAAA,aAAiC;;;ACA1C,SAAS,YAAY;AACrB,SAAS,YAAAC,iBAAgB;;;ACDzB,SAAS,aAAa,mBAAmB;AACzC,SAAS,QAAQ,UAAU,YAAAC,WAAU,QAAQ,IAAI,aAAAC,kBAAiB;AAClE,SAAS,WAAAC,UAAS,WAAAC,UAAS,MAAM,YAAAC,iBAAgB;AACjD,OAAO,mBAAmB;AAC1B,OAAO,UAAU;;;ACJjB,SAAS,UAAU,QAAQ,iBAAiB;AAC5C,SAAS,UAAU,SAAS,SAAS,YAAY,WAAW,eAAe;AAC3E,SAAS,qBAAqB;AAC9B,SAAS,qBAAqB;AAC9B,SAAS,iBAAiB;AAG1B,IAAM,YAAY,QAAQ,cAAc,YAAY,GAAG,CAAC;AACxD,IAAM,mBAAmB;AAAA,EACxB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACD;AA0CA,IAAM,kCAAkC;AAAA,EACvC;AAAA,EACA;AAAA,EACA;AACD;AACA,IAAM,0BAA0B,CAAC,QAAQ,OAAO,UAAU,QAAQ,OAAO;AACzE,IAAM,0BAA0B,CAAC,QAAQ,OAAO,UAAU,MAAM;AAChE,IAAM,yBAAyB,CAAC,WAAW,OAAO,UAAU,MAAM;AAClE,IAAM,6BAA6B,CAAC,MAAM;AAC1C,IAAM,8BAA8B,CAAC,UAAU,QAAQ,OAAO;AAiC9D,IAAM,6BAA6B;AAAA,EAClC,WAAW;AAAA,EACX,gBAAgB;AAAA,EAChB,gBAAgB;AAAA,EAChB,WAAW;AAAA,EACX,WAAW;AAAA,EACX,SAAS;AAAA,EACT,aAAa;AAAA,EACb,eAAe;AAAA,EACf,eAAe;AAChB;AAEA,IAAM,2BAAkD;AAAA,EACvD,OAAO;AACR;AAeO,SAAS,0BAA0B,cAAsB,cAAsB;AACrF,SAAO,gEAAgE,YAAY,0BAA0B,YAAY,kBAAkB,YAAY;AACxJ;AAEA,SAAS,qBAAqB,OAAgB,KAAa;AAC1D,MAAI,OAAO,UAAU,YAAY,CAAC,MAAM,KAAK,GAAG;AAC/C,UAAM,IAAI,MAAM,UAAU,GAAG,8CAA8C;AAAA,EAC5E;AACD;AAEA,SAAS,kBAAkB,WAAoB;AAC9C,MAAI,aAAa,QAAQ,cAAc,GAAI;AAC3C,MAAI,OAAO,cAAc,YAAY,CAAC,UAAU,WAAW,GAAG,GAAG;AAChE,UAAM,IAAI,MAAM,8DAA8D;AAAA,EAC/E;AACD;AAEA,SAAS,oBAAoB,aAAsB;AAClD,MAAI,eAAe,KAAM;AACzB,MAAI,CAAC,MAAM,QAAQ,WAAW,KAAK,YAAY,KAAK,CAAC,SAAS,OAAO,SAAS,QAAQ,GAAG;AACxF,UAAM,IAAI,MAAM,iDAAiD;AAAA,EAClE;AACD;AAEA,SAAS,2BAA2B,oBAA6B;AAChE,MAAI,sBAAsB,KAAM;AAChC,MAAI,OAAO,uBAAuB,YAAY;AAC7C,UAAM,IAAI,MAAM,+CAA+C;AAAA,EAChE;AACD;AAEO,SAAS,8BACf,OAC2C;AAC3C,SAAO,gCAAgC,SAAS,KAAwC;AACzF;AAEA,SAAS,2BAA2B,OAAwD;AAC3F,SACC,8BAA8B,KAAK,KACnC,OAAO,UAAU,eAAe,KAAK,4BAA4B,KAAK;AAExE;AAEO,SAAS,+BACf,OACwB;AACxB,QAAM,kBAAkB,SAAS;AAEjC,MAAI,OAAO,oBAAoB,YAAY,mBAAmB,QAAQ,MAAM,QAAQ,eAAe,GAAG;AACrG,UAAM,IAAI;AAAA,MACT;AAAA,IACD;AAAA,EACD;AAEA,MAAI,OAAO,gBAAgB,UAAU,YAAY,CAAC,gBAAgB,MAAM,KAAK,GAAG;AAC/E,UAAM,IAAI,MAAM,0EAA0E;AAAA,EAC3F;AAEA,MAAI,CAAC,2BAA2B,gBAAgB,KAAK,GAAG;AACvD,UAAM,IAAI,MAAM,6CAA6C,gBAAgB,KAAK,IAAI;AAAA,EACvF;AAEA,MAAI,8BAA8B,gBAAgB,KAAK,GAAG;AACzD,QACC,EAAE,eAAe,oBACjB,gBAAgB,cAAc,UAC9B,gBAAgB,cAAc,MAC7B;AACD,aAAO,EAAE,OAAO,gBAAgB,OAAO,WAAW,KAAK;AAAA,IACxD;AAEA,UAAM,IAAI;AAAA,MACT,yDAAyD,gBAAgB,KAAK;AAAA,IAC/E;AAAA,EACD;AAEA,QAAM,mBAAmB,2BAA2B,gBAAgB,KAAK;AACzE,QAAM,YAAY,gBAAgB;AAElC,MAAI,aAAa,MAAM;AACtB,QAAI,gBAAgB,UAAU,aAAa;AAC1C,aAAO,EAAE,OAAO,gBAAgB,OAAO,WAAW,OAAO;AAAA,IAC1D;AAEA,WAAO,EAAE,OAAO,gBAAgB,MAAM;AAAA,EACvC;AAEA,MAAI,CAAC,iBAAiB,SAAS,SAA8C,GAAG;AAC/E,UAAM,IAAI;AAAA,MACT,qCAAqC,SAAS,0BAA0B,gBAAgB,KAAK;AAAA,IAC9F;AAAA,EACD;AAEA,SAAO;AAAA,IACN,OAAO,gBAAgB;AAAA,IACvB;AAAA,EACD;AACD;AAEA,IAAM,SAAkC;AAAA,EACvC,YAAY,QAAQ,WAAW,qBAAqB;AAAA,EACpD,eAAe,QAAQ,WAAW,6BAA6B;AAAA,EAC/D,cAAc;AAAA,EACd,cAAc;AAAA,EACd,WAAW;AAAA,EACX,QAAQ;AAAA,EACR,eAAe,0BAA0B,MAAM,IAAI;AAAA,EACnD,iBAAiB;AAAA,EACjB,aAAa,CAAC,kBAAkB;AACjC;AAEA,IAAI,mBAAkC;AACtC,IAAM,iCAAiC,oBAAI,IAA8C;AAElF,SAAS,2BAA2B,MAAwC;AAClF,SAAO,OAAO,QAAQ,IAAI;AAC3B;AAEO,SAAS,6BAA6B;AAC5C,SAAO;AACR;AAEA,SAAS,gBAAgB,MAAc;AACtC,SAAO,UAAU,IAAI,EAAE,QAAQ,OAAO,GAAG;AAC1C;AAEO,SAAS,2BAA2B,MAAc;AACxD,QAAM,WAAW,SAAS,IAAI;AAC9B,MAAI,CAAC,SAAU,QAAO;AACtB,SAAO,SAAS,MAAM,GAAG,SAAS,SAAS,QAAQ,QAAQ,EAAE,MAAM,KAAK;AACzE;AAEA,SAAS,mBACR,OACA,OACC;AACD,MAAI,UAAU,WAAW;AACxB,UAAM,SAAS,2BAA2B,MAAM,aAAa;AAC7D,QAAI,CAAC,QAAQ;AACZ,YAAM,IAAI,MAAM,mDAAmD;AAAA,IACpE;AACA,WAAO;AAAA,EACR;AAEA,MAAI,UAAU,QAAQ;AACrB,UAAM,SAAS,2BAA2B,MAAM,UAAU;AAC1D,QAAI,CAAC,QAAQ;AACZ,YAAM,IAAI,MAAM,gDAAgD;AAAA,IACjE;AACA,WAAO;AAAA,EACR;AAEA,SAAO;AACR;AAEA,SAAS,yBAAyB,cAAsB,cAAsB;AAC7E,MAAI,iBAAiB,WAAW;AAC/B,UAAM,IAAI,MAAM,yEAAyE;AAAA,EAC1F;AAEA,MAAI,iBAAiB,QAAQ;AAC5B,UAAM,IAAI,MAAM,mFAAmF;AAAA,EACpG;AACD;AAEA,SAAS,6BAA6B,MAA+B;AACpE,QAAM,aAAa,2BAA2B,KAAK,UAAU;AAC7D,QAAM,gBAAgB,2BAA2B,KAAK,aAAa;AACnE,QAAM,wBAAwB,GAAG,KAAK,YAAY;AAElD,MAAI,eAAe,KAAK,cAAc;AACrC,UAAM,IAAI;AAAA,MACT,uCAAuC,KAAK,YAAY,0BAA0B,cAAc,SAAS;AAAA,IAC1G;AAAA,EACD;AAEA,MAAI,kBAAkB,uBAAuB;AAC5C,UAAM,IAAI;AAAA,MACT,0CAA0C,qBAAqB,0BAA0B,iBAAiB,SAAS;AAAA,IACpH;AAAA,EACD;AACD;AAWO,SAAS,2BAA2B,MAAc,UAAU,KAAK;AACvE,QAAM,iBAAiB,gBAAgB,IAAI;AAC3C,aAAW,cAAc,gCAAgC;AACxD,eAAW,gBAAgB,OAAO;AAAA,EACnC;AACD;AAEO,SAAS,iCACf,MACA,MAC8B;AAC9B,QAAM,aAAa,EAAE,GAAG,KAAK;AAE7B,MAAI,WAAW,cAAc,CAAC,WAAW,WAAW,UAAU,GAAG;AAChE,eAAW,aAAa,QAAQ,MAAM,WAAW,UAAU;AAAA,EAC5D;AAEA,MAAI,WAAW,iBAAiB,CAAC,WAAW,WAAW,aAAa,GAAG;AACtE,eAAW,gBAAgB,QAAQ,MAAM,WAAW,aAAa;AAAA,EAClE;AAEA,SAAO;AACR;AAEO,SAAS,+BACf,MAC0B;AAC1B,QAAM,kBAAkB,KAAK;AAC7B,QAAM,kBAAkB,KAAK;AAC7B,QAAM,uBAAuB,mBAAmB,iBAAiB,IAAI;AACrE,QAAM,uBAAuB,mBAAmB,iBAAiB,IAAI;AACrE,QAAM,2BACL,KAAK,kBAAkB,0BAA0B,iBAAiB,eAAe;AAElF,QAAM,WAAW;AAAA,IAChB,GAAG;AAAA,IACH,cAAc;AAAA,IACd,cAAc;AAAA,IACd,eAAe,2BACZ,0BAA0B,sBAAsB,oBAAoB,IACpE,KAAK;AAAA,EACT;AAEA,+BAA6B,QAAQ;AACrC,SAAO;AACR;AAEO,SAAS,mBAAmB,OAAiD;AACnF,uBAAqB,MAAM,YAAY,YAAY;AACnD,uBAAqB,MAAM,eAAe,eAAe;AACzD,uBAAqB,MAAM,cAAc,cAAc;AACvD,uBAAqB,MAAM,cAAc,cAAc;AACvD,2BAAyB,MAAM,cAAc,MAAM,YAAY;AAC/D,MAAI,OAAO,MAAM,WAAW,YAAY,OAAO,MAAM,WAAW,aAAa;AAC5E,UAAM,IAAI,MAAM,2FAA2F;AAAA,EAC5G;AACA,oBAAkB,MAAM,SAAS;AACjC,sBAAoB,MAAM,WAAW;AACrC,6BAA2B,MAAM,kBAAkB;AAEnD,SAAO;AAAA,IACN,YAAY,MAAM;AAAA,IAClB,eAAe,MAAM;AAAA,IACrB,cAAc,MAAM;AAAA,IACpB,cAAc,MAAM;AAAA,IACpB,WAAW,MAAM,aAAa;AAAA,IAC9B,QAAQ,MAAM,UAAU;AAAA,IACxB,eACC,MAAM,iBACN,0BAA0B,MAAM,cAAc,MAAM,YAAY;AAAA,IACjE,iBAAiB,+BAA+B,MAAM,eAAe;AAAA,IACrE,aAAa,MAAM,eAAe,CAAC,kBAAkB;AAAA,IACrD,oBAAoB,MAAM;AAAA,EAC3B;AACD;AAEO,SAAS,iBAAiBC,SAAyB;AACzD,SAAO,+BAA+B,mBAAmBA,OAAM,CAAC;AACjE;AAEA,eAAe,WAAW,MAAc;AACvC,MAAI;AACH,UAAM,SAAS,IAAI;AACnB,WAAO;AAAA,EACR,QAAQ;AACP,WAAO;AAAA,EACR;AACD;AAEA,eAAe,mBAAmB,MAAc;AAC/C,QAAM,SAAS,MAAM,SAAS,MAAM,MAAM;AAC1C,QAAM,cAAc,MAAM,UAAU,QAAQ;AAAA,IAC3C,QAAQ;AAAA,IACR,QAAQ;AAAA,IACR,QAAQ;AAAA,EACT,CAAC;AACD,QAAM,WAAW,GAAG,IAAI;AACxB,QAAM,UAAU,UAAU,YAAY,MAAM,MAAM;AAElD,MAAI;AACH,WAAO,MAAM;AAAA;AAAA,MAA0B,GAAG,cAAc,QAAQ,EAAE,IAAI,MAAM,KAAK,IAAI,CAAC;AAAA;AAAA,EACvF,UAAE;AACD,UAAM,OAAO,QAAQ,EAAE,MAAM,MAAM,MAAS;AAAA,EAC7C;AACD;AAEA,eAAe,mBAAmB,MAAc;AAC/C,SAAO;AAAA;AAAA,IAA0B,cAAc,IAAI,EAAE;AAAA;AACtD;AAEA,eAAsB,uBAAuB,MAAc;AAC1D,MAAI,qBAAqB,MAAM;AAC9B,WAAO;AAAA,EACR;AAEA,aAAW,YAAY,kBAAkB;AACxC,UAAM,WAAW,GAAG,IAAI,IAAI,QAAQ,GAAG,QAAQ,OAAO,GAAG;AACzD,QAAI,CAAE,MAAM,WAAW,QAAQ,EAAI;AAEnC,UAAM,SACL,SAAS,SAAS,KAAK,IACpB,MAAM,mBAAmB,QAAQ,IACjC,MAAM,mBAAmB,QAAQ;AACrC,UAAM,MAAO,OAAO,WAAW,OAAO,UAAU,CAAC;AACjD,UAAM,OAAO;AAAA,MACZ,iCAAiC,MAAM,mBAAmB,GAAsB,CAAC;AAAA,IAClF;AACA,+BAA2B,IAAI;AAC/B,uBAAmB;AACnB,WAAO;AAAA,EACR;AAEA,qBAAmB;AACnB,SAAO;AACR;;;ADtbO,IAAM,sBAAsB,oBAAI,IAAoB;AAEpD,IAAM,wBAAN,cAAoC,MAAM;AAAA,EAChD;AAAA,EAEA,YAAY,SAAiB,QAAiC;AAC7D,UAAM,OAAO;AACb,SAAK,OAAO;AACZ,SAAK,SAAS;AAAA,EACf;AACD;AAUO,SAAS,gBAAgB,OAAe,SAAwB;AACtE,SAAO,GAAG,WAAW,EAAE,KAAK,KAAK;AAClC;AAEO,SAAS,oBAAoB,OAAuB;AAC1D,SAAO,MAAM,QAAQ,QAAQ,GAAG,EAAE,KAAK;AACxC;AAEO,SAAS,mBAAmB,OAAuB;AACzD,SAAO,oBAAoB,KAAK,EAC9B,QAAQ,qBAAqB,KAAK,EAClC,QAAQ,aAAa,KAAK,EAC1B,QAAQ,0BAA0B,KAAK,EACvC,QAAQ,YAAY,KAAK,EACzB,QAAQ,aAAa,GAAG,EACxB,YAAY;AACf;AAEO,SAAS,kBAAkB,OAAyB;AAC1D,SAAO,mBAAmB,KAAK,EAC7B,MAAM,oBAAoB,EAC1B,IAAI,CAAC,UAAU,MAAM,KAAK,CAAC,EAC3B,OAAO,OAAO;AACjB;AAEO,SAAS,oBAAoB,KAAuB;AAC1D,QAAM,aAAa,mBAAmB,GAAG;AACzC,QAAM,WAAW,oBAAI,IAAY,CAAC,UAAU,CAAC;AAE7C,WAAS,IAAI,WAAW,QAAQ,QAAQ,KAAK,CAAC;AAC9C,WAAS,IAAI,WAAW,QAAQ,QAAQ,EAAE,EAAE,QAAQ,QAAQ,GAAG,EAAE,KAAK,CAAC;AACvE,WAAS,IAAI,WAAW,QAAQ,UAAU,EAAE,EAAE,QAAQ,QAAQ,GAAG,EAAE,KAAK,CAAC;AAEzE,SAAO,CAAC,GAAG,QAAQ,EAAE,OAAO,OAAO;AACpC;AAEO,SAAS,iBAAiB,QAAa,mBAAyD;AACtG,QAAM,UAA6B,CAAC;AACpC,QAAM,eAAe,QAAQ,gBAAgB,CAAC;AAE9C,aAAW,CAAC,QAAQ,KAAK,KAAK,OAAO,QAAQ,YAAY,GAAG;AAC3D,eAAW,CAAC,UAAU,GAAG,KAAK,OAAO,QAAQ,KAAgC,GAAG;AAC/E,UAAI,CAAC,SAAU;AAEf,YAAM,QAAQ;AACd,YAAM,QAAQ,MAAM,SAAS;AAC7B,YAAM,UAAU,MAAM,YAAY,WAAW,KAAK,OAAO;AACzD,YAAM,cAAc,MAAM,gBAAgB;AAC1C,YAAM,SAAS,MAAM,QAAQ,MAAM,MAAM,IAAI,MAAM,OAAO,OAAO,OAAO,IAAI,CAAC;AAC7E,YAAM,aAAa,MAAM,UAAU,YAChC,MAAM,SAAS,UAAU,MAAM,IAAI,EAAE,IAAI,CAAC,SAAS,KAAK,KAAK,CAAC,EAAE,OAAO,OAAO,IAC9E,CAAC;AACJ,YAAM,oBAAoB,MAAM,UAAU,YACvC,MAAM,SAAS,UAAU,MAAM,IAAI,EAAE,IAAI,CAAC,SAAS,KAAK,KAAK,CAAC,EAAE,OAAO,OAAO,IAC9E,CAAC;AACJ,YAAM,QAAQ,MAAM,UAAU,OAC3B,MAAM,SAAS,KAAK,MAAM,GAAG,EAAE,IAAI,CAAC,SAAS,KAAK,KAAK,CAAC,EAAE,OAAO,OAAO,IACxE,CAAC;AACJ,YAAM,WAAW,MAAM,UAAU,WAC9B,MAAM,SAAS,SAAS,MAAM,IAAI,EAAE,IAAI,CAAC,SAAS,KAAK,KAAK,CAAC,EAAE,OAAO,OAAO,IAC7E,CAAC;AACJ,YAAM,aAAa,MAAM,QAAQ,KAAK,CAAC,SAAS,KAAK,KAAK,EAAE,SAAS,CAAC,KAAK;AAC3E,YAAM,QAAQ,MAAM,UAAU,MAAM,SAAS,OAAO;AAEpD,cAAQ,KAAK;AAAA,QACZ;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA,UAAU,QAAQ,MAAM,QAAQ;AAAA,QAChC,YAAY,mBAAmB,KAAK;AAAA,QACpC,cAAc,kBAAkB,KAAK;AAAA,QACrC,SAAS;AAAA,QACT,gBAAgB;AAAA,QAChB;AAAA,MACD,CAAC;AAAA,IACF;AAAA,EACD;AAEA,SAAO;AACR;AAEO,SAAS,WAAW,SAA4B;AACtD,SAAO,IAAI,KAAK,SAAS;AAAA,IACxB,cAAc;AAAA,IACd,gBAAgB;AAAA,IAChB,WAAW;AAAA,IACX,oBAAoB;AAAA,IACpB,MAAM;AAAA,MACL,EAAE,MAAM,SAAS,QAAQ,IAAI;AAAA,MAC7B,EAAE,MAAM,cAAc,QAAQ,IAAI;AAAA,MAClC,EAAE,MAAM,cAAc,QAAQ,IAAI;AAAA,IACnC;AAAA,EACD,CAAC;AACF;AAEO,SAAS,cAAc,SAA4B;AACzD,SAAO,IAAI,IAAI,QAAQ,IAAI,CAAC,UAAU,CAAC,gBAAgB,MAAM,OAAO,MAAM,OAAO,GAAG,KAAK,CAAC,CAAC;AAC5F;AAEO,SAAS,cAAc,OAAgD;AAC7E,MAAI,CAAC,OAAO,QAAQ,OAAQ,QAAO;AACnC,SAAO,MAAM,OAAO,KAAK,CAAC,SAAS,KAAK,KAAK,EAAE,SAAS,CAAC,GAAG,KAAK,KAAK;AACvE;AAsBA,eAAsBC,YAAW,MAAc;AAC9C,MAAI;AACH,UAAM,OAAO,MAAM,YAAY,IAAI;AACnC,WAAO;AAAA,EACR,QAAQ;AACP,WAAO;AAAA,EACR;AACD;AAwBA,eAAsB,kBAAkB,SAA4B;AACnE,QAAM,EAAE,WAAW,IAAI,2BAA2B;AAClD,QAAM,OAAO,YAAY,SAAS,aAAa,MAAM,0BAA0B;AAE/E,MAAI,YAAY,WAAW;AAC1B,UAAM,SAAS,MAAMC,YAAW,IAAI;AACpC,QAAI,CAAC,OAAQ,QAAO;AAAA,EACrB;AAEA,QAAM,MAAM,MAAMC,UAAS,IAAI;AAC/B,SAAO,cAAc,GAAG,MAAM,GAAG;AAClC;AAEA,eAAsB,uBAAuB;AAC5C,QAAM,EAAE,YAAY,cAAc,IAAI,2BAA2B;AACjE,QAAM,SAAS,MAAMD,YAAW,aAAa;AAC7C,MAAI,CAAC,QAAQ;AACZ,UAAM,SAAS,YAAY,aAAa;AAAA,EACzC;AACD;AAEO,SAAS,wBAAwB;AACvC,QAAM,EAAE,cAAc,IAAI,2BAA2B;AACrD,QAAM,YAAYE,SAAQ,aAAa;AACvC,QAAM,OAAO,cAAc,MAAM,GAAG,cAAc,SAAS,UAAU,MAAM;AAC3E,SAAO,GAAG,IAAI,cAAc,SAAS;AACtC;AAEA,eAAe,4BAA4B;AAC1C,QAAM,cAAc,sBAAsB;AAC1C,MAAI,MAAMF,YAAW,WAAW,GAAG;AAClC,WAAO;AAAA,EACR;AAEA,SAAO,2BAA2B,EAAE;AACrC;AAEA,eAAsB,4BAA4B;AACjD,QAAM,qBAAqB;AAE3B,QAAM,EAAE,cAAc,IAAI,2BAA2B;AACrD,QAAM,cAAc,sBAAsB;AAC1C,QAAM,SAAS,MAAMA,YAAW,WAAW;AAC3C,MAAI,CAAC,QAAQ;AACZ,UAAM,SAAS,eAAe,WAAW;AAAA,EAC1C;AACD;AAEO,SAAS,gBAAgB,YAAqB;AACpD,MAAI,CAAC,WAAY,QAAO;AAExB,SAAO,WACL,MAAM,GAAG,EACT,IAAI,CAAC,SAAS,KAAK,KAAK,CAAC,EACzB,OAAO,OAAO,EACd,OAAO,CAAC,SAAS,SAAS,OAAO,EACjC,KAAK,IAAI;AACZ;AAEA,eAAsB,oBAAoB,QAAa;AACtD,QAAM,0BAA0B;AAChC,QAAM,cAAc,sBAAsB;AAC1C,QAAM,WAAW,cAAc,GAAG,QAAQ,MAAM;AAChD,QAAMG,WAAU,aAAa,QAAQ;AACtC;AAEA,eAAsB,+BAA+B;AACpD,QAAM,0BAA0B;AAEhC,QAAM,EAAE,cAAc,IAAI,2BAA2B;AACrD,QAAM,cAAc,sBAAsB;AAC1C,QAAM,cAAc,GAAG,aAAa;AACpC,QAAM,WAAW,MAAMF,UAAS,WAAW;AAE3C,6BAA2B,aAAa;AACxC,QAAME,WAAU,aAAa,QAAQ;AACrC,QAAM,GAAG,eAAe,EAAE,OAAO,KAAK,CAAC;AACvC,QAAM,OAAO,aAAa,aAAa;AACxC;AAEO,SAAS,8BACf,aACA,gBACC;AACD,QAAM,SAAkC,CAAC;AACzC,QAAM,UAAU,cAAc,WAAW;AACzC,QAAM,aAAa,cAAc,cAAc;AAE/C,aAAW,aAAa,aAAa;AACpC,UAAM,MAAM,gBAAgB,UAAU,OAAO,UAAU,OAAO;AAC9D,UAAM,eAAe,WAAW,IAAI,GAAG;AACvC,QAAI,CAAC,cAAc;AAClB,aAAO,KAAK;AAAA,QACX,MAAM;AAAA,QACN,OAAO,UAAU;AAAA,QACjB,SAAS,UAAU;AAAA,QACnB,QAAQ;AAAA,MACT,CAAC;AACD;AAAA,IACD;AAEA,UAAM,YAAY,cAAc,SAAS;AACzC,UAAM,eAAe,cAAc,YAAY;AAC/C,QAAI,aAAa,CAAC,cAAc;AAC/B,aAAO,KAAK;AAAA,QACX,MAAM;AAAA,QACN,OAAO,UAAU;AAAA,QACjB,SAAS,UAAU;AAAA,QACnB;AAAA,MACD,CAAC;AAAA,IACF;AAAA,EACD;AAEA,aAAW,gBAAgB,gBAAgB;AAC1C,UAAM,MAAM,gBAAgB,aAAa,OAAO,aAAa,OAAO;AACpE,QAAI,CAAC,QAAQ,IAAI,GAAG,GAAG;AACtB,aAAO,KAAK;AAAA,QACX,MAAM;AAAA,QACN,OAAO,aAAa;AAAA,QACpB,SAAS,aAAa;AAAA,QACtB,QAAQ;AAAA,MACT,CAAC;AAAA,IACF;AAAA,EACD;AAEA,SAAO;AACR;AAEO,SAAS,uBAAuB,aAAgC,gBAAmC;AACzG,QAAM,SAAS,8BAA8B,aAAa,cAAc;AACxE,MAAI,OAAO,QAAQ;AAClB,UAAM,IAAI;AAAA,MACT;AAAA,MACA;AAAA,IACD;AAAA,EACD;AACD;AAEO,SAAS,sBACf,aACA,YACC;AACD,QAAM,WAAiC,CAAC;AAExC,aAAW,aAAa,aAAa;AACpC,UAAM,eAAe,WAAW,IAAI,gBAAgB,UAAU,OAAO,UAAU,OAAO,CAAC;AACvF,QAAI,CAAC,aAAc;AAEnB,UAAM,YAAY,cAAc,SAAS;AACzC,UAAM,eAAe,cAAc,YAAY;AAC/C,QAAI,CAAC,aAAc;AACnB,QAAI,cAAc,aAAc;AAEhC,aAAS,KAAK;AAAA,MACb,OAAO,UAAU;AAAA,MACjB,SAAS,UAAU;AAAA,MACnB;AAAA,MACA;AAAA,IACD,CAAC;AAAA,EACF;AAEA,SAAO;AACR;AAEO,SAAS,wBACf,WACA,cACC;AACD,QAAM,YAAY,cAAc,SAAS;AACzC,QAAM,eAAe,cAAc,YAAY;AAC/C,QAAM,cAAc,gBAAgB,eAAe,eAAe;AAClE,QAAM,QAAQ,QAAQ,YAAY,OAAO;AACzC,MAAI,oBAAuC;AAC3C,MAAI,oBAAuC;AAC3C,MAAI,iBAAiB;AAErB,MAAI,gBAAgB,cAAc;AACjC,qBAAiB;AACjB,QAAI,CAAC,aAAa,iBAAiB,WAAW;AAC7C,0BAAoB;AACpB,0BAAoB;AAAA,IACrB,OAAO;AACN,0BAAoB;AACpB,0BAAoB;AAAA,IACrB;AAAA,EACD,WAAW,WAAW;AACrB,qBAAiB;AACjB,wBAAoB;AACpB,wBAAoB;AAAA,EACrB;AAEA,MAAI,OAAO;AACV,wBAAoB;AAAA,EACrB;AAEA,SAAO;AAAA,IACN;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,gBAAgB,QAAQ,cAAc,cAAc,CAAC;AAAA,IACrD,SAAS;AAAA,EACV;AACD;AAEA,eAAsB,gBAAgB,SAA6D;AAClG,MAAI,SAAS,eAAe;AAC3B,UAAM,qBAAqB;AAAA,EAC5B;AAEA,QAAM,CAAC,YAAY,aAAa,IAAI,MAAM,QAAQ,IAAI;AAAA,IACrD,kBAAkB,MAAM;AAAA,IACxB,kBAAkB,SAAS;AAAA,EAC5B,CAAC;AAED,MAAI,CAAC,cAAc,CAAC,eAAe;AAClC,UAAM,IAAI,MAAM,2CAA2C;AAAA,EAC5D;AAEA,QAAM,cAAc,iBAAiB,YAAY,MAAM;AACvD,QAAM,iBAAiB,iBAAiB,eAAe,SAAS;AAChE,yBAAuB,aAAa,cAAc;AAElD,QAAM,UAAU,cAAc,WAAW;AACzC,QAAM,aAAa,cAAc,cAAc;AAC/C,QAAM,iBAAiB,sBAAsB,aAAa,UAAU;AAEpE,SAAO;AAAA,IACN;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACD;AACD;AAEA,eAAsB,uBAAuB;AAC5C,QAAM,EAAE,eAAe,IAAI,MAAM,gBAAgB,EAAE,eAAe,KAAK,CAAC;AAExE,SAAO;AAAA,IACN,MAAM;AAAA,IACN,QAAQ;AAAA,IACR,UAAU;AAAA,EACX;AACD;AAEA,SAAS,qBAAqB;AAC7B,UAAO,oBAAI,KAAK,GAAE,YAAY,EAAE,QAAQ,SAAS,GAAG;AACrD;AAEA,SAAS,gBAAgB,MAAc,OAAe;AACrD,QAAM,YAAYC,SAAQ,IAAI;AAC9B,QAAM,YAAYF,SAAQ,IAAI;AAC9B,QAAM,OAAOG,UAAS,MAAM,SAAS;AACrC,SAAO,KAAK,WAAW,GAAG,IAAI,IAAI,KAAK,IAAI,mBAAmB,CAAC,GAAG,SAAS,EAAE;AAC9E;AAEA,eAAsB,eAAe,SAAwC;AAC5E,QAAM,EAAE,YAAY,cAAc,IAAI,2BAA2B;AACjE,QAAM,cAAc,sBAAsB;AAC1C,QAAM,yBAAyB,MAAM,0BAA0B;AAC/D,QAAM,gBAAgB,MAAML,YAAW,sBAAsB;AAE7D,MAAI,CAAC,eAAe;AACnB,WAAO,EAAE,IAAI,OAAgB,OAAO,iCAAiC;AAAA,EACtE;AAEA,QAAM,iBAAiB,gBAAgB,YAAY,aAAa;AAChE,QAAM,oBAAoB,gBAAgB,eAAe,gBAAgB;AAEzE,QAAM,SAAS,YAAY,cAAc;AACzC,QAAM,SAAS,wBAAwB,iBAAiB;AACxD,QAAM,SAAS,wBAAwB,UAAU;AACjD,QAAM,SAAS,YAAY,aAAa;AACxC,QAAM,SAAS,eAAe,WAAW;AAEzC,SAAO;AAAA,IACN,IAAI;AAAA,IACJ;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACD;AACD;;;AD9cA,SAAS,WAAW,OAAe,SAAwB;AAC1D,SAAO,GAAG,WAAW,EAAE,KAAK,KAAK;AAClC;AAEA,eAAsB,iCACrB,eACA,iBACA,kBACC;AACD,QAAM,qBAAqB;AAE3B,QAAM,gBAAgB,EAAE,eAAe,KAAK,CAAC;AAE7C,QAAM,CAAC,YAAY,aAAa,IAAI,MAAM,QAAQ,IAAI,CAAC,kBAAkB,MAAM,GAAG,kBAAkB,SAAS,CAAC,CAAC;AAE/G,MAAI,CAAC,cAAc,CAAC,eAAe;AAClC,WAAO,EAAE,IAAI,OAAgB,OAAO,0BAA0B;AAAA,EAC/D;AAEA,QAAM,mBAAmB,WAAW,gBAAgB,CAAC;AACrD,QAAM,sBAAsB,cAAc,gBAAgB,CAAC;AAE3D,QAAM,SAAS,mBAAmB;AAClC,QAAM,YAAY,iBAAiB,MAAM;AACzC,MAAI,CAAC,WAAW;AACf,WAAO,EAAE,IAAI,OAAgB,OAAO,0CAA0C;AAAA,EAC/E;AAEA,QAAM,YAAY,UAAU,aAAa;AACzC,MAAI,CAAC,WAAW;AACf,WAAO,EAAE,IAAI,OAAgB,OAAO,yCAAyC;AAAA,EAC9E;AAEA,QAAM,eAAgB,oBAAoB,MAAM,MAAM,CAAC;AACvD,QAAM,QAAU,aAAa,aAAa,MACzC,qBAAqB,WAAW,eAAe,eAAe;AAE/D,QAAM,SAAS,CAAC,gBAAgB;AAChC,QAAM,aAAa,CAAC;AACpB,QAAM,SAAS,OAAO,gBAAgB,MAAM,SAAS,IAAI;AAEzD,QAAM,oBAAoB,aAAa;AAEvC,sBAAoB,IAAI,WAAW,eAAe,eAAe,GAAG,gBAAgB;AAEpF,QAAM,qBAAqB,2BAA2B,2BAA2B,EAAE,aAAa,KAAK;AAErG,SAAO;AAAA,IACN,IAAI;AAAA,IACJ,OAAO;AAAA,IACP,SAAS;AAAA,IACT,gBAAgB;AAAA,EACjB;AACD;AAEA,SAAS,qBACR,WACA,OACA,SACqB;AACrB,SAAO;AAAA,IACN;AAAA,IACA,SAAS,WAAW;AAAA,IACpB,cAAc,UAAU;AAAA,IACxB,QAAQ,MAAM,QAAQ,UAAU,MAAM,IAAI,CAAC,GAAG,UAAU,MAAM,IAAI,CAAC,EAAE;AAAA,IACrE,UAAU,QAAQ,UAAU,QAAQ;AAAA,IACpC,UAAU,UAAU,WACjB;AAAA,MACA,WAAW,UAAU,SAAS;AAAA,MAC9B,WAAW,UAAU,SAAS;AAAA,MAC9B,MAAM,UAAU,SAAS;AAAA,MACzB,UAAU,UAAU,SAAS;AAAA,IAC9B,IACC;AAAA,EACJ;AACD;AAEA,eAAsB,kBAAkB,SAAkB;AACzD,QAAM,OAAO,MAAM,QAAQ,KAAK,EAAE,MAAM,MAAM,IAAI;AAClD,QAAM,QAAQ,MAAM,QAAQ,MAAM,KAAK,IAAK,KAAK,QAA8B,CAAC;AAEhF,MAAI,CAAC,MAAM,QAAQ;AAClB,WAAO;AAAA,MACN;AAAA,QACC,SAAS;AAAA,QACT,OAAO;AAAA,MACR;AAAA,MACA,EAAE,QAAQ,IAAI;AAAA,IACf;AAAA,EACD;AAEA,MAAI;AACH,UAAM,gBAAgB,EAAE,eAAe,KAAK,CAAC;AAAA,EAC9C,SAAS,OAAO;AACf,QAAI,iBAAiB,uBAAuB;AAC3C,aAAO;AAAA,QACN;AAAA,UACC,SAAS;AAAA,UACT,OAAO,MAAM;AAAA,UACb,MAAM;AAAA,UACN,QAAQ,MAAM;AAAA,QACf;AAAA,QACA,EAAE,QAAQ,IAAI;AAAA,MACf;AAAA,IACD;AAEA,UAAM;AAAA,EACP;AAEA,QAAM,UAAyF,CAAC;AAEhG,aAAW,QAAQ,OAAO;AACzB,UAAM,gBAAgB,KAAK,eAAe,KAAK;AAC/C,UAAM,kBACL,KAAK,mBAAmB,KAAK,gBAAgB,KAAK,EAAE,SAAS,IAC1D,KAAK,gBAAgB,KAAK,IAC1B;AACJ,UAAM,mBAAmB,KAAK,kBAAkB,KAAK;AAErD,QAAI,CAAC,iBAAiB,CAAC,kBAAkB;AACxC,cAAQ,KAAK;AAAA,QACZ,OAAO,iBAAiB;AAAA,QACxB,SAAS;AAAA,QACT,IAAI;AAAA,QACJ,OAAO;AAAA,MACR,CAAC;AACD;AAAA,IACD;AAEA,UAAM,SAAS,MAAM;AAAA,MACpB;AAAA,MACA;AAAA,MACA;AAAA,IACD;AAEA,QAAI,CAAC,OAAO,IAAI;AACf,cAAQ,KAAK;AAAA,QACZ,OAAO;AAAA,QACP,SAAS;AAAA,QACT,IAAI;AAAA,QACJ,OAAO,OAAO;AAAA,MACf,CAAC;AACD;AAAA,IACD;AAEA,YAAQ,KAAK;AAAA,MACZ,OAAO;AAAA,MACP,SAAS;AAAA,MACT,IAAI;AAAA,IACL,CAAC;AAAA,EACF;AAEA,QAAM,SAAS,QAAQ,OAAO,CAAC,SAAS,CAAC,KAAK,EAAE;AAChD,MAAI,OAAO,QAAQ;AAClB,WAAO;AAAA,MACN;AAAA,QACC,SAAS;AAAA,QACT,OAAO;AAAA,QACP;AAAA,MACD;AAAA,MACA,EAAE,QAAQ,IAAI;AAAA,IACf;AAAA,EACD;AAEA,SAAO,KAAK;AAAA,IACX,SAAS;AAAA,IACT,SAAS,aAAa,QAAQ,MAAM;AAAA,IACpC;AAAA,EACD,CAAC;AACF;AAEA,eAAsB,aAAa,SAAkB;AACpD,QAAM,OAAO,MAAM,QAAQ,SAAS;AACpC,QAAM,gBAAgB,KAAK,IAAI,eAAe,GAAG,SAAS,EAAE,KAAK;AACjE,QAAM,qBAAqB,KAAK,IAAI,iBAAiB,GAAG,SAAS;AACjE,QAAM,mBAAmB,KAAK,IAAI,kBAAkB,GAAG,SAAS,EAAE,KAAK;AACvE,QAAM,kBACL,sBAAsB,mBAAmB,KAAK,EAAE,SAAS,IACtD,mBAAmB,KAAK,IACxB;AAEJ,MAAI,CAAC,iBAAiB,CAAC,kBAAkB;AACxC,WAAO;AAAA,MACN;AAAA,QACC,SAAS;AAAA,QACT,OAAO;AAAA,MACR;AAAA,MACA,EAAE,QAAQ,IAAI;AAAA,IACf;AAAA,EACD;AAEA,QAAM,SAAS,MAAM;AAAA,IACpB;AAAA,IACA;AAAA,IACA;AAAA,EACD;AAEA,MAAI,CAAC,OAAO,IAAI;AACf,WAAO;AAAA,MACN;AAAA,QACC,SAAS;AAAA,QACT,OAAO,OAAO;AAAA,MACf;AAAA,MACA,EAAE,QAAQ,IAAI;AAAA,IACf;AAAA,EACD;AAEA,SAAO,KAAK;AAAA,IACX,SAAS;AAAA,IACT,SAAS;AAAA,IACT,OAAO,OAAO;AAAA,IACd,SAAS,OAAO;AAAA,IAChB,gBAAgB,OAAO;AAAA,EACxB,CAAC;AACF;AAEA,eAAsB,8BAA8B;AACnD,MAAI;AACH,UAAM,gBAAgB,EAAE,eAAe,KAAK,CAAC;AAC7C,UAAM,6BAA6B;AAEnC,WAAO,KAAK;AAAA,MACX,SAAS;AAAA,MACT,SAAS,4BAA4BM,UAAS,2BAA2B,EAAE,aAAa,CAAC;AAAA,IAC1F,CAAC;AAAA,EACF,SAAS,OAAO;AACf,QAAI,iBAAiB,uBAAuB;AAC3C,aAAO;AAAA,QACN;AAAA,UACC,SAAS;AAAA,UACT,OAAO,MAAM;AAAA,UACb,MAAM;AAAA,UACN,QAAQ,MAAM;AAAA,QACf;AAAA,QACA,EAAE,QAAQ,IAAI;AAAA,MACf;AAAA,IACD;AAEA,UAAM;AAAA,EACP;AACD;AAEA,eAAsB,wBAAwB;AAC7C,MAAI;AACH,UAAM,YAAY,MAAM,qBAAqB;AAC7C,WAAO,KAAK;AAAA,MACX,SAAS;AAAA,MACT,MAAM,UAAU;AAAA,MAChB,QAAQ,UAAU;AAAA,MAClB,UAAU,UAAU;AAAA,IACrB,CAAC;AAAA,EACF,SAAS,OAAO;AACf,QAAI,iBAAiB,uBAAuB;AAC3C,aAAO;AAAA,QACN;AAAA,UACC,SAAS;AAAA,UACT,OAAO,MAAM;AAAA,UACb,MAAM;AAAA,UACN,QAAQ,MAAM;AAAA,QACf;AAAA,QACA,EAAE,QAAQ,IAAI;AAAA,MACf;AAAA,IACD;AAEA,UAAM;AAAA,EACP;AACD;AAEA,eAAsB,qBAAqB,SAAkB;AAC5D,QAAM,OAAO,MAAM,QAAQ,KAAK,EAAE,MAAM,MAAM,IAAI;AAClD,QAAM,qBAAqB,MAAM,uBAAuB;AACxD,QAAM,YAAY,MAAM,qBAAqB;AAE7C,MAAI,CAAC,UAAU,QAAQ,CAAC,oBAAoB;AAC3C,WAAO;AAAA,MACN;AAAA,QACC,SAAS;AAAA,QACT,OAAO;AAAA,QACP,MAAM;AAAA,QACN,QAAQ,UAAU;AAAA,QAClB,UAAU,UAAU;AAAA,MACrB;AAAA,MACA,EAAE,QAAQ,IAAI;AAAA,IACf;AAAA,EACD;AAEA,QAAM,SAAS,MAAM,eAAe,EAAE,gBAAgB,mBAAmB,CAAC;AAE1E,MAAI,CAAC,OAAO,IAAI;AACf,WAAO;AAAA,MACN;AAAA,QACC,SAAS;AAAA,QACT,OAAO,OAAO;AAAA,QACd,QAAQ,YAAY,SAAS,OAAO,SAAS;AAAA,QAC7C,UAAU,cAAc,SAAS,OAAO,WAAW;AAAA,MACpD;AAAA,MACA,EAAE,QAAQ,YAAY,UAAU,OAAO,WAAW,gBAAgB,MAAM,IAAI;AAAA,IAC7E;AAAA,EACD;AAEA,SAAO,KAAK;AAAA,IACX,SAAS;AAAA,IACT,SAAS;AAAA,IACT,YAAY,OAAO;AAAA,IACnB,eAAe,OAAO;AAAA,IACtB,gBAAgB,OAAO;AAAA,IACvB,mBAAmB,OAAO;AAAA,EAC3B,CAAC;AACF;;;AGrUA,SAAS,QAAAC,aAAY;AAWrB,IAAM,qBAAqB;AAC3B,IAAM,mBAAmB;AACzB,IAAM,oBAAoB,KAAK,MAAM,mBAAmB,GAAG;AAC3D,IAAM,sBAAsB,mBAAmB;AAC/C,IAAM,0BAA0B;AAczB,SAAS,SAAS,OAAe,SAAwB;AAC/D,SAAO,gBAAgB,OAAO,OAAO;AACtC;AAEA,SAAS,qBAAqB,aAAqB;AAClD,MAAI;AACH,WAAO,IAAI,IAAI,aAAa,kBAAkB,EAAE;AAAA,EACjD,QAAQ;AACP,WAAO;AAAA,EACR;AACD;AAEA,SAAS,mBAAmB,aAAqB;AAChD,QAAM,iBAAiB,qBAAqB,WAAW;AACvD,MAAI,mBAAmB,KAAK;AAC3B,WAAO;AAAA,EACR;AAEA,QAAM,cAAc,eAAe,QAAQ,QAAQ,EAAE;AACrD,SAAO,aAAa,WAAW;AAChC;AAEA,SAAS,mBAAmB,gBAAwB,MAAgB,mBAA6B;AAChG,MAAI,CAAC,eAAgB,QAAO;AAE5B,QAAM,WAAW,CAAC,GAAG,MAAM,GAAG,iBAAiB,EAAE,KAAK,GAAG,EAAE,YAAY;AACvE,SAAO,SAAS,SAAS,eAAe,YAAY,CAAC;AACtD;AAEA,SAAS,iBAAiB,YAAyB;AAClD,QAAM,UAAU,oBAAI,IAAuB;AAE3C,aAAW,QAAQ,YAAY;AAC9B,UAAM,KAAK,SAAS,KAAK,MAAM,OAAO,KAAK,MAAM,OAAO;AACxD,UAAM,WAAW,QAAQ,IAAI,EAAE;AAC/B,QAAI,CAAC,YAAY,KAAK,QAAQ,SAAS,OAAO;AAC7C,cAAQ,IAAI,IAAI,IAAI;AAAA,IACrB;AAAA,EACD;AAEA,SAAO,CAAC,GAAG,QAAQ,OAAO,CAAC;AAC5B;AAEA,SAAS,kBAAkB,YAAyB,gBAAwB;AAC3E,SAAO,CAAC,GAAG,UAAU,EAAE,KAAK,CAAC,MAAM,UAAU;AAC5C,UAAM,YAAY;AAAA,MACjB;AAAA,MACA,KAAK,MAAM;AAAA,MACX,KAAK,MAAM;AAAA,IACZ,IACG,QACA;AACH,UAAM,aAAa;AAAA,MAClB;AAAA,MACA,MAAM,MAAM;AAAA,MACZ,MAAM,MAAM;AAAA,IACb,IACG,QACA;AACH,UAAM,mBAAmB,CAAC,KAAK,MAAM,iBAAiB,CAAC,0BAA0B;AACjF,UAAM,oBAAoB,CAAC,MAAM,MAAM,iBAAiB,CAAC,0BAA0B;AAEnF,WAAO,KAAK,QAAQ,YAAY,oBAAoB,MAAM,QAAQ,aAAa;AAAA,EAChF,CAAC;AACF;AAEA,SAAS,mBAAmB,WAAmB;AAC9C,SAAO,UACL,YAAY,EACZ,MAAM,eAAe,EACrB,IAAI,CAAC,UAAU,MAAM,KAAK,CAAC,EAC3B,OAAO,OAAO;AACjB;AAEA,SAAS,uBAAuB,WAAmB,gBAAwB;AAC1E,QAAM,kBAAkB,IAAI,IAAI,mBAAmB,SAAS,CAAC;AAC7D,QAAM,cAAc,mBAAmB,cAAc;AAErD,MAAI,CAAC,YAAY,UAAU,CAAC,gBAAgB,KAAM,QAAO;AAEzD,MAAI,UAAU;AACd,aAAW,SAAS,aAAa;AAChC,QAAI,gBAAgB,IAAI,KAAK,GAAG;AAC/B,iBAAW;AAAA,IACZ;AAAA,EACD;AAEA,SAAO,UAAU,YAAY;AAC9B;AAEA,SAAS,mCACR,SACA,WACA,gBACA,cACC;AACD,QAAM,mBAAmB,IAAI,IAAI,UAAU,WAAW,IAAI,CAAC,cAAc,UAAU,YAAY,CAAC,CAAC;AAEjG,SAAO,QACL,OAAO,CAAC,UAAU,CAAC,aAAa,IAAI,SAAS,MAAM,OAAO,MAAM,OAAO,CAAC,CAAC,EACzE,IAAI,CAAC,UAAU;AACf,UAAM,mBAAmB,MAAM,WAAW;AAAA,MAAO,CAAC,cACjD,iBAAiB,IAAI,UAAU,YAAY,CAAC;AAAA,IAC7C;AACA,UAAM,aAAa,MAAM,WAAW;AAAA,MAAK,CAAC,cACzC,UAAU,YAAY,EAAE,SAAS,eAAe,YAAY,CAAC;AAAA,IAC9D;AAEA,QAAI,CAAC,iBAAiB,UAAU,CAAC,YAAY;AAC5C,aAAO;AAAA,IACR;AAEA,UAAM,sBAAsB,KAAK;AAAA,MAChC;AAAA,MACA,GAAG,MAAM,WAAW,IAAI,CAAC,cAAc,uBAAuB,WAAW,cAAc,CAAC;AAAA,IACzF;AACA,UAAM,oBAAoB,CAAC,MAAM,iBAAiB,OAAO;AAEzD,WAAO;AAAA,MACN;AAAA,MACA,OACC,iBAAiB,SAAS,OACzB,aAAa,KAAK,KACnB,sBACA;AAAA,MACD,SAAS;AAAA,IACV;AAAA,EACD,CAAC,EACA,OAAO,CAAC,SAA8B,QAAQ,IAAI,CAAC,EACnD,KAAK,CAAC,MAAM,UAAU,MAAM,QAAQ,KAAK,KAAK;AACjD;AAEA,SAAS,6BACR,SACA,gBACA,cACC;AACD,SAAO,QACL,OAAO,CAAC,UAAU,CAAC,aAAa,IAAI,SAAS,MAAM,OAAO,MAAM,OAAO,CAAC,CAAC,EACzE,IAAI,CAAC,UAAU;AACf,UAAM,sBAAsB,KAAK;AAAA,MAChC;AAAA,MACA,GAAG,MAAM,WAAW,IAAI,CAAC,cAAc,uBAAuB,WAAW,cAAc,CAAC;AAAA,IACzF;AAEA,QAAI,uBAAuB,GAAG;AAC7B,aAAO;AAAA,IACR;AACA,UAAM,oBAAoB,CAAC,MAAM,iBAAiB,MAAM;AAExD,WAAO;AAAA,MACN;AAAA,MACA,OAAO,sBAAsB;AAAA,MAC7B,SAAS;AAAA,IACV;AAAA,EACD,CAAC,EACA,OAAO,CAAC,SAA8B,QAAQ,IAAI,CAAC,EACnD,KAAK,CAAC,MAAM,UAAU,MAAM,QAAQ,KAAK,KAAK;AACjD;AAEA,SAAS,sCACR,eACA,cACC;AACD,SAAO,cACL,OAAO,CAAC,SAAS,KAAK,MAAM,cAAc,EAC1C,OAAO,CAAC,SAAS,CAAC,aAAa,IAAI,SAAS,KAAK,MAAM,OAAO,KAAK,MAAM,OAAO,CAAC,CAAC,EAClF,IAAI,CAAC,UAAU;AAAA,IACf,OAAO,KAAK;AAAA,IACZ,OAAO,IAAI,KAAK;AAAA,IAChB,SAAS;AAAA,EACV,EAAE,EACD,KAAK,CAAC,MAAM,UAAU,MAAM,QAAQ,KAAK,KAAK;AACjD;AAEA,SAAS,0CACR,eACA,cACC;AACD,SAAO,cACL,OAAO,CAAC,SAAS,CAAC,KAAK,MAAM,cAAc,EAC3C,OAAO,CAAC,SAAS,CAAC,aAAa,IAAI,SAAS,KAAK,MAAM,OAAO,KAAK,MAAM,OAAO,CAAC,CAAC,EAClF,IAAI,CAAC,UAAU;AAAA,IACf,OAAO,KAAK;AAAA,IACZ,OAAO,IAAI,KAAK;AAAA,IAChB,SAAS;AAAA,EACV,EAAE,EACD,KAAK,CAAC,MAAM,UAAU,MAAM,QAAQ,KAAK,KAAK;AACjD;AAEA,SAAS,qBACR,uBACA,6BACA,uBACA,gCACA,oCACC;AACD,QAAM,eAA8B,CAAC,GAAG,qBAAqB;AAC7D,QAAM,eAAe,IAAI;AAAA,IACxB,sBAAsB,IAAI,CAAC,SAAS,SAAS,KAAK,MAAM,OAAO,KAAK,MAAM,OAAO,CAAC;AAAA,EACnF;AAEA,MAAI,kBAAkB,sBAAsB,OAAO,CAAC,SAAS,KAAK,MAAM,cAAc,EAAE;AACxF,MAAI,oBAAoB,sBAAsB,SAAS;AAEvD,QAAM,SAAS,CAAC,MAAmB,aAAa,SAAS;AACxD,QAAI,aAAa,UAAU,iBAAkB,QAAO;AAEpD,UAAM,KAAK,SAAS,KAAK,MAAM,OAAO,KAAK,MAAM,OAAO;AACxD,QAAI,aAAa,IAAI,EAAE,EAAG,QAAO;AAEjC,QAAI,YAAY;AACf,UAAI,KAAK,MAAM,kBAAkB,mBAAmB,kBAAmB,QAAO;AAC9E,UAAI,CAAC,KAAK,MAAM,kBAAkB,qBAAqB,oBAAqB,QAAO;AAAA,IACpF;AAEA,iBAAa,IAAI,EAAE;AACnB,iBAAa,KAAK,IAAI;AAEtB,QAAI,KAAK,MAAM,gBAAgB;AAC9B,yBAAmB;AAAA,IACpB,OAAO;AACN,2BAAqB;AAAA,IACtB;AAEA,WAAO;AAAA,EACR;AAEA,QAAM,qBAAqB;AAAA,IAC1B;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACD;AAEA,aAAW,UAAU,oBAAoB;AACxC,eAAW,QAAQ,QAAQ;AAC1B,aAAO,MAAM,IAAI;AAAA,IAClB;AAAA,EACD;AAEA,MAAI,aAAa,SAAS,kBAAkB;AAC3C,eAAW,UAAU,oBAAoB;AACxC,iBAAW,QAAQ,QAAQ;AAC1B,eAAO,MAAM,KAAK;AAAA,MACnB;AAAA,IACD;AAAA,EACD;AAEA,SAAO,aAAa,MAAM,GAAG,gBAAgB;AAC9C;AAEA,eAAsB,uBACrB,KACA,aACC;AACD,QAAM,cAAc,MAAM,gBAAgB,EAAE,eAAe,KAAK,CAAC;AACjE,QAAM,mBAAmB,YAAY,YAAY,IAAI,CAAC,UAAU;AAC/D,UAAM,QAAQ,wBAAwB,OAAO,YAAY,WAAW,IAAI,SAAS,MAAM,OAAO,MAAM,OAAO,CAAC,CAAC;AAC7G,WAAO;AAAA,MACN,GAAG,MAAM;AAAA,MACT,gBAAgB,MAAM;AAAA,MACtB,SAAS,MAAM;AAAA,MACf,mBAAmB,MAAM;AAAA,IAC1B;AAAA,EACD,CAAC;AACD,QAAM,iBAAiB;AAAA,IACtB,SAAS;AAAA,IACT,MAAM,WAAW,gBAAgB;AAAA,EAClC;AAEA,QAAM,iBAAiB,mBAAmB,WAAW;AACrD,QAAM,gBAAgB;AAAA,IACrB;AAAA,MACC,oBAAoB,GAAG,EAAE;AAAA,QAAQ,CAAC,YACjC,eAAe,KAAK,OAAO,SAAS,EAAE,OAAO,IAAI,CAAC,EAAE,IAAI,CAAC,SAAS;AAAA,UACjE,OAAO,IAAI;AAAA,UACX,OAAO,IAAI,SAAS;AAAA,UACpB;AAAA,QACD,EAAE;AAAA,MACH;AAAA,IACD;AAAA,IACA;AAAA,EACD;AAEA,QAAM,OAAO,cAAc,CAAC;AAC5B,MAAI,CAAC,QAAQ,KAAK,QAAQ,MAAM;AAC/B,WAAO;AAAA,EACR;AAEA,QAAM,yBAAyB;AAC/B,QAAM,gBAAgB,uBAAuB,CAAC;AAC9C,QAAM,wBAAwB,uBAAuB,MAAM,GAAG,kBAAkB;AAChF,QAAM,eAAe,oBAAI,IAAY;AAAA,IACpC,SAAS,KAAK,MAAM,OAAO,KAAK,MAAM,OAAO;AAAA,IAC7C,GAAG,sBAAsB,IAAI,CAAC,SAAS,SAAS,KAAK,MAAM,OAAO,KAAK,MAAM,OAAO,CAAC;AAAA,EACtF,CAAC;AAED,QAAM,8BAA8B;AAAA,IACnC;AAAA,IACA,cAAc;AAAA,IACd;AAAA,IACA;AAAA,EACD;AAEA,QAAM,wBAAwB;AAAA,IAC7B;AAAA,IACA;AAAA,IACA;AAAA,EACD;AAEA,QAAM,iCAAiC;AAAA,IACtC,uBAAuB,MAAM,kBAAkB;AAAA,IAC/C;AAAA,EACD;AAEA,QAAM,qCAAqC;AAAA,IAC1C,uBAAuB,MAAM,kBAAkB;AAAA,IAC/C;AAAA,EACD;AAEA,SAAO;AAAA,IACN;AAAA,IACA,cAAc;AAAA,MACb;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACD;AAAA,IACA;AAAA,IACA,SAAS,YAAY;AAAA,IACrB,YAAY,YAAY;AAAA,IACxB,cAAc;AAAA,MACb,QAAQ;AAAA,MACR,eAAe,YAAY,eAAe;AAAA,IAC3C;AAAA,EACD;AACD;AAEO,SAAS,cACf,OACA,cACC;AACD,QAAM,QAAQ,wBAAwB,OAAO,YAAY;AAEzD,SAAO;AAAA,IACN,OAAO,MAAM;AAAA,IACb,SAAS,MAAM;AAAA,IACf,aAAa,MAAM;AAAA,IACnB,QAAQ,MAAM,eAAe;AAAA,IAC7B,YAAY,MAAM;AAAA,IAClB,mBAAmB,MAAM;AAAA,IACzB,OAAO,MAAM,eAAe;AAAA,IAC5B,UAAU,MAAM,eAAe;AAAA,IAC/B,UAAU,MAAM,eAAe;AAAA,IAC/B,gBAAgB,MAAM;AAAA,IACtB,SAAS,MAAM;AAAA,IACf,sBAAsB,MAAM,sBAAsB;AAAA,IAClD,0BAA0B,MAAM,sBAAsB,UAAU,MAAM;AAAA,IACtE,mBAAmB,MAAM;AAAA,IACzB,mBAAmB,MAAM;AAAA,EAC1B;AACD;AAEO,SAAS,oBACf,OACA,OACA,cACC;AACD,QAAM,QAAQ,wBAAwB,OAAO,YAAY;AAEzD,SAAO;AAAA,IACN,OAAO,MAAM;AAAA,IACb,SAAS,MAAM;AAAA,IACf;AAAA,IACA,YAAY,MAAM;AAAA,IAClB,QAAQ,MAAM,eAAe;AAAA,IAC7B,gBAAgB,MAAM;AAAA,IACtB,SAAS,MAAM;AAAA,IACf,sBAAsB,MAAM,sBAAsB;AAAA,IAClD,0BAA0B,MAAM,sBAAsB,UAAU,MAAM;AAAA,IACtE,mBAAmB,MAAM;AAAA,IACzB,mBAAmB,MAAM;AAAA,EAC1B;AACD;AAEA,eAAsB,cAAc,SAAkB;AACrD,QAAM,OAAO,MAAM,QAAQ,SAAS;AACpC,QAAM,MAAM,KAAK,IAAI,gBAAgB,GAAG,SAAS,EAAE,KAAK;AACxD,QAAM,cAAc,KAAK,IAAI,aAAa,GAAG,SAAS,EAAE,KAAK;AAE7D,MAAI,CAAC,OAAO,CAAC,aAAa;AACzB,WAAOC;AAAA,MACN;AAAA,QACC,SAAS;AAAA,QACT,OAAO;AAAA,MACR;AAAA,MACA,EAAE,QAAQ,IAAI;AAAA,IACf;AAAA,EACD;AAEA,MAAI;AACJ,MAAI;AACH,gBAAY,MAAM,uBAAuB,KAAK,WAAW;AAAA,EAC1D,SAAS,OAAO;AACf,QAAI,iBAAiB,uBAAuB;AAC3C,aAAOA;AAAA,QACN;AAAA,UACC,SAAS;AAAA,UACT,OAAO,MAAM;AAAA,UACb,MAAM;AAAA,UACN,QAAQ,MAAM;AAAA,QACf;AAAA,QACA,EAAE,QAAQ,IAAI;AAAA,MACf;AAAA,IACD;AAEA,UAAM;AAAA,EACP;AAEA,MAAI,CAAC,WAAW;AACf,WAAOA;AAAA,MACN;AAAA,QACC,SAAS;AAAA,QACT,OAAO;AAAA,MACR;AAAA,MACA,EAAE,QAAQ,IAAI;AAAA,IACf;AAAA,EACD;AAEA,QAAM,WACL,UAAU,QAAQ,IAAI,SAAS,UAAU,KAAK,MAAM,OAAO,UAAU,KAAK,MAAM,OAAO,CAAC,KACxF,UAAU,KAAK;AAChB,QAAM,cAAc,UAAU,WAAW,IAAI,SAAS,SAAS,OAAO,SAAS,OAAO,CAAC;AAEvF,SAAOA,MAAK;AAAA,IACX,SAAS;AAAA,IACT,OAAO;AAAA,MACN,OAAO,UAAU,KAAK;AAAA,MACtB,KAAK,UAAU,KAAK;AAAA,IACrB;AAAA,IACA,cAAc,UAAU;AAAA,IACxB,OAAO,cAAc,UAAU,WAAW;AAAA,IAC1C,cAAc,UAAU,aAAa,IAAI,CAAC,SAAS;AAClD,YAAM,UACL,UAAU,QAAQ,IAAI,SAAS,KAAK,MAAM,OAAO,KAAK,MAAM,OAAO,CAAC,KAAK,KAAK;AAC/E,YAAM,aAAa,UAAU,WAAW,IAAI,SAAS,QAAQ,OAAO,QAAQ,OAAO,CAAC;AACpF,aAAO,oBAAoB,SAAS,KAAK,OAAO,UAAU;AAAA,IAC3D,CAAC;AAAA,EACF,CAAC;AACF;;;ACheA,SAAS,QAAAC,aAAY;AASrB,SAAS,yBAAyB,eAAyC;AAC1E,SAAO,CAAC,cAAc,OAAO,GAAG,cAAc,YAAY;AAC3D;AAEA,SAAS,sBAAsB,eAAyC;AACvE,SAAO,yBAAyB,aAAa,EAC3C,OAAO,CAAC,UAAU,MAAM,kBAAkB,MAAM,SAAS,CAAC,CAAC,EAC3D,MAAM,GAAG,EAAE,EACX,IAAI,CAAC,WAAW;AAAA,IAChB,OAAO,MAAM;AAAA,IACb,SAAS,MAAM;AAAA,IACf,aAAa,MAAM,OAAO,CAAC;AAAA,EAC5B,EAAE;AACJ;AAEA,SAAS,iBACR,eACA,OACC;AACD,SAAO,KAAK;AAAA,IACX;AAAA,MACC,MAAM;AAAA,MACN,eAAe;AAAA,QACd,OAAO;AAAA,UACN;AAAA,YACC,OAAO;AAAA,YACP,SAAS;AAAA,YACT,YAAY;AAAA,UACb;AAAA,QACD;AAAA,MACD;AAAA,MACA,qBAAqB,sBAAsB,aAAa;AAAA,MACxD,oBAAoB;AAAA,IACrB;AAAA,IACA;AAAA,IACA;AAAA,EACD;AACD;AAEA,SAAS,iBAAiB,cAAgD;AACzE,MAAI;AACH,UAAM,SAAS,KAAK,MAAM,YAAY;AACtC,UAAM,QAAQ,MAAM,QAAQ,QAAQ,KAAK,IAAI,OAAO,QAAQ,CAAC;AAC7D,WAAO,MAAM;AAAA,MACZ,CAAC,SACA,OAAO,MAAM,UAAU,aACtB,KAAK,YAAY,QAAQ,OAAO,KAAK,YAAY,aAClD,OAAO,MAAM,eAAe;AAAA,IAC9B;AAAA,EACD,QAAQ;AACP,WAAO,CAAC;AAAA,EACT;AACD;AAEO,SAAS,2BAA2B;AAAA,EAC1C;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACD,GAKG;AACF,QAAM,OAAgC;AAAA,IACrC,OAAO,YAAY;AAAA,IACnB,OAAO;AAAA,MACN;AAAA,QACC,MAAM;AAAA,QACN,SAAS,CAAC,EAAE,MAAM,cAAc,MAAM,cAAc,CAAC;AAAA,MACtD;AAAA,MACA;AAAA,QACC,MAAM;AAAA,QACN,SAAS,CAAC,EAAE,MAAM,cAAc,MAAM,iBAAiB,SAAS,KAAK,EAAE,CAAC;AAAA,MACzE;AAAA,IACD;AAAA,IACA,MAAM;AAAA,MACL,QAAQ;AAAA,QACP,MAAM;AAAA,QACN,MAAM;AAAA,QACN,QAAQ;AAAA,UACP,MAAM;AAAA,UACN,sBAAsB;AAAA,UACtB,YAAY;AAAA,YACX,OAAO;AAAA,cACN,MAAM;AAAA,cACN,OAAO;AAAA,gBACN,MAAM;AAAA,gBACN,sBAAsB;AAAA,gBACtB,YAAY;AAAA,kBACX,OAAO,EAAE,MAAM,SAAS;AAAA,kBACxB,SAAS,EAAE,MAAM,CAAC,UAAU,MAAM,EAAE;AAAA,kBACpC,YAAY,EAAE,MAAM,SAAS;AAAA,gBAC9B;AAAA,gBACA,UAAU,CAAC,SAAS,WAAW,YAAY;AAAA,cAC5C;AAAA,YACD;AAAA,UACD;AAAA,UACA,UAAU,CAAC,OAAO;AAAA,QACnB;AAAA,MACD;AAAA,IACD;AAAA,EACD;AAEA,MAAI,YAAY,aAAa,YAAY,cAAc,QAAQ;AAC9D,SAAK,YAAY;AAAA,MAChB,QAAQ,YAAY;AAAA,IACrB;AAAA,EACD;AAEA,SAAO;AACR;AAEA,eAAsB,kBAAkB,SAAkB;AACzD,QAAM;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACD,IAAI,2BAA2B;AAE/B,QAAM,OAAO,MAAM,QAAQ,KAAK,EAAE,MAAM,MAAM,IAAI;AAClD,QAAM,UAAU,MAAM;AACtB,QAAM,QAAQ,MAAM,QAAQ,MAAM,KAAK,IAAK,KAAK,QAAoC,CAAC;AAEtF,MAAI,CAAC,WAAW,CAAC,MAAM,QAAQ;AAC9B,WAAOC;AAAA,MACN;AAAA,QACC,SAAS;AAAA,QACT,OAAO;AAAA,MACR;AAAA,MACA,EAAE,QAAQ,IAAI;AAAA,IACf;AAAA,EACD;AAEA,MAAI,oBAAoB;AACvB,UAAM,gBAAgB,MAAM,mBAAmB;AAAA,MAC9C;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACD,CAAC;AAED,WAAOA,MAAK;AAAA,MACX,SAAS;AAAA,MACT,OAAO;AAAA,IACR,CAAC;AAAA,EACF;AAEA,MAAI,CAAC,OAAO,KAAK,GAAG;AACnB,YAAQ,KAAK,sDAAsD;AACnE,WAAOA,MAAK;AAAA,MACX,SAAS;AAAA,MACT,UAAU;AAAA,MACV,OAAO,CAAC;AAAA,IACT,CAAC;AAAA,EACF;AAEA,MAAI;AACH,YAAQ,KAAK,uCAAuC;AAAA,MACnD,OAAO,gBAAgB;AAAA,MACvB,WAAW,gBAAgB,aAAa;AAAA,MACxC;AAAA,MACA;AAAA,MACA,WAAW,MAAM;AAAA,MACjB,WAAW,QAAQ,OAAO,KAAK,CAAC;AAAA,IACjC,CAAC;AAED,UAAM,WAAW,MAAM,MAAM,uCAAuC;AAAA,MACnE,QAAQ;AAAA,MACR,SAAS;AAAA,QACR,eAAe,UAAU,MAAM;AAAA,QAC/B,gBAAgB;AAAA,MACjB;AAAA,MACA,MAAM,KAAK;AAAA,QACV,2BAA2B;AAAA,UAC1B;AAAA,UACA;AAAA,UACA,aAAa;AAAA,UACb;AAAA,QACD,CAAC;AAAA,MACF;AAAA,IACD,CAAC;AAED,QAAI,CAAC,SAAS,IAAI;AACjB,YAAM,YAAY,MAAM,SAAS,KAAK,EAAE,MAAM,MAAM,EAAE;AACtD,cAAQ,MAAM,qCAAqC;AAAA,QAClD,QAAQ,SAAS;AAAA,QACjB,YAAY,SAAS;AAAA,QACrB,MAAM;AAAA,MACP,CAAC;AAED,aAAOA;AAAA,QACN;AAAA,UACC,SAAS;AAAA,UACT,OAAO,8BAA8B,SAAS,MAAM;AAAA,QACrD;AAAA,QACA,EAAE,QAAQ,IAAI;AAAA,MACf;AAAA,IACD;AAEA,UAAM,eAAe,MAAM,SAAS,KAAK;AAC1C,UAAM,eACL,cAAc,eACd,cAAc,QACX,QAAQ,CAAC,SAAc,MAAM,WAAW,CAAC,CAAC,EAC3C,KAAK,CAAC,SAAc,MAAM,IAAI,GAAG,QACnC;AAEA,WAAOA,MAAK;AAAA,MACX,SAAS;AAAA,MACT,OAAO,iBAAiB,YAAY;AAAA,IACrC,CAAC;AAAA,EACF,SAAS,OAAO;AACf,YAAQ,MAAM,oCAAoC,KAAK;AACvD,WAAOA;AAAA,MACN;AAAA,QACC,SAAS;AAAA,QACT,OAAO;AAAA,MACR;AAAA,MACA,EAAE,QAAQ,IAAI;AAAA,IACf;AAAA,EACD;AACD;;;AL7NA,eAAsB,yBACrB,SACA,KACA,SACC;AACD,QAAM,UAAU,SAAS,WAAW;AACpC,QAAM,aACL,OAAO,gBAAgB,eACvB,OAAO,YAAY,QAAQ,eAC3B,YAAY,IAAI,QAAQ;AACzB,QAAM,QAAQ,SAAS,OAAO;AAE9B,MAAI,WAAW,CAAC,OAAO;AACtB,WAAOC;AAAA,MACN;AAAA,QACC,SAAS;AAAA,QACT,OAAO;AAAA,MACR;AAAA,MACA,EAAE,QAAQ,IAAI;AAAA,IACf;AAAA,EACD;AAEA,MAAI,SAAS,QAAQ;AACpB,+BAA2B,iCAAiC,QAAQ,IAAI,GAAG,QAAQ,MAAM,CAAC;AAAA,EAC3F,OAAO;AACN,UAAM,uBAAuB,QAAQ,IAAI,CAAC;AAAA,EAC3C;AAEA,QAAM,SAAS,IAAI,aAAa,IAAI,QAAQ,KAAK;AAEjD,MAAI,WAAW,gBAAgB;AAC9B,WAAO,kBAAkB,OAAO;AAAA,EACjC;AAEA,MAAI,WAAW,WAAW;AACzB,WAAO,cAAc,OAAO;AAAA,EAC7B;AAEA,MAAI,WAAW,eAAe;AAC7B,WAAO,kBAAkB,OAAO;AAAA,EACjC;AAEA,MAAI,WAAW,mBAAmB;AACjC,WAAO,qBAAqB,OAAO;AAAA,EACpC;AAEA,MAAI,WAAW,oBAAoB;AAClC,WAAO,sBAAsB;AAAA,EAC9B;AAEA,MAAI,WAAW,2BAA2B;AACzC,WAAO,4BAA4B;AAAA,EACpC;AAEA,SAAO,aAAa,OAAO;AAC5B;AAEA,SAAS,kBACR,SACiB;AACjB,SAAO,OAAO,EAAE,SAAS,IAAI,MAAM,yBAAyB,SAAS,KAAK,OAAO;AAClF;AAEO,IAAM,UAAU,kBAAkB;",
6
6
  "names": ["json", "basename", "readFile", "writeFile", "dirname", "extname", "basename", "config", "fileExists", "fileExists", "readFile", "extname", "writeFile", "dirname", "basename", "basename", "json", "json", "json", "json", "json"]
7
7
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@walkinissue/angy",
3
- "version": "0.2.18",
3
+ "version": "0.2.19",
4
4
  "description": "Dev-only SvelteKit translation helper for in-app PO catalog lookup, staging, and commit flows.",
5
5
  "type": "module",
6
6
  "files": [