@walkinissue/angy 0.2.17
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/LICENSE +21 -0
- package/README.md +246 -0
- package/dist/client/Angy.svelte +787 -0
- package/dist/client/PendingChangesDialog.svelte +109 -0
- package/dist/client/TranslationAlternativeItem.svelte +309 -0
- package/dist/client/TranslationHelperForm.svelte +645 -0
- package/dist/client/VibeTooltip.svelte +146 -0
- package/dist/client/dragItem.ts +50 -0
- package/dist/client/toggleQA.shared.ts +164 -0
- package/dist/client/translationSuggestions.ts +100 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/plugin.d.ts +26 -0
- package/dist/plugin.js +288 -0
- package/dist/server/types.ts +40 -0
- package/dist/server.d.ts +95 -0
- package/dist/server.js +1094 -0
- package/dist/server.js.map +7 -0
- package/package.json +85 -0
package/dist/plugin.js
ADDED
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
// src/lib/server/config.ts
|
|
2
|
+
import { readFile, unlink, writeFile } from "node:fs/promises";
|
|
3
|
+
import { basename, dirname, extname, isAbsolute, normalize, resolve } from "node:path";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
import { pathToFileURL } from "node:url";
|
|
6
|
+
import { transform } from "esbuild";
|
|
7
|
+
var __dirname = dirname(fileURLToPath(import.meta.url));
|
|
8
|
+
var CONFIG_FILENAMES = [
|
|
9
|
+
"angy.config.ts",
|
|
10
|
+
"angy.config.js",
|
|
11
|
+
"angy.config.mjs",
|
|
12
|
+
"angy.config.cjs"
|
|
13
|
+
];
|
|
14
|
+
function buildDefaultSystemMessage(sourceLocale, targetLocale) {
|
|
15
|
+
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
|
+
}
|
|
17
|
+
function assertNonEmptyString(value, key) {
|
|
18
|
+
if (typeof value !== "string" || !value.trim()) {
|
|
19
|
+
throw new Error(`[angy] ${key} is required and must be a non-empty string.`);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
function validateRoutePath(routePath) {
|
|
23
|
+
if (routePath == null || routePath === "") return;
|
|
24
|
+
if (typeof routePath !== "string" || !routePath.startsWith("/")) {
|
|
25
|
+
throw new Error(`[angy] routePath must be an absolute path starting with "/".`);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
function validateWatchIgnore(watchIgnore) {
|
|
29
|
+
if (watchIgnore == null) return;
|
|
30
|
+
if (!Array.isArray(watchIgnore) || watchIgnore.some((item) => typeof item !== "string")) {
|
|
31
|
+
throw new Error(`[angy] watchIgnore must be an array of strings.`);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
function validateSuggestionProvider(suggestionProvider) {
|
|
35
|
+
if (suggestionProvider == null) return;
|
|
36
|
+
if (typeof suggestionProvider !== "function") {
|
|
37
|
+
throw new Error(`[angy] suggestionProvider must be a function.`);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
var config = {
|
|
41
|
+
basePoPath: resolve(__dirname, "../../locales/en.po"),
|
|
42
|
+
workingPoPath: resolve(__dirname, "../../locales/en-working.po"),
|
|
43
|
+
sourceLocale: "sv",
|
|
44
|
+
targetLocale: "en",
|
|
45
|
+
routePath: "/api/translations",
|
|
46
|
+
apiKey: "",
|
|
47
|
+
systemMessage: buildDefaultSystemMessage("sv", "en"),
|
|
48
|
+
suggestionModel: "gpt-4.1-mini",
|
|
49
|
+
watchIgnore: ["**/en-working.po"]
|
|
50
|
+
};
|
|
51
|
+
var loadedConfigRoot = null;
|
|
52
|
+
var workingCatalogWatchControllers = /* @__PURE__ */ new Set();
|
|
53
|
+
function configureTranslationHelper(next) {
|
|
54
|
+
Object.assign(config, next);
|
|
55
|
+
}
|
|
56
|
+
function inferLocaleFromCatalogPath(path) {
|
|
57
|
+
const fileName = basename(path);
|
|
58
|
+
if (!fileName) return null;
|
|
59
|
+
return fileName.slice(0, fileName.length - extname(fileName).length) || null;
|
|
60
|
+
}
|
|
61
|
+
function resolveLocaleAlias(value, paths) {
|
|
62
|
+
if (value === "working") {
|
|
63
|
+
const locale = inferLocaleFromCatalogPath(paths.workingPoPath);
|
|
64
|
+
if (!locale) {
|
|
65
|
+
throw new Error("[angy] Unable to infer locale from workingPoPath.");
|
|
66
|
+
}
|
|
67
|
+
return locale;
|
|
68
|
+
}
|
|
69
|
+
if (value === "base") {
|
|
70
|
+
const locale = inferLocaleFromCatalogPath(paths.basePoPath);
|
|
71
|
+
if (!locale) {
|
|
72
|
+
throw new Error("[angy] Unable to infer locale from basePoPath.");
|
|
73
|
+
}
|
|
74
|
+
return locale;
|
|
75
|
+
}
|
|
76
|
+
return value;
|
|
77
|
+
}
|
|
78
|
+
function validateLocaleAliasUsage(sourceLocale, targetLocale) {
|
|
79
|
+
if (sourceLocale === "working") {
|
|
80
|
+
throw new Error('[angy] sourceLocale cannot be "working". Use an explicit source locale.');
|
|
81
|
+
}
|
|
82
|
+
if (targetLocale === "base") {
|
|
83
|
+
throw new Error('[angy] targetLocale cannot be "base". Use an explicit target locale or "working".');
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
function registerWorkingCatalogWatchController(controller) {
|
|
87
|
+
workingCatalogWatchControllers.add(controller);
|
|
88
|
+
return () => {
|
|
89
|
+
workingCatalogWatchControllers.delete(controller);
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
function normalizeTranslationHelperConfig(root, next) {
|
|
93
|
+
const normalized = { ...next };
|
|
94
|
+
if (normalized.basePoPath && !isAbsolute(normalized.basePoPath)) {
|
|
95
|
+
normalized.basePoPath = resolve(root, normalized.basePoPath);
|
|
96
|
+
}
|
|
97
|
+
if (normalized.workingPoPath && !isAbsolute(normalized.workingPoPath)) {
|
|
98
|
+
normalized.workingPoPath = resolve(root, normalized.workingPoPath);
|
|
99
|
+
}
|
|
100
|
+
return normalized;
|
|
101
|
+
}
|
|
102
|
+
function resolveConfiguredLocaleAliases(next) {
|
|
103
|
+
const rawSourceLocale = next.sourceLocale;
|
|
104
|
+
const rawTargetLocale = next.targetLocale;
|
|
105
|
+
const resolvedSourceLocale = resolveLocaleAlias(rawSourceLocale, next);
|
|
106
|
+
const resolvedTargetLocale = resolveLocaleAlias(rawTargetLocale, next);
|
|
107
|
+
const usesDefaultSystemMessage = next.systemMessage === buildDefaultSystemMessage(rawSourceLocale, rawTargetLocale);
|
|
108
|
+
return {
|
|
109
|
+
...next,
|
|
110
|
+
sourceLocale: resolvedSourceLocale,
|
|
111
|
+
targetLocale: resolvedTargetLocale,
|
|
112
|
+
systemMessage: usesDefaultSystemMessage ? buildDefaultSystemMessage(resolvedSourceLocale, resolvedTargetLocale) : next.systemMessage
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
function completeAngyConfig(input) {
|
|
116
|
+
assertNonEmptyString(input.basePoPath, "basePoPath");
|
|
117
|
+
assertNonEmptyString(input.workingPoPath, "workingPoPath");
|
|
118
|
+
assertNonEmptyString(input.sourceLocale, "sourceLocale");
|
|
119
|
+
assertNonEmptyString(input.targetLocale, "targetLocale");
|
|
120
|
+
validateLocaleAliasUsage(input.sourceLocale, input.targetLocale);
|
|
121
|
+
if (typeof input.apiKey !== "string") {
|
|
122
|
+
throw new Error(`[angy] apiKey is required and must be a string. Use an empty string to disable suggestions.`);
|
|
123
|
+
}
|
|
124
|
+
validateRoutePath(input.routePath);
|
|
125
|
+
validateWatchIgnore(input.watchIgnore);
|
|
126
|
+
validateSuggestionProvider(input.suggestionProvider);
|
|
127
|
+
return {
|
|
128
|
+
basePoPath: input.basePoPath,
|
|
129
|
+
workingPoPath: input.workingPoPath,
|
|
130
|
+
sourceLocale: input.sourceLocale,
|
|
131
|
+
targetLocale: input.targetLocale,
|
|
132
|
+
routePath: input.routePath ?? "/api/translations",
|
|
133
|
+
apiKey: input.apiKey,
|
|
134
|
+
systemMessage: input.systemMessage ?? buildDefaultSystemMessage(input.sourceLocale, input.targetLocale),
|
|
135
|
+
suggestionModel: input.suggestionModel ?? "gpt-4.1-mini",
|
|
136
|
+
watchIgnore: input.watchIgnore ?? ["**/en-working.po"],
|
|
137
|
+
suggestionProvider: input.suggestionProvider
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
function defineAngyConfig(config2) {
|
|
141
|
+
return completeAngyConfig(config2);
|
|
142
|
+
}
|
|
143
|
+
async function fileExists(path) {
|
|
144
|
+
try {
|
|
145
|
+
await readFile(path);
|
|
146
|
+
return true;
|
|
147
|
+
} catch {
|
|
148
|
+
return false;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
async function loadTsConfigModule(path) {
|
|
152
|
+
const source = await readFile(path, "utf8");
|
|
153
|
+
const transformed = await transform(source, {
|
|
154
|
+
loader: "ts",
|
|
155
|
+
format: "esm",
|
|
156
|
+
target: "es2022"
|
|
157
|
+
});
|
|
158
|
+
const tempPath = `${path}.angy.tmp.mjs`;
|
|
159
|
+
await writeFile(tempPath, transformed.code, "utf8");
|
|
160
|
+
try {
|
|
161
|
+
return await import(`${pathToFileURL(tempPath).href}?t=${Date.now()}`);
|
|
162
|
+
} finally {
|
|
163
|
+
await unlink(tempPath).catch(() => void 0);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
async function loadJsConfigModule(path) {
|
|
167
|
+
return import(pathToFileURL(path).href);
|
|
168
|
+
}
|
|
169
|
+
async function loadAngyConfigFromRoot(root) {
|
|
170
|
+
if (loadedConfigRoot === root) {
|
|
171
|
+
return config;
|
|
172
|
+
}
|
|
173
|
+
for (const filename of CONFIG_FILENAMES) {
|
|
174
|
+
const fullPath = `${root}/${filename}`.replace(/\\/g, "/");
|
|
175
|
+
if (!await fileExists(fullPath)) continue;
|
|
176
|
+
const module = filename.endsWith(".ts") ? await loadTsConfigModule(fullPath) : await loadJsConfigModule(fullPath);
|
|
177
|
+
const raw = module.default ?? module.config ?? {};
|
|
178
|
+
const next = resolveConfiguredLocaleAliases(
|
|
179
|
+
normalizeTranslationHelperConfig(root, completeAngyConfig(raw))
|
|
180
|
+
);
|
|
181
|
+
configureTranslationHelper(next);
|
|
182
|
+
loadedConfigRoot = root;
|
|
183
|
+
return config;
|
|
184
|
+
}
|
|
185
|
+
loadedConfigRoot = root;
|
|
186
|
+
return config;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// src/lib/plugin.ts
|
|
190
|
+
import { basename as basename2, relative } from "node:path";
|
|
191
|
+
function angy(options = {}) {
|
|
192
|
+
let root = process.cwd();
|
|
193
|
+
let resolvedConfig = options.config ?? null;
|
|
194
|
+
let unregisterWatchController = null;
|
|
195
|
+
const rewatchTimers = /* @__PURE__ */ new Map();
|
|
196
|
+
function normalizePath(path) {
|
|
197
|
+
return path.replace(/\\/g, "/");
|
|
198
|
+
}
|
|
199
|
+
return {
|
|
200
|
+
name: "angy",
|
|
201
|
+
apply: "serve",
|
|
202
|
+
async config(config2, env) {
|
|
203
|
+
root = config2.root ? `${process.cwd()}/${config2.root}`.replace(/\\/g, "/") : process.cwd().replace(/\\/g, "/");
|
|
204
|
+
if (env.mode !== "development") {
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
resolvedConfig ??= await loadAngyConfigFromRoot(root);
|
|
208
|
+
if (!resolvedConfig) {
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
211
|
+
const watchIgnore = resolvedConfig.watchIgnore ?? ["**/en-working.po"];
|
|
212
|
+
const workingPoPath = normalizePath(resolvedConfig.workingPoPath ?? "");
|
|
213
|
+
const workingPoRelative = workingPoPath ? normalizePath(relative(root, workingPoPath)) : "";
|
|
214
|
+
const workingPoName = workingPoPath ? basename2(workingPoPath) : "";
|
|
215
|
+
const localeRotation = Array.from(
|
|
216
|
+
new Set(
|
|
217
|
+
[
|
|
218
|
+
resolvedConfig.sourceLocale,
|
|
219
|
+
resolvedConfig.targetLocale,
|
|
220
|
+
workingPoPath ? inferLocaleFromCatalogPath(workingPoPath) : null
|
|
221
|
+
].filter((locale) => Boolean(locale))
|
|
222
|
+
)
|
|
223
|
+
);
|
|
224
|
+
const filteredWatchIgnore = watchIgnore.filter((pattern) => {
|
|
225
|
+
const normalizedPattern = normalizePath(pattern);
|
|
226
|
+
if (!workingPoPath) return true;
|
|
227
|
+
if (normalizedPattern === workingPoPath || normalizedPattern === workingPoRelative) {
|
|
228
|
+
return false;
|
|
229
|
+
}
|
|
230
|
+
if (workingPoName && normalizedPattern.includes(workingPoName)) {
|
|
231
|
+
return false;
|
|
232
|
+
}
|
|
233
|
+
return true;
|
|
234
|
+
});
|
|
235
|
+
return {
|
|
236
|
+
define: {
|
|
237
|
+
__ANGY_ROUTE_PATH__: JSON.stringify(resolvedConfig.routePath ?? "/api/translations"),
|
|
238
|
+
__ANGY_LOCALES: JSON.stringify(localeRotation)
|
|
239
|
+
},
|
|
240
|
+
optimizeDeps: {
|
|
241
|
+
exclude: ["angy", "angy/client", "angy/plugin", "angy/server"]
|
|
242
|
+
},
|
|
243
|
+
server: {
|
|
244
|
+
watch: {
|
|
245
|
+
ignored: filteredWatchIgnore
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
};
|
|
249
|
+
},
|
|
250
|
+
configureServer(server) {
|
|
251
|
+
unregisterWatchController?.();
|
|
252
|
+
unregisterWatchController = registerWorkingCatalogWatchController((path, delayMs = 800) => {
|
|
253
|
+
const normalizedPath = normalizePath(path);
|
|
254
|
+
const existingTimer = rewatchTimers.get(normalizedPath);
|
|
255
|
+
if (existingTimer) {
|
|
256
|
+
clearTimeout(existingTimer);
|
|
257
|
+
} else {
|
|
258
|
+
server.watcher.unwatch(normalizedPath);
|
|
259
|
+
}
|
|
260
|
+
const timer = setTimeout(() => {
|
|
261
|
+
server.watcher.add(normalizedPath);
|
|
262
|
+
rewatchTimers.delete(normalizedPath);
|
|
263
|
+
}, delayMs);
|
|
264
|
+
rewatchTimers.set(normalizedPath, timer);
|
|
265
|
+
});
|
|
266
|
+
},
|
|
267
|
+
configResolved(config2) {
|
|
268
|
+
root = config2.root.replace(/\\/g, "/");
|
|
269
|
+
if (!resolvedConfig) {
|
|
270
|
+
config2.logger?.warn(
|
|
271
|
+
"[angy] No angy.config.(ts|js|mjs|cjs) found. The helper will stay disabled."
|
|
272
|
+
);
|
|
273
|
+
}
|
|
274
|
+
},
|
|
275
|
+
buildEnd() {
|
|
276
|
+
unregisterWatchController?.();
|
|
277
|
+
unregisterWatchController = null;
|
|
278
|
+
for (const timer of rewatchTimers.values()) {
|
|
279
|
+
clearTimeout(timer);
|
|
280
|
+
}
|
|
281
|
+
rewatchTimers.clear();
|
|
282
|
+
}
|
|
283
|
+
};
|
|
284
|
+
}
|
|
285
|
+
export {
|
|
286
|
+
angy,
|
|
287
|
+
defineAngyConfig
|
|
288
|
+
};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
export type TranslationOrigin = "base" | "working";
|
|
2
|
+
|
|
3
|
+
export type CommitBatchItem = {
|
|
4
|
+
resolvedMsgid: string;
|
|
5
|
+
resolvedMsgctxt: string | null;
|
|
6
|
+
translationValue: string;
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
export type PoComment = {
|
|
10
|
+
reference?: string;
|
|
11
|
+
extracted?: string;
|
|
12
|
+
flag?: string;
|
|
13
|
+
previous?: string;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export type PoTranslationEntry = {
|
|
17
|
+
msgid?: string;
|
|
18
|
+
msgid_plural?: string;
|
|
19
|
+
msgstr?: string[];
|
|
20
|
+
msgctxt?: string;
|
|
21
|
+
comments?: PoComment;
|
|
22
|
+
obsolete?: boolean;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export type NormalizedEntry = {
|
|
26
|
+
msgid: string;
|
|
27
|
+
msgctxt: string | null;
|
|
28
|
+
msgidPlural: string | null;
|
|
29
|
+
msgstr: string[];
|
|
30
|
+
references: string[];
|
|
31
|
+
extractedComments: string[];
|
|
32
|
+
flags: string[];
|
|
33
|
+
previous: string[];
|
|
34
|
+
obsolete: boolean;
|
|
35
|
+
searchText: string;
|
|
36
|
+
searchTokens: string[];
|
|
37
|
+
hasTranslation: boolean;
|
|
38
|
+
isFuzzy: boolean | undefined;
|
|
39
|
+
translationOrigin: TranslationOrigin;
|
|
40
|
+
};
|
package/dist/server.d.ts
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import type { RequestHandler } from "@sveltejs/kit";
|
|
2
|
+
|
|
3
|
+
export type TranslationOrigin = "base" | "working";
|
|
4
|
+
|
|
5
|
+
export type TranslationEntry = {
|
|
6
|
+
msgid: string;
|
|
7
|
+
msgctxt: string | null;
|
|
8
|
+
msgidPlural: string | null;
|
|
9
|
+
msgstr: string[];
|
|
10
|
+
references: string[];
|
|
11
|
+
extractedComments: string[];
|
|
12
|
+
flags: string[];
|
|
13
|
+
previous: string[];
|
|
14
|
+
obsolete: boolean;
|
|
15
|
+
hasTranslation?: boolean;
|
|
16
|
+
isFuzzy?: boolean | null;
|
|
17
|
+
isCommittedToWorking?: boolean;
|
|
18
|
+
translationOrigin?: TranslationOrigin | null;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export type TranslationAlternative = {
|
|
22
|
+
msgid: string;
|
|
23
|
+
msgctxt: string | null;
|
|
24
|
+
score: number;
|
|
25
|
+
references: string[];
|
|
26
|
+
msgstr: string[];
|
|
27
|
+
hasTranslation: boolean;
|
|
28
|
+
isFuzzy: boolean | null | undefined;
|
|
29
|
+
isCommittedToWorking?: boolean;
|
|
30
|
+
translationOrigin?: TranslationOrigin | null;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
export type TranslationContextResult = {
|
|
34
|
+
match: {
|
|
35
|
+
score: number;
|
|
36
|
+
via: string;
|
|
37
|
+
};
|
|
38
|
+
entry: TranslationEntry;
|
|
39
|
+
alternatives: TranslationAlternative[];
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
export type SuggestionRequestItem = {
|
|
43
|
+
msgid: string;
|
|
44
|
+
msgctxt: string | null;
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
export type SuggestionResponseItem = {
|
|
48
|
+
msgid: string;
|
|
49
|
+
msgctxt: string | null;
|
|
50
|
+
suggestion: string;
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
export type SuggestionProviderInput = {
|
|
54
|
+
context: TranslationContextResult;
|
|
55
|
+
items: SuggestionRequestItem[];
|
|
56
|
+
sourceLocale: string;
|
|
57
|
+
targetLocale: string;
|
|
58
|
+
systemMessage: string;
|
|
59
|
+
model: string;
|
|
60
|
+
apiKey: string;
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
export type SuggestionProvider = (
|
|
64
|
+
input: SuggestionProviderInput
|
|
65
|
+
) => Promise<SuggestionResponseItem[]>;
|
|
66
|
+
|
|
67
|
+
export type AngyConfigInput = {
|
|
68
|
+
basePoPath: string;
|
|
69
|
+
workingPoPath: string;
|
|
70
|
+
sourceLocale: string;
|
|
71
|
+
targetLocale: string;
|
|
72
|
+
routePath?: string;
|
|
73
|
+
apiKey: string;
|
|
74
|
+
systemMessage?: string;
|
|
75
|
+
suggestionModel?: string;
|
|
76
|
+
watchIgnore?: string[];
|
|
77
|
+
suggestionProvider?: SuggestionProvider;
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
export type AngyResolvedConfig = {
|
|
81
|
+
basePoPath: string;
|
|
82
|
+
workingPoPath: string;
|
|
83
|
+
sourceLocale: string;
|
|
84
|
+
targetLocale: string;
|
|
85
|
+
routePath: string;
|
|
86
|
+
apiKey: string;
|
|
87
|
+
systemMessage: string;
|
|
88
|
+
suggestionModel: string;
|
|
89
|
+
watchIgnore: string[];
|
|
90
|
+
suggestionProvider?: SuggestionProvider;
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
export declare function defineAngyConfig(config: AngyConfigInput): AngyResolvedConfig;
|
|
94
|
+
|
|
95
|
+
export declare const handler: RequestHandler;
|