import {z} from 'zod' export type GetApiProjectsArgs = {orderBy?: ('id' | 'name' | 'identifier' | 'description' | 'createdAt' | 'updatedAt' | 'lastActivity') | undefined, filter?: string | undefined, userId?: number | undefined, hasManagerAccess?: (1 | 0) | undefined, type?: (1 | 0) | undefined, limit?: number | undefined, offset?: number | undefined}; export type GetApiProjectsOptions = {baseUrl?: string | undefined, fetch?: typeof fetch | undefined}; export const language = z.object({id: z.string().optional(), name: z.string().optional(), editorCode: z.string().optional(), twoLettersCode: z.string().optional(), threeLettersCode: z.string().optional(), locale: z.string().optional(), androidCode: z.string().optional(), bcp47Code: z.string().optional(), osxCode: z.string().optional(), osxLocale: z.string().optional(), pluralCategoryNames: z.array(z.string()).optional(), pluralRules: z.string().optional(), pluralExamples: z.array(z.string()).optional(), textDirection: z.enum(["ltr", "rtl"]).optional(), dialectOf: z.string().nullable().optional()}); export const qaCheckCategories = z.object({empty: z.boolean().optional(), size: z.boolean().optional(), tags: z.boolean().optional(), spaces: z.boolean().optional(), variables: z.boolean().optional(), punctuation: z.boolean().optional(), symbolRegister: z.boolean().optional(), specialSymbols: z.boolean().optional(), wrongTranslation: z.boolean().optional(), spellcheck: z.boolean().optional(), icu: z.boolean().optional(), terms: z.boolean().optional(), duplicate: z.boolean().optional(), ftl: z.boolean().optional(), android: z.boolean().optional(), numbers: z.boolean().optional(), ai: z.boolean().optional(), outdated: z.boolean().optional(), mdx: z.boolean().optional(), unifiedPlaceholders: z.boolean().optional()}); export const qaChecksIgnorableCategories = z.object({empty: z.boolean().optional(), size: z.boolean().optional(), tags: z.boolean().optional(), spaces: z.boolean().optional(), variables: z.boolean().optional(), punctuation: z.boolean().optional(), symbolRegister: z.boolean().optional(), specialSymbols: z.boolean().optional(), wrongTranslation: z.boolean().optional(), spellcheck: z.boolean().optional(), icu: z.boolean().optional(), terms: z.boolean().optional(), duplicate: z.boolean().optional(), ftl: z.boolean().optional(), android: z.boolean().optional(), numbers: z.boolean().optional(), ai: z.boolean().optional(), outdated: z.boolean().optional(), mdx: z.boolean().optional(), unifiedPlaceholders: z.boolean().optional()}); export const languageMappingUk = z.object({name: z.string().optional(), two_letters_code: z.string().optional(), three_letters_code: z.string().optional(), locale: z.string().optional(), locale_with_underscore: z.string().optional(), android_code: z.string().optional(), osx_code: z.string().optional(), osx_locale: z.string().optional()}); export const assignedTm = z.object({priority: z.number().int().optional()}); export const tmPreTranslateProjectSettings = z.object({enabled: z.boolean().optional(), autoApproveOption: z.enum(["all", "perfectMatchOnly", "exceptAutoSubstituted", "perfectMatchApprovedOnly", "none"]).optional(), minimumMatchRatio: z.enum(["perfect", "100"]).optional()}); export const engineMtPreTranslateProjectSettings = z.object({mtId: z.number().int().optional(), languageIds: z.array(z.string()).optional()}); export const mtPreTranslateProjectSettings = z.object({enabled: z.boolean().optional(), mts: z.array(engineMtPreTranslateProjectSettings).optional()}); export const promptAiPreTranslateProjectSettings = z.object({aiPromptId: z.number().int().optional(), languageIds: z.array(z.string()).optional()}); export const aiPreTranslateProjectSettings = z.object({enabled: z.boolean().optional(), aiPrompts: z.array(promptAiPreTranslateProjectSettings).optional()}); export const languageUk = z.object({id: z.string().optional(), name: z.string().optional(), editorCode: z.string().optional(), twoLettersCode: z.string().optional(), threeLettersCode: z.string().optional(), locale: z.string().optional(), androidCode: z.string().optional(), osxCode: z.string().optional(), osxLocale: z.string().optional(), pluralCategoryNames: z.array(z.string()).optional(), pluralRules: z.string().optional(), pluralExamples: z.array(z.string()).optional(), textDirection: z.string().optional(), dialectOf: z.string().nullable().optional()}); export const crowdinStringBasedProjectSettingsResponse = z.object({id: z.number().int().optional(), type: z.number().int().optional(), userId: z.number().int().optional(), sourceLanguageId: z.string().optional(), targetLanguageIds: z.array(z.string()).optional(), languageAccessPolicy: z.string().optional(), name: z.string().optional(), cname: z.string().nullable().optional(), identifier: z.string().optional(), description: z.string().optional(), visibility: z.string().optional(), logo: z.string().optional(), publicDownloads: z.boolean().nullable().optional(), createdAt: z.string().nullable().optional(), updatedAt: z.string().nullable().optional(), lastActivity: z.string().nullable().optional(), sourceLanguage: language.optional(), targetLanguages: z.array(language).optional(), webUrl: z.string().optional(), savingsReportSettingsTemplateId: z.number().int().optional(), translateDuplicates: z.number().int().optional(), tagsDetection: z.number().int().optional(), glossaryAccess: z.boolean().optional(), glossaryAccessOption: z.enum(["readOnly", "fullAccess", "manageDrafts"]).optional(), isMtAllowed: z.boolean().optional(), taskBasedAccessControl: z.boolean().optional(), hiddenStringsProofreadersAccess: z.boolean().optional(), autoSubstitution: z.boolean().optional(), exportTranslatedOnly: z.boolean().optional(), skipUntranslatedStrings: z.boolean().optional(), exportApprovedOnly: z.boolean().optional(), autoTranslateDialects: z.boolean().optional(), useGlobalTm: z.boolean().optional(), showTmSuggestionsDialects: z.boolean().optional(), tmApprovedSuggestionsOnly: z.boolean().optional(), isSuspended: z.boolean().optional(), qaCheckIsActive: z.boolean().optional(), qaCheckCategories: qaCheckCategories.optional(), qaChecksIgnorableCategories: qaChecksIgnorableCategories.optional(), languageMapping: z.object({uk: languageMappingUk.optional()}).optional(), notificationSettings: z.object({translatorNewStrings: z.boolean().optional(), managerNewStrings: z.boolean().optional(), managerLanguageCompleted: z.boolean().optional()}).optional(), defaultTmId: z.number().int().optional(), defaultGlossaryId: z.number().int().optional(), assignedTms: z.object({"1": assignedTm.optional()}).optional(), assignedGlossaries: z.array(z.number().int()).optional(), assignedStyleGuides: z.array(z.number().int()).optional(), tmPenalties: z.object({autoSubstitution: z.number().int().optional(), tmPriority: z.object({priority: z.number().int().optional(), penalty: z.number().int().optional()}).optional(), multipleTranslations: z.number().int().optional(), timeSinceLastUsage: z.object({months: z.number().int().optional(), penalty: z.number().int().optional()}).optional(), timeSinceLastModified: z.object({months: z.number().int().optional(), penalty: z.number().int().optional()}).optional()}).optional(), normalizePlaceholder: z.boolean().optional(), tmPreTranslate: tmPreTranslateProjectSettings.optional(), mtPreTranslate: mtPreTranslateProjectSettings.optional(), aiPreTranslate: aiPreTranslateProjectSettings.optional(), preTranslationAiPromptId: z.number().int().nullable().optional(), editorSuggestionAiPromptId: z.number().int().nullable().optional(), qaCheckActionAiPromptId: z.number().int().nullable().optional(), contextReviewAiPromptId: z.number().int().nullable().optional(), inContext: z.boolean().optional(), inContextProcessHiddenStrings: z.boolean().optional(), inContextPseudoLanguageId: z.string().nullable().optional(), inContextPseudoLanguage: languageUk.nullable().optional()}); export const projectResponse = z.object({id: z.number().int().optional(), type: z.number().int().optional(), userId: z.number().int().optional(), sourceLanguageId: z.string().optional(), targetLanguageIds: z.array(z.string()).optional(), languageAccessPolicy: z.string().optional(), name: z.string().optional(), cname: z.string().nullable().optional(), identifier: z.string().optional(), description: z.string().optional(), visibility: z.string().optional(), logo: z.string().optional(), publicDownloads: z.boolean().nullable().optional(), createdAt: z.string().nullable().optional(), updatedAt: z.string().nullable().optional(), lastActivity: z.string().nullable().optional(), sourceLanguage: language.optional(), targetLanguages: z.array(language).optional(), webUrl: z.string().optional(), savingsReportSettingsTemplateId: z.number().int().optional()}); export const pagination = z.object({offset: z.number().int().optional(), limit: z.number().int().optional()}); export const crowdinStringBasedProjectCollectionResource = z.object({data: z.array(z.union([z.object({data: crowdinStringBasedProjectSettingsResponse}), z.object({data: projectResponse})])), pagination: pagination.optional()}); export const validateResponse = async ( schema: { readonly '~standard': { readonly validate: (value: unknown) => ({ readonly value: Output; readonly issues?: undefined } | { readonly issues: ReadonlyArray<{ readonly message: string; readonly path?: ReadonlyArray | undefined }> }) | Promise<{ readonly value: Output; readonly issues?: undefined } | { readonly issues: ReadonlyArray<{ readonly message: string; readonly path?: ReadonlyArray | undefined }> }> } }, value: unknown ): Promise => { const result = await schema['~standard'].validate(value) if (result.issues !== undefined) { const detail = result.issues .map(({ message, path }) => path?.length ? `${path.map((segment) => String(typeof segment === 'object' ? segment.key : segment)).join('.')}: ${message}` : message ) .join('; ') throw new Error(`Response does not match the schema: ${detail}`, { cause: { issues: result.issues } }) } return result.value }; /** * List Projects * * GET /projects */ export const getApiProjects = async (args: GetApiProjectsArgs, options?: GetApiProjectsOptions) => { const {baseUrl = "https://api.crowdin.com/api/v2", fetch: fetchFn = fetch} = options ?? {} const query = new URLSearchParams() const headers = new Headers() headers.set('accept', "application/json") if (args.orderBy != null) query.append("orderBy", String(args.orderBy)) if (args.filter != null) query.append("filter", String(args.filter)) if (args.userId != null) query.append("userId", String(args.userId)) if (args.hasManagerAccess != null) query.append("hasManagerAccess", String(args.hasManagerAccess)) if (args.type != null) query.append("type", String(args.type)) if (args.limit != null) query.append("limit", String(args.limit)) if (args.offset != null) query.append("offset", String(args.offset)) const url = `${baseUrl.replace(/\/+$/, '')}/projects${query.size > 0 ? `?${query}` : ''}` const res = await fetchFn(url, {method: "GET", headers}) if (!res.ok) throw new Error(`Request failed with status ${res.status}`, {cause: {status: res.status, body: await res.text()}}) return validateResponse(crowdinStringBasedProjectCollectionResource, await res.json()) };