@stapel/cdn-react 0.1.0
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/CHANGELOG.md +1 -0
- package/MODULE.md +124 -0
- package/README.md +103 -0
- package/dist/api/cdnApi.d.ts +122 -0
- package/dist/api/cdnApi.d.ts.map +1 -0
- package/dist/api/cdnApi.js +27 -0
- package/dist/api/cdnApi.js.map +1 -0
- package/dist/api/generated/schema.d.ts +1050 -0
- package/dist/api/generated/schema.d.ts.map +1 -0
- package/dist/api/generated/schema.js +2 -0
- package/dist/api/generated/schema.js.map +1 -0
- package/dist/api/types.d.ts +76 -0
- package/dist/api/types.d.ts.map +1 -0
- package/dist/api/types.js +2 -0
- package/dist/api/types.js.map +1 -0
- package/dist/default/ErrorAlert.d.ts +8 -0
- package/dist/default/ErrorAlert.d.ts.map +1 -0
- package/dist/default/ErrorAlert.js +26 -0
- package/dist/default/ErrorAlert.js.map +1 -0
- package/dist/default/ImageUploadField.d.ts +12 -0
- package/dist/default/ImageUploadField.d.ts.map +1 -0
- package/dist/default/ImageUploadField.js +48 -0
- package/dist/default/ImageUploadField.js.map +1 -0
- package/dist/default/MediaGalleryField.d.ts +14 -0
- package/dist/default/MediaGalleryField.d.ts.map +1 -0
- package/dist/default/MediaGalleryField.js +65 -0
- package/dist/default/MediaGalleryField.js.map +1 -0
- package/dist/default/index.d.ts +15 -0
- package/dist/default/index.d.ts.map +1 -0
- package/dist/default/index.js +13 -0
- package/dist/default/index.js.map +1 -0
- package/dist/default/phase.d.ts +17 -0
- package/dist/default/phase.d.ts.map +1 -0
- package/dist/default/phase.js +25 -0
- package/dist/default/phase.js.map +1 -0
- package/dist/flows/registry.d.ts +28 -0
- package/dist/flows/registry.d.ts.map +1 -0
- package/dist/flows/registry.js +26 -0
- package/dist/flows/registry.js.map +1 -0
- package/dist/headless/CdnProvider.d.ts +18 -0
- package/dist/headless/CdnProvider.d.ts.map +1 -0
- package/dist/headless/CdnProvider.js +14 -0
- package/dist/headless/CdnProvider.js.map +1 -0
- package/dist/headless/ImageUpload.d.ts +18 -0
- package/dist/headless/ImageUpload.d.ts.map +1 -0
- package/dist/headless/ImageUpload.js +16 -0
- package/dist/headless/ImageUpload.js.map +1 -0
- package/dist/headless/MediaUploader.d.ts +31 -0
- package/dist/headless/MediaUploader.d.ts.map +1 -0
- package/dist/headless/MediaUploader.js +30 -0
- package/dist/headless/MediaUploader.js.map +1 -0
- package/dist/headless/useUploadImage.d.ts +33 -0
- package/dist/headless/useUploadImage.d.ts.map +1 -0
- package/dist/headless/useUploadImage.js +118 -0
- package/dist/headless/useUploadImage.js.map +1 -0
- package/dist/headless/useUploadPreview.d.ts +22 -0
- package/dist/headless/useUploadPreview.d.ts.map +1 -0
- package/dist/headless/useUploadPreview.js +37 -0
- package/dist/headless/useUploadPreview.js.map +1 -0
- package/dist/headless/useUploadQueue.d.ts +83 -0
- package/dist/headless/useUploadQueue.d.ts.map +1 -0
- package/dist/headless/useUploadQueue.js +0 -0
- package/dist/headless/useUploadQueue.js.map +1 -0
- package/dist/i18n/errorsMap.d.ts +12 -0
- package/dist/i18n/errorsMap.d.ts.map +1 -0
- package/dist/i18n/errorsMap.js +22 -0
- package/dist/i18n/errorsMap.js.map +1 -0
- package/dist/i18n/es.d.ts +17 -0
- package/dist/i18n/es.d.ts.map +1 -0
- package/dist/i18n/es.js +65 -0
- package/dist/i18n/es.js.map +1 -0
- package/dist/i18n/generated/errors.es.gen.d.ts +16 -0
- package/dist/i18n/generated/errors.es.gen.d.ts.map +1 -0
- package/dist/i18n/generated/errors.es.gen.js +58 -0
- package/dist/i18n/generated/errors.es.gen.js.map +1 -0
- package/dist/i18n/generated/errors.gen.d.ts +353 -0
- package/dist/i18n/generated/errors.gen.d.ts.map +1 -0
- package/dist/i18n/generated/errors.gen.js +180 -0
- package/dist/i18n/generated/errors.gen.js.map +1 -0
- package/dist/i18n/generated/errors.ru.gen.d.ts +16 -0
- package/dist/i18n/generated/errors.ru.gen.d.ts.map +1 -0
- package/dist/i18n/generated/errors.ru.gen.js +58 -0
- package/dist/i18n/generated/errors.ru.gen.js.map +1 -0
- package/dist/i18n/keys.d.ts +61 -0
- package/dist/i18n/keys.d.ts.map +1 -0
- package/dist/i18n/keys.js +110 -0
- package/dist/i18n/keys.js.map +1 -0
- package/dist/i18n/ru.d.ts +21 -0
- package/dist/i18n/ru.d.ts.map +1 -0
- package/dist/i18n/ru.js +70 -0
- package/dist/i18n/ru.js.map +1 -0
- package/dist/index.d.ts +89 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +83 -0
- package/dist/index.js.map +1 -0
- package/dist/model/context.d.ts +11 -0
- package/dist/model/context.d.ts.map +1 -0
- package/dist/model/context.js +15 -0
- package/dist/model/context.js.map +1 -0
- package/dist/model/hash.d.ts +36 -0
- package/dist/model/hash.d.ts.map +1 -0
- package/dist/model/hash.js +59 -0
- package/dist/model/hash.js.map +1 -0
- package/dist/model/limits.d.ts +86 -0
- package/dist/model/limits.d.ts.map +1 -0
- package/dist/model/limits.js +163 -0
- package/dist/model/limits.js.map +1 -0
- package/dist/model/queries.d.ts +29 -0
- package/dist/model/queries.d.ts.map +1 -0
- package/dist/model/queries.js +32 -0
- package/dist/model/queries.js.map +1 -0
- package/dist/model/queryKeys.d.ts +18 -0
- package/dist/model/queryKeys.d.ts.map +1 -0
- package/dist/model/queryKeys.js +18 -0
- package/dist/model/queryKeys.js.map +1 -0
- package/dist/model/refs.d.ts +54 -0
- package/dist/model/refs.d.ts.map +1 -0
- package/dist/model/refs.js +82 -0
- package/dist/model/refs.js.map +1 -0
- package/dist/model/runtime.d.ts +40 -0
- package/dist/model/runtime.d.ts.map +1 -0
- package/dist/model/runtime.js +23 -0
- package/dist/model/runtime.js.map +1 -0
- package/dist/model/upload.d.ts +88 -0
- package/dist/model/upload.d.ts.map +1 -0
- package/dist/model/upload.js +254 -0
- package/dist/model/upload.js.map +1 -0
- package/llms.txt +83 -0
- package/manifest.json +601 -0
- package/package.json +120 -0
- package/src/analytics/generated/events.json +7 -0
- package/src/api/cdnApi.ts +187 -0
- package/src/api/generated/schema.ts +1052 -0
- package/src/api/types.ts +86 -0
- package/src/default/ErrorAlert.tsx +43 -0
- package/src/default/ImageUploadField.tsx +131 -0
- package/src/default/MediaGalleryField.tsx +233 -0
- package/src/default/index.ts +14 -0
- package/src/default/phase.ts +34 -0
- package/src/flows/registry.ts +38 -0
- package/src/headless/CdnProvider.tsx +19 -0
- package/src/headless/ImageUpload.tsx +24 -0
- package/src/headless/MediaUploader.tsx +43 -0
- package/src/headless/useUploadImage.ts +155 -0
- package/src/headless/useUploadPreview.ts +48 -0
- package/src/headless/useUploadQueue.ts +411 -0
- package/src/i18n/errorsMap.ts +33 -0
- package/src/i18n/es.ts +81 -0
- package/src/i18n/generated/errors.es.gen.ts +64 -0
- package/src/i18n/generated/errors.gen.ts +211 -0
- package/src/i18n/generated/errors.json +403 -0
- package/src/i18n/generated/errors.ru.gen.ts +64 -0
- package/src/i18n/keys.ts +131 -0
- package/src/i18n/ru.ts +85 -0
- package/src/index.ts +154 -0
- package/src/model/context.tsx +25 -0
- package/src/model/hash.ts +67 -0
- package/src/model/limits.ts +207 -0
- package/src/model/queries.ts +64 -0
- package/src/model/queryKeys.ts +23 -0
- package/src/model/refs.ts +95 -0
- package/src/model/runtime.ts +52 -0
- package/src/model/upload.ts +365 -0
- package/tsconfig.json +26 -0
package/src/i18n/es.ts
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import type { I18nDictionary, I18nEngine } from "@stapel/core";
|
|
2
|
+
import { cdnErrorBundleEs } from "./generated/errors.es.gen.js";
|
|
3
|
+
|
|
4
|
+
export { cdnErrorBundleEs } from "./generated/errors.es.gen.js";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Spanish bundle for cdn-react — the `@stapel/cdn-react/i18n/es` subpath.
|
|
8
|
+
* Same two sources as `./ru`: the generated bundle carries the cross-cutting
|
|
9
|
+
* keys stapel-core localizes, and the 11 keys stapel-cdn owns are authored
|
|
10
|
+
* here until upstream ships `translations/errors.es.json`.
|
|
11
|
+
*
|
|
12
|
+
* The UI copy is translated too, not only the errors. An upload control is
|
|
13
|
+
* where a seller spends the most attention while listing something, and a
|
|
14
|
+
* picker that speaks Spanish while its progress line speaks English is the
|
|
15
|
+
* half-translated surface a person notices first.
|
|
16
|
+
*/
|
|
17
|
+
export const cdnI18nBundleEs: I18nDictionary = {
|
|
18
|
+
...cdnErrorBundleEs,
|
|
19
|
+
|
|
20
|
+
// Backend error codes stapel-cdn owns — authored here.
|
|
21
|
+
"error.400.file_hash_required": "Falta el parámetro file_hash",
|
|
22
|
+
"error.400.file_type_not_allowed": "No se permite este tipo de archivo",
|
|
23
|
+
"error.400.invalid_format": "Formato de archivo no admitido",
|
|
24
|
+
"error.400.invalid_hash": "Hash de archivo no válido",
|
|
25
|
+
"error.400.invalid_image_type": "Tipo de imagen desconocido",
|
|
26
|
+
"error.400.missing_fields": "Faltan campos obligatorios",
|
|
27
|
+
"error.400.no_file": "No se ha elegido ningún archivo",
|
|
28
|
+
"error.403.storage_quota_exceeded": "Se ha superado la cuota de almacenamiento",
|
|
29
|
+
"error.404.no_images": "No se han encontrado imágenes procesadas",
|
|
30
|
+
"error.413.file_too_large": "El archivo es demasiado grande",
|
|
31
|
+
"error.503.image_decoder_unavailable":
|
|
32
|
+
"Este servidor no puede procesar imágenes {extension} ahora mismo",
|
|
33
|
+
|
|
34
|
+
// UI copy.
|
|
35
|
+
"cdn.error.unknown": "Algo ha fallado en esta subida",
|
|
36
|
+
|
|
37
|
+
"cdn.pick.image": "Elegir una imagen",
|
|
38
|
+
"cdn.pick.images": "Añadir fotos",
|
|
39
|
+
"cdn.pick.replace": "Reemplazar",
|
|
40
|
+
"cdn.pick.hint": "{formats} · hasta {maxMb} MB",
|
|
41
|
+
"cdn.pick.drop_hint": "Arrastra archivos aquí o haz clic para elegirlos",
|
|
42
|
+
|
|
43
|
+
"cdn.phase.hashing": "Leyendo el archivo…",
|
|
44
|
+
"cdn.phase.checking": "Comprobando si ya lo tenemos…",
|
|
45
|
+
"cdn.phase.uploading": "Subiendo…",
|
|
46
|
+
"cdn.phase.processing": "Preparando las vistas previas…",
|
|
47
|
+
"cdn.phase.done": "Listo",
|
|
48
|
+
"cdn.phase.canceled": "Cancelado",
|
|
49
|
+
"cdn.phase.failed": "Ha fallado",
|
|
50
|
+
"cdn.phase.queued": "Esperando su turno",
|
|
51
|
+
|
|
52
|
+
"cdn.outcome.deduped": "Ya estaba subido: no se ha enviado nada de nuevo",
|
|
53
|
+
"cdn.outcome.variants_pending":
|
|
54
|
+
"Las vistas previas se están generando; la foto ya está guardada",
|
|
55
|
+
"cdn.outcome.dedup_skipped.no_crypto":
|
|
56
|
+
"Esta página no puede calcular el hash, así que se ha omitido la comprobación de duplicados",
|
|
57
|
+
"cdn.outcome.dedup_skipped.unauthorized":
|
|
58
|
+
"La comprobación de duplicados necesita una sesión iniciada; la subida ha continuado",
|
|
59
|
+
"cdn.outcome.dedup_skipped.check_failed":
|
|
60
|
+
"La comprobación de duplicados no ha respondido; la subida ha continuado",
|
|
61
|
+
|
|
62
|
+
"cdn.item.cancel": "Cancelar",
|
|
63
|
+
"cdn.item.retry": "Reintentar",
|
|
64
|
+
"cdn.item.remove": "Quitar",
|
|
65
|
+
"cdn.item.move_earlier": "Mover antes",
|
|
66
|
+
"cdn.item.move_later": "Mover después",
|
|
67
|
+
"cdn.item.cover": "Foto principal",
|
|
68
|
+
"cdn.item.alt": "Foto subida",
|
|
69
|
+
|
|
70
|
+
"cdn.gallery.count": "{used} de {max} fotos",
|
|
71
|
+
"cdn.gallery.empty": "Todavía no hay fotos",
|
|
72
|
+
|
|
73
|
+
"cdn.upload.blocked.full": "Esta galería admite como máximo {max} fotos",
|
|
74
|
+
"cdn.upload.blocked.pending": "Espera a que terminen las subidas",
|
|
75
|
+
"cdn.upload.blocked.failed": "Quita o reintenta las fotos que han fallado",
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
/** Register the Spanish bundle into a core i18n engine. */
|
|
79
|
+
export function registerCdnI18nEs(engine: I18nEngine, locale = "es"): void {
|
|
80
|
+
engine.registerBundle(locale, cdnI18nBundleEs);
|
|
81
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
// AUTO-GENERATED by scripts/gen-errors.mjs — do not edit by hand.
|
|
2
|
+
// Source: stapel-auth canonical errors.json (backend codegen artifact).
|
|
3
|
+
// Regenerate: pnpm gen:errors · Drift gate: pnpm gen:errors:check
|
|
4
|
+
// Locale source: backend translations/errors.es.json (locale catalog
|
|
5
|
+
// beside the canonical errors.json — i18n-shipping.md §1).
|
|
6
|
+
import type { CdnErrorCode } from "./errors.gen.js";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* `es` texts for the backend error codes this catalog carries.
|
|
10
|
+
*
|
|
11
|
+
* PARTIAL, and deliberately typed to say so: 11 key(s) owned by
|
|
12
|
+
* stapel_cdn are absent, because that owner ships no locale catalog
|
|
13
|
+
* (ERRORS_LOCALE_EXEMPT_OWNERS). English for them still comes from the registry
|
|
14
|
+
* artifact via the en bundle; the pair layers its own authored `es`
|
|
15
|
+
* strings over this one until upstream ships translations. `Partial` is what
|
|
16
|
+
* makes the gap visible to TypeScript instead of a silent English fallback.
|
|
17
|
+
*
|
|
18
|
+
* Import from the pair's `./i18n/es` subpath, NOT the main entry, so
|
|
19
|
+
* the locale stays out of hosts that don't ship it.
|
|
20
|
+
*/
|
|
21
|
+
export const cdnErrorBundleEs: Partial<Record<CdnErrorCode, string>> = {
|
|
22
|
+
"error.400.bad_request": "Solicitud incorrecta",
|
|
23
|
+
"error.400.captcha_invalid": "La verificación del captcha ha fallado. Inténtalo de nuevo.",
|
|
24
|
+
"error.400.captcha_required": "Se requiere el token del captcha.",
|
|
25
|
+
"error.400.expected_list": "Se esperaba una lista de elementos",
|
|
26
|
+
"error.400.field.blank": "{field} no puede estar vacío",
|
|
27
|
+
"error.400.field.does_not_exist": "{field} no existe",
|
|
28
|
+
"error.400.field.invalid": "{field} no es válido",
|
|
29
|
+
"error.400.field.invalid_choice": "{field} no es una opción válida",
|
|
30
|
+
"error.400.field.max_length": "{field} debe tener como máximo {max_length} caracteres",
|
|
31
|
+
"error.400.field.max_value": "{field} debe ser como máximo {max_value}",
|
|
32
|
+
"error.400.field.min_length": "{field} debe tener al menos {min_length} caracteres",
|
|
33
|
+
"error.400.field.min_value": "{field} debe ser como mínimo {min_value}",
|
|
34
|
+
"error.400.field.null": "{field} no puede ser nulo",
|
|
35
|
+
"error.400.field.required": "{field} es obligatorio",
|
|
36
|
+
"error.400.field.unique": "{field} debe ser único",
|
|
37
|
+
"error.400.invalid_ad_id": "ID de anuncio no válido",
|
|
38
|
+
"error.400.validation_error": "Error de validación",
|
|
39
|
+
"error.400.verification_failed": "La verificación ha fallado",
|
|
40
|
+
"error.400.verification_invalid_factor": "Este factor de verificación no está disponible",
|
|
41
|
+
"error.401.unauthorized": "Se requiere autenticación",
|
|
42
|
+
"error.402.payment_required": "Se requiere pago",
|
|
43
|
+
"error.403.forbidden": "No tienes permiso para realizar esta acción",
|
|
44
|
+
"error.403.network_blocked": "No se permiten solicitudes desde esta red.",
|
|
45
|
+
"error.403.verification_enrollment_required": "Es necesario registrar un factor de verificación.",
|
|
46
|
+
"error.403.verification_required": "Se requiere verificación adicional",
|
|
47
|
+
"error.404.ad_not_found": "Anuncio no encontrado",
|
|
48
|
+
"error.404.not_found": "Recurso solicitado no encontrado",
|
|
49
|
+
"error.404.verification_challenge_not_found": "Desafío de verificación no encontrado o caducado",
|
|
50
|
+
"error.405.method_not_allowed": "Método no permitido",
|
|
51
|
+
"error.406.not_acceptable": "No aceptable",
|
|
52
|
+
"error.408.request_timeout": "Tiempo de espera de la solicitud agotado",
|
|
53
|
+
"error.409.conflict": "El recurso ya existe",
|
|
54
|
+
"error.410.gone": "El recurso se ha eliminado permanentemente",
|
|
55
|
+
"error.413.payload_too_large": "El cuerpo de la solicitud es demasiado grande",
|
|
56
|
+
"error.415.unsupported_media_type": "Tipo de contenido no compatible",
|
|
57
|
+
"error.422.unprocessable_entity": "Entidad no procesable",
|
|
58
|
+
"error.423.locked": "El recurso está bloqueado",
|
|
59
|
+
"error.423.verification_locked": "Demasiados intentos fallidos — verificación bloqueada",
|
|
60
|
+
"error.429.rate_limit": "Demasiados intentos. Inténtalo de nuevo en {retry_after_minutes} minutos.",
|
|
61
|
+
"error.429.too_many_requests": "Demasiadas solicitudes. Inténtalo de nuevo más tarde.",
|
|
62
|
+
"error.500.internal": "Algo salió mal",
|
|
63
|
+
"error.503.mandate_unavailable": "No se puede verificar el mandato del espacio de trabajo",
|
|
64
|
+
};
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
// AUTO-GENERATED by scripts/gen-errors.mjs — do not edit by hand.
|
|
2
|
+
// Source: stapel-auth canonical errors.json (backend codegen artifact).
|
|
3
|
+
// Regenerate: pnpm gen:errors · Drift gate: pnpm gen:errors:check
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Remediation vocabulary (frontend-core-architecture §2.5). Declared by the
|
|
7
|
+
* backend on the error registry and consumed verbatim from its `errors.json`
|
|
8
|
+
* artifact. A host maps it to UX — retryable → "try again"; `wait_and_retry` →
|
|
9
|
+
* timer from `params.retry_after_minutes`; `verify` → the step-up seam;
|
|
10
|
+
* `fix_input` → highlight the offending field.
|
|
11
|
+
*/
|
|
12
|
+
export type Remediation =
|
|
13
|
+
| "retry"
|
|
14
|
+
| "wait_and_retry"
|
|
15
|
+
| "reauthenticate"
|
|
16
|
+
| "verify"
|
|
17
|
+
| "fix_input"
|
|
18
|
+
| "contact_support"
|
|
19
|
+
| "bug";
|
|
20
|
+
|
|
21
|
+
export interface CdnErrorSpec {
|
|
22
|
+
/** HTTP status the backend raises this key with. */
|
|
23
|
+
readonly status: number;
|
|
24
|
+
/** `{param}` interpolation slots present in the message. */
|
|
25
|
+
readonly params: readonly string[];
|
|
26
|
+
/** Remediation hint declared by the backend (see {@link Remediation}). */
|
|
27
|
+
readonly remediation: Remediation;
|
|
28
|
+
/** English fallback (source: stapel-auth errors.json). */
|
|
29
|
+
readonly en: string;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* The CDN_ERRORS map — every `error.*` key stapel-auth can raise, keyed by its
|
|
34
|
+
* canonical code. The single source of truth behind the en fallback bundle, the
|
|
35
|
+
* remediation lookup, and the manifest `errors` block.
|
|
36
|
+
*/
|
|
37
|
+
export const CDN_ERRORS = {
|
|
38
|
+
"error.400.bad_request": { status: 400, params: [], remediation: "fix_input", en: "Bad request" },
|
|
39
|
+
"error.400.captcha_invalid": { status: 400, params: [], remediation: "retry", en: "Captcha verification failed. Please try again." },
|
|
40
|
+
"error.400.captcha_required": { status: 400, params: [], remediation: "retry", en: "Captcha token is required." },
|
|
41
|
+
"error.400.expected_list": { status: 400, params: [], remediation: "fix_input", en: "Expected a list of items" },
|
|
42
|
+
"error.400.field.blank": { status: 400, params: ["field"], remediation: "fix_input", en: "{field} may not be blank" },
|
|
43
|
+
"error.400.field.does_not_exist": { status: 400, params: ["field"], remediation: "fix_input", en: "{field} does not exist" },
|
|
44
|
+
"error.400.field.invalid": { status: 400, params: ["field"], remediation: "fix_input", en: "{field} is invalid" },
|
|
45
|
+
"error.400.field.invalid_choice": { status: 400, params: ["field"], remediation: "fix_input", en: "{field} is not a valid choice" },
|
|
46
|
+
"error.400.field.max_length": { status: 400, params: ["field","max_length"], remediation: "fix_input", en: "{field} must be at most {max_length} characters" },
|
|
47
|
+
"error.400.field.max_value": { status: 400, params: ["field","max_value"], remediation: "fix_input", en: "{field} must be at most {max_value}" },
|
|
48
|
+
"error.400.field.min_length": { status: 400, params: ["field","min_length"], remediation: "fix_input", en: "{field} must be at least {min_length} characters" },
|
|
49
|
+
"error.400.field.min_value": { status: 400, params: ["field","min_value"], remediation: "fix_input", en: "{field} must be at least {min_value}" },
|
|
50
|
+
"error.400.field.null": { status: 400, params: ["field"], remediation: "fix_input", en: "{field} may not be null" },
|
|
51
|
+
"error.400.field.required": { status: 400, params: ["field"], remediation: "fix_input", en: "{field} is required" },
|
|
52
|
+
"error.400.field.unique": { status: 400, params: ["field"], remediation: "fix_input", en: "{field} must be unique" },
|
|
53
|
+
"error.400.file_hash_required": { status: 400, params: [], remediation: "fix_input", en: "file_hash parameter is required" },
|
|
54
|
+
"error.400.file_type_not_allowed": { status: 400, params: [], remediation: "fix_input", en: "File type not allowed" },
|
|
55
|
+
"error.400.invalid_ad_id": { status: 400, params: [], remediation: "fix_input", en: "Invalid advertisement ID" },
|
|
56
|
+
"error.400.invalid_format": { status: 400, params: [], remediation: "fix_input", en: "Unsupported file format" },
|
|
57
|
+
"error.400.invalid_hash": { status: 400, params: [], remediation: "fix_input", en: "Invalid file hash" },
|
|
58
|
+
"error.400.invalid_image_type": { status: 400, params: [], remediation: "fix_input", en: "Invalid image type" },
|
|
59
|
+
"error.400.missing_fields": { status: 400, params: [], remediation: "fix_input", en: "Required fields are missing" },
|
|
60
|
+
"error.400.no_file": { status: 400, params: [], remediation: "fix_input", en: "No file provided" },
|
|
61
|
+
"error.400.validation_error": { status: 400, params: [], remediation: "fix_input", en: "Validation error" },
|
|
62
|
+
"error.400.verification_failed": { status: 400, params: [], remediation: "verify", en: "Verification failed" },
|
|
63
|
+
"error.400.verification_invalid_factor": { status: 400, params: [], remediation: "verify", en: "This verification factor is not available" },
|
|
64
|
+
"error.401.unauthorized": { status: 401, params: [], remediation: "reauthenticate", en: "Authentication required" },
|
|
65
|
+
"error.402.payment_required": { status: 402, params: [], remediation: "retry", en: "Payment required" },
|
|
66
|
+
"error.403.forbidden": { status: 403, params: [], remediation: "retry", en: "You do not have permission to perform this action" },
|
|
67
|
+
"error.403.network_blocked": { status: 403, params: [], remediation: "contact_support", en: "Requests from this network are not allowed" },
|
|
68
|
+
"error.403.storage_quota_exceeded": { status: 403, params: [], remediation: "retry", en: "Storage quota exceeded" },
|
|
69
|
+
"error.403.verification_enrollment_required": { status: 403, params: [], remediation: "verify", en: "Verification factor enrollment required" },
|
|
70
|
+
"error.403.verification_required": { status: 403, params: [], remediation: "verify", en: "Additional verification required" },
|
|
71
|
+
"error.404.ad_not_found": { status: 404, params: [], remediation: "retry", en: "Listing not found" },
|
|
72
|
+
"error.404.no_images": { status: 404, params: [], remediation: "fix_input", en: "No processed images found" },
|
|
73
|
+
"error.404.not_found": { status: 404, params: [], remediation: "retry", en: "Requested resource not found" },
|
|
74
|
+
"error.404.verification_challenge_not_found": { status: 404, params: [], remediation: "verify", en: "Verification challenge not found or expired" },
|
|
75
|
+
"error.405.method_not_allowed": { status: 405, params: [], remediation: "retry", en: "Method not allowed" },
|
|
76
|
+
"error.406.not_acceptable": { status: 406, params: [], remediation: "retry", en: "Not acceptable" },
|
|
77
|
+
"error.408.request_timeout": { status: 408, params: [], remediation: "retry", en: "Request timeout" },
|
|
78
|
+
"error.409.conflict": { status: 409, params: [], remediation: "fix_input", en: "Resource already exists" },
|
|
79
|
+
"error.410.gone": { status: 410, params: [], remediation: "retry", en: "Resource has been permanently removed" },
|
|
80
|
+
"error.413.file_too_large": { status: 413, params: [], remediation: "retry", en: "File is too large" },
|
|
81
|
+
"error.413.payload_too_large": { status: 413, params: [], remediation: "retry", en: "Request body is too large" },
|
|
82
|
+
"error.415.unsupported_media_type": { status: 415, params: [], remediation: "retry", en: "Unsupported media type" },
|
|
83
|
+
"error.422.unprocessable_entity": { status: 422, params: [], remediation: "wait_and_retry", en: "Unprocessable entity" },
|
|
84
|
+
"error.423.locked": { status: 423, params: [], remediation: "wait_and_retry", en: "Resource is locked" },
|
|
85
|
+
"error.423.verification_locked": { status: 423, params: [], remediation: "wait_and_retry", en: "Too many failed attempts — verification locked" },
|
|
86
|
+
"error.429.rate_limit": { status: 429, params: ["retry_after_minutes"], remediation: "wait_and_retry", en: "Too many attempts. Try again in {retry_after_minutes} minutes." },
|
|
87
|
+
"error.429.too_many_requests": { status: 429, params: [], remediation: "wait_and_retry", en: "Too many requests. Please try again later." },
|
|
88
|
+
"error.500.internal": { status: 500, params: [], remediation: "contact_support", en: "Something went wrong" },
|
|
89
|
+
"error.503.image_decoder_unavailable": { status: 503, params: ["extension"], remediation: "contact_support", en: "This server cannot process {extension} images right now" },
|
|
90
|
+
"error.503.mandate_unavailable": { status: 503, params: [], remediation: "retry", en: "Cannot verify workspace mandate right now" },
|
|
91
|
+
} as const;
|
|
92
|
+
|
|
93
|
+
export type CdnErrorCode = keyof typeof CDN_ERRORS;
|
|
94
|
+
|
|
95
|
+
/** Every backend error code this module can surface, sorted. */
|
|
96
|
+
export const CDN_ERROR_CODES: readonly CdnErrorCode[] = [
|
|
97
|
+
"error.400.bad_request",
|
|
98
|
+
"error.400.captcha_invalid",
|
|
99
|
+
"error.400.captcha_required",
|
|
100
|
+
"error.400.expected_list",
|
|
101
|
+
"error.400.field.blank",
|
|
102
|
+
"error.400.field.does_not_exist",
|
|
103
|
+
"error.400.field.invalid",
|
|
104
|
+
"error.400.field.invalid_choice",
|
|
105
|
+
"error.400.field.max_length",
|
|
106
|
+
"error.400.field.max_value",
|
|
107
|
+
"error.400.field.min_length",
|
|
108
|
+
"error.400.field.min_value",
|
|
109
|
+
"error.400.field.null",
|
|
110
|
+
"error.400.field.required",
|
|
111
|
+
"error.400.field.unique",
|
|
112
|
+
"error.400.file_hash_required",
|
|
113
|
+
"error.400.file_type_not_allowed",
|
|
114
|
+
"error.400.invalid_ad_id",
|
|
115
|
+
"error.400.invalid_format",
|
|
116
|
+
"error.400.invalid_hash",
|
|
117
|
+
"error.400.invalid_image_type",
|
|
118
|
+
"error.400.missing_fields",
|
|
119
|
+
"error.400.no_file",
|
|
120
|
+
"error.400.validation_error",
|
|
121
|
+
"error.400.verification_failed",
|
|
122
|
+
"error.400.verification_invalid_factor",
|
|
123
|
+
"error.401.unauthorized",
|
|
124
|
+
"error.402.payment_required",
|
|
125
|
+
"error.403.forbidden",
|
|
126
|
+
"error.403.network_blocked",
|
|
127
|
+
"error.403.storage_quota_exceeded",
|
|
128
|
+
"error.403.verification_enrollment_required",
|
|
129
|
+
"error.403.verification_required",
|
|
130
|
+
"error.404.ad_not_found",
|
|
131
|
+
"error.404.no_images",
|
|
132
|
+
"error.404.not_found",
|
|
133
|
+
"error.404.verification_challenge_not_found",
|
|
134
|
+
"error.405.method_not_allowed",
|
|
135
|
+
"error.406.not_acceptable",
|
|
136
|
+
"error.408.request_timeout",
|
|
137
|
+
"error.409.conflict",
|
|
138
|
+
"error.410.gone",
|
|
139
|
+
"error.413.file_too_large",
|
|
140
|
+
"error.413.payload_too_large",
|
|
141
|
+
"error.415.unsupported_media_type",
|
|
142
|
+
"error.422.unprocessable_entity",
|
|
143
|
+
"error.423.locked",
|
|
144
|
+
"error.423.verification_locked",
|
|
145
|
+
"error.429.rate_limit",
|
|
146
|
+
"error.429.too_many_requests",
|
|
147
|
+
"error.500.internal",
|
|
148
|
+
"error.503.image_decoder_unavailable",
|
|
149
|
+
"error.503.mandate_unavailable",
|
|
150
|
+
];
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* English fallback bundle — one string per backend key, so a `StapelApiError.code`
|
|
154
|
+
* never renders as a raw key. Merged UNDER the hand-polished UI copy in
|
|
155
|
+
* {@link authI18nBundleEn} (polish wins; this guarantees coverage).
|
|
156
|
+
*/
|
|
157
|
+
export const cdnErrorBundleEn: Record<CdnErrorCode, string> = {
|
|
158
|
+
"error.400.bad_request": "Bad request",
|
|
159
|
+
"error.400.captcha_invalid": "Captcha verification failed. Please try again.",
|
|
160
|
+
"error.400.captcha_required": "Captcha token is required.",
|
|
161
|
+
"error.400.expected_list": "Expected a list of items",
|
|
162
|
+
"error.400.field.blank": "{field} may not be blank",
|
|
163
|
+
"error.400.field.does_not_exist": "{field} does not exist",
|
|
164
|
+
"error.400.field.invalid": "{field} is invalid",
|
|
165
|
+
"error.400.field.invalid_choice": "{field} is not a valid choice",
|
|
166
|
+
"error.400.field.max_length": "{field} must be at most {max_length} characters",
|
|
167
|
+
"error.400.field.max_value": "{field} must be at most {max_value}",
|
|
168
|
+
"error.400.field.min_length": "{field} must be at least {min_length} characters",
|
|
169
|
+
"error.400.field.min_value": "{field} must be at least {min_value}",
|
|
170
|
+
"error.400.field.null": "{field} may not be null",
|
|
171
|
+
"error.400.field.required": "{field} is required",
|
|
172
|
+
"error.400.field.unique": "{field} must be unique",
|
|
173
|
+
"error.400.file_hash_required": "file_hash parameter is required",
|
|
174
|
+
"error.400.file_type_not_allowed": "File type not allowed",
|
|
175
|
+
"error.400.invalid_ad_id": "Invalid advertisement ID",
|
|
176
|
+
"error.400.invalid_format": "Unsupported file format",
|
|
177
|
+
"error.400.invalid_hash": "Invalid file hash",
|
|
178
|
+
"error.400.invalid_image_type": "Invalid image type",
|
|
179
|
+
"error.400.missing_fields": "Required fields are missing",
|
|
180
|
+
"error.400.no_file": "No file provided",
|
|
181
|
+
"error.400.validation_error": "Validation error",
|
|
182
|
+
"error.400.verification_failed": "Verification failed",
|
|
183
|
+
"error.400.verification_invalid_factor": "This verification factor is not available",
|
|
184
|
+
"error.401.unauthorized": "Authentication required",
|
|
185
|
+
"error.402.payment_required": "Payment required",
|
|
186
|
+
"error.403.forbidden": "You do not have permission to perform this action",
|
|
187
|
+
"error.403.network_blocked": "Requests from this network are not allowed",
|
|
188
|
+
"error.403.storage_quota_exceeded": "Storage quota exceeded",
|
|
189
|
+
"error.403.verification_enrollment_required": "Verification factor enrollment required",
|
|
190
|
+
"error.403.verification_required": "Additional verification required",
|
|
191
|
+
"error.404.ad_not_found": "Listing not found",
|
|
192
|
+
"error.404.no_images": "No processed images found",
|
|
193
|
+
"error.404.not_found": "Requested resource not found",
|
|
194
|
+
"error.404.verification_challenge_not_found": "Verification challenge not found or expired",
|
|
195
|
+
"error.405.method_not_allowed": "Method not allowed",
|
|
196
|
+
"error.406.not_acceptable": "Not acceptable",
|
|
197
|
+
"error.408.request_timeout": "Request timeout",
|
|
198
|
+
"error.409.conflict": "Resource already exists",
|
|
199
|
+
"error.410.gone": "Resource has been permanently removed",
|
|
200
|
+
"error.413.file_too_large": "File is too large",
|
|
201
|
+
"error.413.payload_too_large": "Request body is too large",
|
|
202
|
+
"error.415.unsupported_media_type": "Unsupported media type",
|
|
203
|
+
"error.422.unprocessable_entity": "Unprocessable entity",
|
|
204
|
+
"error.423.locked": "Resource is locked",
|
|
205
|
+
"error.423.verification_locked": "Too many failed attempts — verification locked",
|
|
206
|
+
"error.429.rate_limit": "Too many attempts. Try again in {retry_after_minutes} minutes.",
|
|
207
|
+
"error.429.too_many_requests": "Too many requests. Please try again later.",
|
|
208
|
+
"error.500.internal": "Something went wrong",
|
|
209
|
+
"error.503.image_decoder_unavailable": "This server cannot process {extension} images right now",
|
|
210
|
+
"error.503.mandate_unavailable": "Cannot verify workspace mandate right now",
|
|
211
|
+
};
|