@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.
Files changed (164) hide show
  1. package/CHANGELOG.md +1 -0
  2. package/MODULE.md +124 -0
  3. package/README.md +103 -0
  4. package/dist/api/cdnApi.d.ts +122 -0
  5. package/dist/api/cdnApi.d.ts.map +1 -0
  6. package/dist/api/cdnApi.js +27 -0
  7. package/dist/api/cdnApi.js.map +1 -0
  8. package/dist/api/generated/schema.d.ts +1050 -0
  9. package/dist/api/generated/schema.d.ts.map +1 -0
  10. package/dist/api/generated/schema.js +2 -0
  11. package/dist/api/generated/schema.js.map +1 -0
  12. package/dist/api/types.d.ts +76 -0
  13. package/dist/api/types.d.ts.map +1 -0
  14. package/dist/api/types.js +2 -0
  15. package/dist/api/types.js.map +1 -0
  16. package/dist/default/ErrorAlert.d.ts +8 -0
  17. package/dist/default/ErrorAlert.d.ts.map +1 -0
  18. package/dist/default/ErrorAlert.js +26 -0
  19. package/dist/default/ErrorAlert.js.map +1 -0
  20. package/dist/default/ImageUploadField.d.ts +12 -0
  21. package/dist/default/ImageUploadField.d.ts.map +1 -0
  22. package/dist/default/ImageUploadField.js +48 -0
  23. package/dist/default/ImageUploadField.js.map +1 -0
  24. package/dist/default/MediaGalleryField.d.ts +14 -0
  25. package/dist/default/MediaGalleryField.d.ts.map +1 -0
  26. package/dist/default/MediaGalleryField.js +65 -0
  27. package/dist/default/MediaGalleryField.js.map +1 -0
  28. package/dist/default/index.d.ts +15 -0
  29. package/dist/default/index.d.ts.map +1 -0
  30. package/dist/default/index.js +13 -0
  31. package/dist/default/index.js.map +1 -0
  32. package/dist/default/phase.d.ts +17 -0
  33. package/dist/default/phase.d.ts.map +1 -0
  34. package/dist/default/phase.js +25 -0
  35. package/dist/default/phase.js.map +1 -0
  36. package/dist/flows/registry.d.ts +28 -0
  37. package/dist/flows/registry.d.ts.map +1 -0
  38. package/dist/flows/registry.js +26 -0
  39. package/dist/flows/registry.js.map +1 -0
  40. package/dist/headless/CdnProvider.d.ts +18 -0
  41. package/dist/headless/CdnProvider.d.ts.map +1 -0
  42. package/dist/headless/CdnProvider.js +14 -0
  43. package/dist/headless/CdnProvider.js.map +1 -0
  44. package/dist/headless/ImageUpload.d.ts +18 -0
  45. package/dist/headless/ImageUpload.d.ts.map +1 -0
  46. package/dist/headless/ImageUpload.js +16 -0
  47. package/dist/headless/ImageUpload.js.map +1 -0
  48. package/dist/headless/MediaUploader.d.ts +31 -0
  49. package/dist/headless/MediaUploader.d.ts.map +1 -0
  50. package/dist/headless/MediaUploader.js +30 -0
  51. package/dist/headless/MediaUploader.js.map +1 -0
  52. package/dist/headless/useUploadImage.d.ts +33 -0
  53. package/dist/headless/useUploadImage.d.ts.map +1 -0
  54. package/dist/headless/useUploadImage.js +118 -0
  55. package/dist/headless/useUploadImage.js.map +1 -0
  56. package/dist/headless/useUploadPreview.d.ts +22 -0
  57. package/dist/headless/useUploadPreview.d.ts.map +1 -0
  58. package/dist/headless/useUploadPreview.js +37 -0
  59. package/dist/headless/useUploadPreview.js.map +1 -0
  60. package/dist/headless/useUploadQueue.d.ts +83 -0
  61. package/dist/headless/useUploadQueue.d.ts.map +1 -0
  62. package/dist/headless/useUploadQueue.js +0 -0
  63. package/dist/headless/useUploadQueue.js.map +1 -0
  64. package/dist/i18n/errorsMap.d.ts +12 -0
  65. package/dist/i18n/errorsMap.d.ts.map +1 -0
  66. package/dist/i18n/errorsMap.js +22 -0
  67. package/dist/i18n/errorsMap.js.map +1 -0
  68. package/dist/i18n/es.d.ts +17 -0
  69. package/dist/i18n/es.d.ts.map +1 -0
  70. package/dist/i18n/es.js +65 -0
  71. package/dist/i18n/es.js.map +1 -0
  72. package/dist/i18n/generated/errors.es.gen.d.ts +16 -0
  73. package/dist/i18n/generated/errors.es.gen.d.ts.map +1 -0
  74. package/dist/i18n/generated/errors.es.gen.js +58 -0
  75. package/dist/i18n/generated/errors.es.gen.js.map +1 -0
  76. package/dist/i18n/generated/errors.gen.d.ts +353 -0
  77. package/dist/i18n/generated/errors.gen.d.ts.map +1 -0
  78. package/dist/i18n/generated/errors.gen.js +180 -0
  79. package/dist/i18n/generated/errors.gen.js.map +1 -0
  80. package/dist/i18n/generated/errors.ru.gen.d.ts +16 -0
  81. package/dist/i18n/generated/errors.ru.gen.d.ts.map +1 -0
  82. package/dist/i18n/generated/errors.ru.gen.js +58 -0
  83. package/dist/i18n/generated/errors.ru.gen.js.map +1 -0
  84. package/dist/i18n/keys.d.ts +61 -0
  85. package/dist/i18n/keys.d.ts.map +1 -0
  86. package/dist/i18n/keys.js +110 -0
  87. package/dist/i18n/keys.js.map +1 -0
  88. package/dist/i18n/ru.d.ts +21 -0
  89. package/dist/i18n/ru.d.ts.map +1 -0
  90. package/dist/i18n/ru.js +70 -0
  91. package/dist/i18n/ru.js.map +1 -0
  92. package/dist/index.d.ts +89 -0
  93. package/dist/index.d.ts.map +1 -0
  94. package/dist/index.js +83 -0
  95. package/dist/index.js.map +1 -0
  96. package/dist/model/context.d.ts +11 -0
  97. package/dist/model/context.d.ts.map +1 -0
  98. package/dist/model/context.js +15 -0
  99. package/dist/model/context.js.map +1 -0
  100. package/dist/model/hash.d.ts +36 -0
  101. package/dist/model/hash.d.ts.map +1 -0
  102. package/dist/model/hash.js +59 -0
  103. package/dist/model/hash.js.map +1 -0
  104. package/dist/model/limits.d.ts +86 -0
  105. package/dist/model/limits.d.ts.map +1 -0
  106. package/dist/model/limits.js +163 -0
  107. package/dist/model/limits.js.map +1 -0
  108. package/dist/model/queries.d.ts +29 -0
  109. package/dist/model/queries.d.ts.map +1 -0
  110. package/dist/model/queries.js +32 -0
  111. package/dist/model/queries.js.map +1 -0
  112. package/dist/model/queryKeys.d.ts +18 -0
  113. package/dist/model/queryKeys.d.ts.map +1 -0
  114. package/dist/model/queryKeys.js +18 -0
  115. package/dist/model/queryKeys.js.map +1 -0
  116. package/dist/model/refs.d.ts +54 -0
  117. package/dist/model/refs.d.ts.map +1 -0
  118. package/dist/model/refs.js +82 -0
  119. package/dist/model/refs.js.map +1 -0
  120. package/dist/model/runtime.d.ts +40 -0
  121. package/dist/model/runtime.d.ts.map +1 -0
  122. package/dist/model/runtime.js +23 -0
  123. package/dist/model/runtime.js.map +1 -0
  124. package/dist/model/upload.d.ts +88 -0
  125. package/dist/model/upload.d.ts.map +1 -0
  126. package/dist/model/upload.js +254 -0
  127. package/dist/model/upload.js.map +1 -0
  128. package/llms.txt +83 -0
  129. package/manifest.json +601 -0
  130. package/package.json +120 -0
  131. package/src/analytics/generated/events.json +7 -0
  132. package/src/api/cdnApi.ts +187 -0
  133. package/src/api/generated/schema.ts +1052 -0
  134. package/src/api/types.ts +86 -0
  135. package/src/default/ErrorAlert.tsx +43 -0
  136. package/src/default/ImageUploadField.tsx +131 -0
  137. package/src/default/MediaGalleryField.tsx +233 -0
  138. package/src/default/index.ts +14 -0
  139. package/src/default/phase.ts +34 -0
  140. package/src/flows/registry.ts +38 -0
  141. package/src/headless/CdnProvider.tsx +19 -0
  142. package/src/headless/ImageUpload.tsx +24 -0
  143. package/src/headless/MediaUploader.tsx +43 -0
  144. package/src/headless/useUploadImage.ts +155 -0
  145. package/src/headless/useUploadPreview.ts +48 -0
  146. package/src/headless/useUploadQueue.ts +411 -0
  147. package/src/i18n/errorsMap.ts +33 -0
  148. package/src/i18n/es.ts +81 -0
  149. package/src/i18n/generated/errors.es.gen.ts +64 -0
  150. package/src/i18n/generated/errors.gen.ts +211 -0
  151. package/src/i18n/generated/errors.json +403 -0
  152. package/src/i18n/generated/errors.ru.gen.ts +64 -0
  153. package/src/i18n/keys.ts +131 -0
  154. package/src/i18n/ru.ts +85 -0
  155. package/src/index.ts +154 -0
  156. package/src/model/context.tsx +25 -0
  157. package/src/model/hash.ts +67 -0
  158. package/src/model/limits.ts +207 -0
  159. package/src/model/queries.ts +64 -0
  160. package/src/model/queryKeys.ts +23 -0
  161. package/src/model/refs.ts +95 -0
  162. package/src/model/runtime.ts +52 -0
  163. package/src/model/upload.ts +365 -0
  164. package/tsconfig.json +26 -0
@@ -0,0 +1,58 @@
1
+ /**
2
+ * `ru` texts for the backend error codes this catalog carries.
3
+ *
4
+ * PARTIAL, and deliberately typed to say so: 11 key(s) owned by
5
+ * stapel_cdn are absent, because that owner ships no locale catalog
6
+ * (ERRORS_LOCALE_EXEMPT_OWNERS). English for them still comes from the registry
7
+ * artifact via the en bundle; the pair layers its own authored `ru`
8
+ * strings over this one until upstream ships translations. `Partial` is what
9
+ * makes the gap visible to TypeScript instead of a silent English fallback.
10
+ *
11
+ * Import from the pair's `./i18n/ru` subpath, NOT the main entry, so
12
+ * the locale stays out of hosts that don't ship it.
13
+ */
14
+ export const cdnErrorBundleRu = {
15
+ "error.400.bad_request": "Некорректный запрос",
16
+ "error.400.captcha_invalid": "Проверка капчи не пройдена. Пожалуйста, попробуйте ещё раз.",
17
+ "error.400.captcha_required": "Требуется токен капчи.",
18
+ "error.400.expected_list": "Ожидался список элементов",
19
+ "error.400.field.blank": "Поле «{field}» не может быть пустым",
20
+ "error.400.field.does_not_exist": "«{field}» не существует",
21
+ "error.400.field.invalid": "Поле «{field}» содержит недопустимое значение",
22
+ "error.400.field.invalid_choice": "Недопустимый вариант для поля «{field}»",
23
+ "error.400.field.max_length": "Поле «{field}» должно содержать не более {max_length} символов",
24
+ "error.400.field.max_value": "Значение поля «{field}» должно быть не больше {max_value}",
25
+ "error.400.field.min_length": "Поле «{field}» должно содержать не менее {min_length} символов",
26
+ "error.400.field.min_value": "Значение поля «{field}» должно быть не меньше {min_value}",
27
+ "error.400.field.null": "Поле «{field}» не может быть null",
28
+ "error.400.field.required": "Поле «{field}» обязательно",
29
+ "error.400.field.unique": "Значение поля «{field}» должно быть уникальным",
30
+ "error.400.invalid_ad_id": "Недопустимый идентификатор объявления",
31
+ "error.400.validation_error": "Ошибка валидации",
32
+ "error.400.verification_failed": "Проверка не пройдена",
33
+ "error.400.verification_invalid_factor": "Этот способ подтверждения недоступен",
34
+ "error.401.unauthorized": "Требуется аутентификация",
35
+ "error.402.payment_required": "Требуется оплата",
36
+ "error.403.forbidden": "У вас нет прав для выполнения этого действия",
37
+ "error.403.network_blocked": "Запросы из этой сети не разрешены.",
38
+ "error.403.verification_enrollment_required": "Требуется регистрация фактора подтверждения.",
39
+ "error.403.verification_required": "Требуется дополнительная проверка",
40
+ "error.404.ad_not_found": "Объявление не найдено",
41
+ "error.404.not_found": "Запрошенный ресурс не найден",
42
+ "error.404.verification_challenge_not_found": "Запрос на подтверждение не найден или истёк",
43
+ "error.405.method_not_allowed": "Метод не разрешён",
44
+ "error.406.not_acceptable": "Недопустимый формат ответа",
45
+ "error.408.request_timeout": "Время ожидания запроса истекло",
46
+ "error.409.conflict": "Ресурс уже существует",
47
+ "error.410.gone": "Ресурс был безвозвратно удалён",
48
+ "error.413.payload_too_large": "Тело запроса слишком большое",
49
+ "error.415.unsupported_media_type": "Неподдерживаемый тип данных",
50
+ "error.422.unprocessable_entity": "Невозможно обработать данные запроса",
51
+ "error.423.locked": "Ресурс заблокирован",
52
+ "error.423.verification_locked": "Слишком много неудачных попыток — подтверждение заблокировано",
53
+ "error.429.rate_limit": "Слишком много попыток. Повторите попытку через {retry_after_minutes} мин.",
54
+ "error.429.too_many_requests": "Слишком много запросов. Пожалуйста, повторите попытку позже.",
55
+ "error.500.internal": "Что-то пошло не так",
56
+ "error.503.mandate_unavailable": "Не удалось проверить доступ к рабочему пространству",
57
+ };
58
+ //# sourceMappingURL=errors.ru.gen.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.ru.gen.js","sourceRoot":"","sources":["../../../src/i18n/generated/errors.ru.gen.ts"],"names":[],"mappings":"AAOA;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAA0C;IACrE,uBAAuB,EAAE,qBAAqB;IAC9C,2BAA2B,EAAE,6DAA6D;IAC1F,4BAA4B,EAAE,wBAAwB;IACtD,yBAAyB,EAAE,2BAA2B;IACtD,uBAAuB,EAAE,qCAAqC;IAC9D,gCAAgC,EAAE,yBAAyB;IAC3D,yBAAyB,EAAE,+CAA+C;IAC1E,gCAAgC,EAAE,yCAAyC;IAC3E,4BAA4B,EAAE,gEAAgE;IAC9F,2BAA2B,EAAE,2DAA2D;IACxF,4BAA4B,EAAE,gEAAgE;IAC9F,2BAA2B,EAAE,2DAA2D;IACxF,sBAAsB,EAAE,mCAAmC;IAC3D,0BAA0B,EAAE,4BAA4B;IACxD,wBAAwB,EAAE,gDAAgD;IAC1E,yBAAyB,EAAE,uCAAuC;IAClE,4BAA4B,EAAE,kBAAkB;IAChD,+BAA+B,EAAE,sBAAsB;IACvD,uCAAuC,EAAE,sCAAsC;IAC/E,wBAAwB,EAAE,0BAA0B;IACpD,4BAA4B,EAAE,kBAAkB;IAChD,qBAAqB,EAAE,8CAA8C;IACrE,2BAA2B,EAAE,oCAAoC;IACjE,4CAA4C,EAAE,8CAA8C;IAC5F,iCAAiC,EAAE,mCAAmC;IACtE,wBAAwB,EAAE,uBAAuB;IACjD,qBAAqB,EAAE,8BAA8B;IACrD,4CAA4C,EAAE,6CAA6C;IAC3F,8BAA8B,EAAE,mBAAmB;IACnD,0BAA0B,EAAE,4BAA4B;IACxD,2BAA2B,EAAE,gCAAgC;IAC7D,oBAAoB,EAAE,uBAAuB;IAC7C,gBAAgB,EAAE,gCAAgC;IAClD,6BAA6B,EAAE,8BAA8B;IAC7D,kCAAkC,EAAE,6BAA6B;IACjE,gCAAgC,EAAE,sCAAsC;IACxE,kBAAkB,EAAE,qBAAqB;IACzC,+BAA+B,EAAE,+DAA+D;IAChG,sBAAsB,EAAE,2EAA2E;IACnG,6BAA6B,EAAE,8DAA8D;IAC7F,oBAAoB,EAAE,qBAAqB;IAC3C,+BAA+B,EAAE,qDAAqD;CACvF,CAAC"}
@@ -0,0 +1,61 @@
1
+ import type { I18nDictionary, I18nEngine } from "@stapel/core";
2
+ /**
3
+ * cdn-react's own translation KEYS (frontend-standard §4.2): headless
4
+ * components never render literal strings — hosts resolve these through core's
5
+ * i18n engine (`useT`). Backend error codes flow through the SAME contour: a
6
+ * `StapelApiError.code` is already a key, so the default bundle below ships
7
+ * English for both the backend's codes (generated) and the pair's own UI keys.
8
+ */
9
+ export declare const CDN_I18N_KEYS: {
10
+ readonly unknownError: "cdn.error.unknown";
11
+ readonly pickImage: "cdn.pick.image";
12
+ readonly pickImages: "cdn.pick.images";
13
+ readonly pickReplace: "cdn.pick.replace";
14
+ readonly pickHint: "cdn.pick.hint";
15
+ readonly pickDropHint: "cdn.pick.drop_hint";
16
+ readonly phaseHashing: "cdn.phase.hashing";
17
+ readonly phaseChecking: "cdn.phase.checking";
18
+ readonly phaseUploading: "cdn.phase.uploading";
19
+ readonly phaseProcessing: "cdn.phase.processing";
20
+ readonly phaseDone: "cdn.phase.done";
21
+ readonly phaseCanceled: "cdn.phase.canceled";
22
+ readonly phaseFailed: "cdn.phase.failed";
23
+ readonly phaseQueued: "cdn.phase.queued";
24
+ readonly deduped: "cdn.outcome.deduped";
25
+ readonly variantsPending: "cdn.outcome.variants_pending";
26
+ readonly dedupSkippedNoCrypto: "cdn.outcome.dedup_skipped.no_crypto";
27
+ readonly dedupSkippedUnauthorized: "cdn.outcome.dedup_skipped.unauthorized";
28
+ readonly dedupSkippedCheckFailed: "cdn.outcome.dedup_skipped.check_failed";
29
+ readonly itemCancel: "cdn.item.cancel";
30
+ readonly itemRetry: "cdn.item.retry";
31
+ readonly itemRemove: "cdn.item.remove";
32
+ readonly itemMoveEarlier: "cdn.item.move_earlier";
33
+ readonly itemMoveLater: "cdn.item.move_later";
34
+ readonly itemCover: "cdn.item.cover";
35
+ readonly itemAlt: "cdn.item.alt";
36
+ readonly galleryCount: "cdn.gallery.count";
37
+ readonly galleryEmpty: "cdn.gallery.empty";
38
+ readonly blockedFull: "cdn.upload.blocked.full";
39
+ readonly blockedPending: "cdn.upload.blocked.pending";
40
+ readonly blockedFailed: "cdn.upload.blocked.failed";
41
+ readonly errorFileHashRequired: "error.400.file_hash_required";
42
+ readonly errorFileTypeNotAllowed: "error.400.file_type_not_allowed";
43
+ readonly errorInvalidFormat: "error.400.invalid_format";
44
+ readonly errorInvalidHash: "error.400.invalid_hash";
45
+ readonly errorInvalidImageType: "error.400.invalid_image_type";
46
+ readonly errorMissingFields: "error.400.missing_fields";
47
+ readonly errorNoFile: "error.400.no_file";
48
+ readonly errorStorageQuotaExceeded: "error.403.storage_quota_exceeded";
49
+ readonly errorNoImages: "error.404.no_images";
50
+ readonly errorFileTooLarge: "error.413.file_too_large";
51
+ readonly errorImageDecoderUnavailable: "error.503.image_decoder_unavailable";
52
+ };
53
+ /**
54
+ * The English bundle: the generated backend fallbacks first, this pair's own
55
+ * UI copy over them. English is INLINE (not a separate subpath) so a host that
56
+ * registers nothing still renders sentences instead of raw keys.
57
+ */
58
+ export declare const cdnI18nBundleEn: I18nDictionary;
59
+ /** Register the English bundle into a core i18n engine. */
60
+ export declare function registerCdnI18n(engine: I18nEngine, locale?: string): void;
61
+ //# sourceMappingURL=keys.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"keys.d.ts","sourceRoot":"","sources":["../../src/i18n/keys.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAG/D;;;;;;GAMG;AACH,eAAO,MAAM,aAAa;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAgEhB,CAAC;AAEX;;;;GAIG;AACH,eAAO,MAAM,eAAe,EAAE,cA4C7B,CAAC;AAEF,2DAA2D;AAC3D,wBAAgB,eAAe,CAAC,MAAM,EAAE,UAAU,EAAE,MAAM,SAAO,GAAG,IAAI,CAEvE"}
@@ -0,0 +1,110 @@
1
+ import { cdnErrorBundleEn } from "./generated/errors.gen.js";
2
+ /**
3
+ * cdn-react's own translation KEYS (frontend-standard §4.2): headless
4
+ * components never render literal strings — hosts resolve these through core's
5
+ * i18n engine (`useT`). Backend error codes flow through the SAME contour: a
6
+ * `StapelApiError.code` is already a key, so the default bundle below ships
7
+ * English for both the backend's codes (generated) and the pair's own UI keys.
8
+ */
9
+ export const CDN_I18N_KEYS = {
10
+ unknownError: "cdn.error.unknown",
11
+ // The picker
12
+ pickImage: "cdn.pick.image",
13
+ pickImages: "cdn.pick.images",
14
+ pickReplace: "cdn.pick.replace",
15
+ pickHint: "cdn.pick.hint",
16
+ pickDropHint: "cdn.pick.drop_hint",
17
+ // Phases. The upload reports which STEP is running, never a percentage —
18
+ // see model/upload.ts for why there is no honest number to show.
19
+ phaseHashing: "cdn.phase.hashing",
20
+ phaseChecking: "cdn.phase.checking",
21
+ phaseUploading: "cdn.phase.uploading",
22
+ phaseProcessing: "cdn.phase.processing",
23
+ phaseDone: "cdn.phase.done",
24
+ phaseCanceled: "cdn.phase.canceled",
25
+ phaseFailed: "cdn.phase.failed",
26
+ phaseQueued: "cdn.phase.queued",
27
+ // Outcomes worth saying out loud
28
+ deduped: "cdn.outcome.deduped",
29
+ variantsPending: "cdn.outcome.variants_pending",
30
+ dedupSkippedNoCrypto: "cdn.outcome.dedup_skipped.no_crypto",
31
+ dedupSkippedUnauthorized: "cdn.outcome.dedup_skipped.unauthorized",
32
+ dedupSkippedCheckFailed: "cdn.outcome.dedup_skipped.check_failed",
33
+ // Per-item controls
34
+ itemCancel: "cdn.item.cancel",
35
+ itemRetry: "cdn.item.retry",
36
+ itemRemove: "cdn.item.remove",
37
+ itemMoveEarlier: "cdn.item.move_earlier",
38
+ itemMoveLater: "cdn.item.move_later",
39
+ itemCover: "cdn.item.cover",
40
+ itemAlt: "cdn.item.alt",
41
+ // The gallery
42
+ galleryCount: "cdn.gallery.count",
43
+ galleryEmpty: "cdn.gallery.empty",
44
+ // Blocked controls — every one of these is the `code` of an
45
+ // ActionAvailability, so a switched-off button always has a sentence.
46
+ blockedFull: "cdn.upload.blocked.full",
47
+ blockedPending: "cdn.upload.blocked.pending",
48
+ blockedFailed: "cdn.upload.blocked.failed",
49
+ // Backend error keys the pair OWNS the localization of. stapel-cdn ships
50
+ // English only (no `translations/` directory at all), so its 11 keys are
51
+ // absent from the generated ru/es bundles and are authored in `./i18n/<lang>`
52
+ // instead — the stapel-forms/stapel_attributes precedent, applied twice
53
+ // before this (forms, chat). Listed here so `i18n-key-exists` knows them and
54
+ // `test/i18n.test.ts` can prove all three locales carry them.
55
+ errorFileHashRequired: "error.400.file_hash_required",
56
+ errorFileTypeNotAllowed: "error.400.file_type_not_allowed",
57
+ errorInvalidFormat: "error.400.invalid_format",
58
+ errorInvalidHash: "error.400.invalid_hash",
59
+ errorInvalidImageType: "error.400.invalid_image_type",
60
+ errorMissingFields: "error.400.missing_fields",
61
+ errorNoFile: "error.400.no_file",
62
+ errorStorageQuotaExceeded: "error.403.storage_quota_exceeded",
63
+ errorNoImages: "error.404.no_images",
64
+ errorFileTooLarge: "error.413.file_too_large",
65
+ errorImageDecoderUnavailable: "error.503.image_decoder_unavailable",
66
+ };
67
+ /**
68
+ * The English bundle: the generated backend fallbacks first, this pair's own
69
+ * UI copy over them. English is INLINE (not a separate subpath) so a host that
70
+ * registers nothing still renders sentences instead of raw keys.
71
+ */
72
+ export const cdnI18nBundleEn = {
73
+ ...cdnErrorBundleEn,
74
+ "cdn.error.unknown": "Something went wrong with this upload",
75
+ "cdn.pick.image": "Choose an image",
76
+ "cdn.pick.images": "Add photos",
77
+ "cdn.pick.replace": "Replace",
78
+ "cdn.pick.hint": "{formats} · up to {maxMb} MB",
79
+ "cdn.pick.drop_hint": "Drop files here, or click to choose",
80
+ "cdn.phase.hashing": "Reading the file…",
81
+ "cdn.phase.checking": "Checking whether we already have it…",
82
+ "cdn.phase.uploading": "Uploading…",
83
+ "cdn.phase.processing": "Preparing previews…",
84
+ "cdn.phase.done": "Ready",
85
+ "cdn.phase.canceled": "Canceled",
86
+ "cdn.phase.failed": "Failed",
87
+ "cdn.phase.queued": "Waiting its turn",
88
+ "cdn.outcome.deduped": "Already uploaded — nothing was sent again",
89
+ "cdn.outcome.variants_pending": "Previews are still being generated; the photo is saved",
90
+ "cdn.outcome.dedup_skipped.no_crypto": "This page cannot hash files, so the duplicate check was skipped",
91
+ "cdn.outcome.dedup_skipped.unauthorized": "The duplicate check needs a signed-in account; the upload went ahead",
92
+ "cdn.outcome.dedup_skipped.check_failed": "The duplicate check did not answer; the upload went ahead",
93
+ "cdn.item.cancel": "Cancel",
94
+ "cdn.item.retry": "Try again",
95
+ "cdn.item.remove": "Remove",
96
+ "cdn.item.move_earlier": "Move earlier",
97
+ "cdn.item.move_later": "Move later",
98
+ "cdn.item.cover": "Cover photo",
99
+ "cdn.item.alt": "Uploaded photo",
100
+ "cdn.gallery.count": "{used} of {max} photos",
101
+ "cdn.gallery.empty": "No photos yet",
102
+ "cdn.upload.blocked.full": "This gallery holds at most {max} photos",
103
+ "cdn.upload.blocked.pending": "Wait for the uploads to finish",
104
+ "cdn.upload.blocked.failed": "Remove or retry the photos that failed",
105
+ };
106
+ /** Register the English bundle into a core i18n engine. */
107
+ export function registerCdnI18n(engine, locale = "en") {
108
+ engine.registerBundle(locale, cdnI18nBundleEn);
109
+ }
110
+ //# sourceMappingURL=keys.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"keys.js","sourceRoot":"","sources":["../../src/i18n/keys.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,21 @@
1
+ import type { I18nDictionary, I18nEngine } from "@stapel/core";
2
+ export { cdnErrorBundleRu } from "./generated/errors.ru.gen.js";
3
+ /**
4
+ * Russian bundle for cdn-react — shipped as the `@stapel/cdn-react/i18n/ru`
5
+ * subpath (i18n-shipping.md §2) so the locale is opt-in: hosts that don't
6
+ * register it never carry these strings (the main entry does not import this
7
+ * module — gated by size-limit and the bundle-purity test).
8
+ *
9
+ * TWO SOURCES, ON PURPOSE. The generated `cdnErrorBundleRu` covers the 42
10
+ * cross-cutting keys stapel-core owns and localizes. The 11 keys stapel-cdn
11
+ * owns are NOT in it, and cannot be: the module ships no `translations/`
12
+ * directory at all, so the generator emits a `Partial` bundle and says so in
13
+ * its own header (`ERRORS_LOCALE_EXEMPT_OWNERS`, the stapel-forms precedent).
14
+ * They are authored below, beside the UI copy. When upstream ships
15
+ * `translations/errors.ru.json`, these eleven lines are deleted and the
16
+ * generated bundle covers them — the keys and the texts do not move.
17
+ */
18
+ export declare const cdnI18nBundleRu: I18nDictionary;
19
+ /** Register the Russian bundle into a core i18n engine. */
20
+ export declare function registerCdnI18nRu(engine: I18nEngine, locale?: string): void;
21
+ //# sourceMappingURL=ru.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ru.d.ts","sourceRoot":"","sources":["../../src/i18n/ru.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAG/D,OAAO,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AAEhE;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,eAAe,EAAE,cA2D7B,CAAC;AAEF,2DAA2D;AAC3D,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,UAAU,EAAE,MAAM,SAAO,GAAG,IAAI,CAEzE"}
@@ -0,0 +1,70 @@
1
+ import { cdnErrorBundleRu } from "./generated/errors.ru.gen.js";
2
+ export { cdnErrorBundleRu } from "./generated/errors.ru.gen.js";
3
+ /**
4
+ * Russian bundle for cdn-react — shipped as the `@stapel/cdn-react/i18n/ru`
5
+ * subpath (i18n-shipping.md §2) so the locale is opt-in: hosts that don't
6
+ * register it never carry these strings (the main entry does not import this
7
+ * module — gated by size-limit and the bundle-purity test).
8
+ *
9
+ * TWO SOURCES, ON PURPOSE. The generated `cdnErrorBundleRu` covers the 42
10
+ * cross-cutting keys stapel-core owns and localizes. The 11 keys stapel-cdn
11
+ * owns are NOT in it, and cannot be: the module ships no `translations/`
12
+ * directory at all, so the generator emits a `Partial` bundle and says so in
13
+ * its own header (`ERRORS_LOCALE_EXEMPT_OWNERS`, the stapel-forms precedent).
14
+ * They are authored below, beside the UI copy. When upstream ships
15
+ * `translations/errors.ru.json`, these eleven lines are deleted and the
16
+ * generated bundle covers them — the keys and the texts do not move.
17
+ */
18
+ export const cdnI18nBundleRu = {
19
+ // Backend error codes — generated ru texts for every key core owns.
20
+ ...cdnErrorBundleRu,
21
+ // Backend error codes stapel-cdn owns — authored here (see the note above).
22
+ "error.400.file_hash_required": "Не передан параметр file_hash",
23
+ "error.400.file_type_not_allowed": "Такой тип файла загружать нельзя",
24
+ "error.400.invalid_format": "Формат файла не поддерживается",
25
+ "error.400.invalid_hash": "Некорректный хеш файла",
26
+ "error.400.invalid_image_type": "Неизвестный тип изображения",
27
+ "error.400.missing_fields": "Заполнены не все обязательные поля",
28
+ "error.400.no_file": "Файл не выбран",
29
+ "error.403.storage_quota_exceeded": "Исчерпана квота на хранение",
30
+ "error.404.no_images": "Обработанных изображений не найдено",
31
+ "error.413.file_too_large": "Файл слишком большой",
32
+ "error.503.image_decoder_unavailable": "Сервер сейчас не может обработать изображения {extension}",
33
+ // UI copy.
34
+ "cdn.error.unknown": "С этой загрузкой что-то пошло не так",
35
+ "cdn.pick.image": "Выберите изображение",
36
+ "cdn.pick.images": "Добавить фото",
37
+ "cdn.pick.replace": "Заменить",
38
+ "cdn.pick.hint": "{formats} · до {maxMb} МБ",
39
+ "cdn.pick.drop_hint": "Перетащите файлы сюда или нажмите, чтобы выбрать",
40
+ "cdn.phase.hashing": "Читаем файл…",
41
+ "cdn.phase.checking": "Проверяем, нет ли такого файла…",
42
+ "cdn.phase.uploading": "Загружаем…",
43
+ "cdn.phase.processing": "Готовим превью…",
44
+ "cdn.phase.done": "Готово",
45
+ "cdn.phase.canceled": "Отменено",
46
+ "cdn.phase.failed": "Не удалось",
47
+ "cdn.phase.queued": "Ожидает очереди",
48
+ "cdn.outcome.deduped": "Такой файл уже загружен — повторно ничего не отправлялось",
49
+ "cdn.outcome.variants_pending": "Превью ещё готовятся; фото сохранено",
50
+ "cdn.outcome.dedup_skipped.no_crypto": "Эта страница не может посчитать хеш, поэтому проверка на дубликат пропущена",
51
+ "cdn.outcome.dedup_skipped.unauthorized": "Проверка на дубликат требует входа в аккаунт; загрузка выполнена без неё",
52
+ "cdn.outcome.dedup_skipped.check_failed": "Проверка на дубликат не ответила; загрузка выполнена без неё",
53
+ "cdn.item.cancel": "Отменить",
54
+ "cdn.item.retry": "Повторить",
55
+ "cdn.item.remove": "Удалить",
56
+ "cdn.item.move_earlier": "Переместить назад",
57
+ "cdn.item.move_later": "Переместить вперёд",
58
+ "cdn.item.cover": "Главное фото",
59
+ "cdn.item.alt": "Загруженное фото",
60
+ "cdn.gallery.count": "{used} из {max} фото",
61
+ "cdn.gallery.empty": "Фото пока нет",
62
+ "cdn.upload.blocked.full": "В галерее не больше {max} фото",
63
+ "cdn.upload.blocked.pending": "Дождитесь окончания загрузки",
64
+ "cdn.upload.blocked.failed": "Удалите или повторите неудавшиеся фото",
65
+ };
66
+ /** Register the Russian bundle into a core i18n engine. */
67
+ export function registerCdnI18nRu(engine, locale = "ru") {
68
+ engine.registerBundle(locale, cdnI18nBundleRu);
69
+ }
70
+ //# sourceMappingURL=ru.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ru.js","sourceRoot":"","sources":["../../src/i18n/ru.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AAEhE,OAAO,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AAEhE;;;;;;;;;;;;;;GAcG;AACH,MAAM,CAAC,MAAM,eAAe,GAAmB;IAC7C,oEAAoE;IACpE,GAAG,gBAAgB;IAEnB,4EAA4E;IAC5E,8BAA8B,EAAE,+BAA+B;IAC/D,iCAAiC,EAAE,kCAAkC;IACrE,0BAA0B,EAAE,gCAAgC;IAC5D,wBAAwB,EAAE,wBAAwB;IAClD,8BAA8B,EAAE,6BAA6B;IAC7D,0BAA0B,EAAE,oCAAoC;IAChE,mBAAmB,EAAE,gBAAgB;IACrC,kCAAkC,EAAE,6BAA6B;IACjE,qBAAqB,EAAE,qCAAqC;IAC5D,0BAA0B,EAAE,sBAAsB;IAClD,qCAAqC,EACnC,2DAA2D;IAE7D,WAAW;IACX,mBAAmB,EAAE,sCAAsC;IAE3D,gBAAgB,EAAE,sBAAsB;IACxC,iBAAiB,EAAE,eAAe;IAClC,kBAAkB,EAAE,UAAU;IAC9B,eAAe,EAAE,2BAA2B;IAC5C,oBAAoB,EAAE,kDAAkD;IAExE,mBAAmB,EAAE,cAAc;IACnC,oBAAoB,EAAE,iCAAiC;IACvD,qBAAqB,EAAE,YAAY;IACnC,sBAAsB,EAAE,iBAAiB;IACzC,gBAAgB,EAAE,QAAQ;IAC1B,oBAAoB,EAAE,UAAU;IAChC,kBAAkB,EAAE,YAAY;IAChC,kBAAkB,EAAE,iBAAiB;IAErC,qBAAqB,EAAE,2DAA2D;IAClF,8BAA8B,EAAE,sCAAsC;IACtE,qCAAqC,EACnC,6EAA6E;IAC/E,wCAAwC,EACtC,0EAA0E;IAC5E,wCAAwC,EACtC,8DAA8D;IAEhE,iBAAiB,EAAE,UAAU;IAC7B,gBAAgB,EAAE,WAAW;IAC7B,iBAAiB,EAAE,SAAS;IAC5B,uBAAuB,EAAE,mBAAmB;IAC5C,qBAAqB,EAAE,oBAAoB;IAC3C,gBAAgB,EAAE,cAAc;IAChC,cAAc,EAAE,kBAAkB;IAElC,mBAAmB,EAAE,sBAAsB;IAC3C,mBAAmB,EAAE,eAAe;IAEpC,yBAAyB,EAAE,gCAAgC;IAC3D,4BAA4B,EAAE,8BAA8B;IAC5D,2BAA2B,EAAE,wCAAwC;CACtE,CAAC;AAEF,2DAA2D;AAC3D,MAAM,UAAU,iBAAiB,CAAC,MAAkB,EAAE,MAAM,GAAG,IAAI;IACjE,MAAM,CAAC,cAAc,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;AACjD,CAAC"}
@@ -0,0 +1,89 @@
1
+ /**
2
+ * `@stapel/cdn-react` — the headless React pair for stapel-cdn
3
+ * (frontend-standard §2). Business + state only, zero visual opinion; the antd
4
+ * skin lives behind the `./default` subpath, so a host that renders its own
5
+ * upload control carries none of it.
6
+ *
7
+ * ── The one-liner ──────────────────────────────────────────────────────────
8
+ *
9
+ * ```tsx
10
+ * const runtime = createCdnRuntime({ baseUrl: "/cdn/api/v1/" });
11
+ * <CdnProvider runtime={runtime}>
12
+ * <MediaUploader max={10} onRefsChange={setImagesDraft}>
13
+ * {(bag) => <YourGrid bag={bag} />}
14
+ * </MediaUploader>
15
+ * </CdnProvider>
16
+ * ```
17
+ *
18
+ * ── The three properties this pair exists to guarantee ─────────────────────
19
+ *
20
+ * 1. **Dedup happens before the bytes move.** The flow hashes the file, asks
21
+ * `file/exists/`, and on a hit returns the reference having sent NOTHING.
22
+ * That is the property the storefront spec asks to be proven, and
23
+ * `test/dedup.test.ts` proves it by counting requests. The pre-check is an
24
+ * optimisation, so it is never allowed to fail the upload: a guest (401 —
25
+ * the endpoint is stricter than the upload endpoints), a page with no
26
+ * `crypto.subtle`, or a check that simply errors all fall through to the
27
+ * POST and say so in `dedupSkipped`.
28
+ * 2. **The unit handed out is the reference, not a URL.** `<type>/<hash>` is
29
+ * what `Profile.avatar` and `Listing.images_draft` store, and it is opaque:
30
+ * the pair does not build URLs out of it and neither should a caller. The
31
+ * row that comes back carries the URLs, and `toStapelImage` converts its
32
+ * ladder for `@stapel/image` in the single place that knows the two
33
+ * contracts spell `tier` differently.
34
+ * 3. **A switched-off control states its reason.** `canAdd` and `settled` are
35
+ * `ActionAvailability`, so "the gallery is full", "wait for the uploads"
36
+ * and "retry the ones that failed" are three different sentences rather
37
+ * than one grey button.
38
+ *
39
+ * ── What this pair does NOT do ─────────────────────────────────────────────
40
+ *
41
+ * No progress percentage: `fetch` cannot observe request-body progress and
42
+ * `crypto.subtle` reports nothing mid-digest, so the bag names the PHASE and a
43
+ * skin shows an indeterminate indicator. Forking onto `XMLHttpRequest` to get
44
+ * a number would mean a second transport with its own auth, refresh and error
45
+ * handling — the exact duplication this package was extracted to end.
46
+ *
47
+ * No `refs/sync/`: that endpoint is `IsServiceRequest` and unreachable from a
48
+ * browser by construction. Reference bookkeeping belongs to the module that
49
+ * owns the entity.
50
+ *
51
+ * No public read-by-reference: `file/exists/` is owner-scoped, so `useCdnRef`
52
+ * resolves the CALLER's own references (a reopened draft) and cannot render a
53
+ * stranger's gallery. Recorded as an upstream gap in the README rather than
54
+ * worked around with a URL convention this pair would have invented.
55
+ *
56
+ * Layers: api → model → headless → i18n. Generated surfaces (the typed schema,
57
+ * the error map, the manifest, llms.txt) are produced by the monorepo `gen:*`
58
+ * drivers from stapel-cdn's own `docs/` artifacts and stand under drift gates.
59
+ */
60
+ export { createCdnApi } from "./api/cdnApi.js";
61
+ export type { CdnApi } from "./api/cdnApi.js";
62
+ export type { CdnFileExistsResponse, CdnFileKind, CdnFileModel, CdnFileUploadResponse, CdnImage, CdnImageUploadResponse, CdnRef, CdnVariantMeta, CdnVideo, CdnVideoUploadResponse, ParsedCdnRef, Schemas, } from "./api/types.js";
63
+ export { createCdnRuntime } from "./model/runtime.js";
64
+ export type { CdnRuntime, CreateCdnRuntimeOptions } from "./model/runtime.js";
65
+ export { CdnRuntimeContext, useCdnAnalytics, useCdnApi, useCdnRuntime, } from "./model/context.js";
66
+ export { cdnQueryKeys } from "./model/queryKeys.js";
67
+ export { useCdnRef } from "./model/queries.js";
68
+ export type { CdnRefBag } from "./model/queries.js";
69
+ export { isUploadCanceled, runUpload, targetAssetType, UploadCanceled, } from "./model/upload.js";
70
+ export type { CdnUploadTarget, CdnVariantWaitOptions, DedupSkipReason, RunUploadOptions, UploadOutcome, UploadPhase, } from "./model/upload.js";
71
+ export { acceptAttribute, CDN_DEFAULT_LIMITS, ERROR_FILE_TOO_LARGE, ERROR_INVALID_FORMAT, ERROR_NO_FILE, fileExtension, resolveCdnLimits, validateFile, } from "./model/limits.js";
72
+ export type { CdnIntakeLimits, CdnLimits, CdnLimitsOverride, } from "./model/limits.js";
73
+ export { formatCdnRef, parseCdnRef, refOf, toStapelImage } from "./model/refs.js";
74
+ export { canHashLocally, sha256Hex } from "./model/hash.js";
75
+ export { CdnProvider } from "./headless/CdnProvider.js";
76
+ export { ImageUpload } from "./headless/ImageUpload.js";
77
+ export { MediaUploader } from "./headless/MediaUploader.js";
78
+ export { useUploadImage } from "./headless/useUploadImage.js";
79
+ export type { UploadImageBag } from "./headless/useUploadImage.js";
80
+ export { useUploadQueue } from "./headless/useUploadQueue.js";
81
+ export type { UploadItem, UploadQueueBag, UseUploadQueueOptions, } from "./headless/useUploadQueue.js";
82
+ export { smallestVariantUrl, useUploadPreview } from "./headless/useUploadPreview.js";
83
+ export type { UploadPreview } from "./headless/useUploadPreview.js";
84
+ export { CDN_FLOWS, flowEndpoints } from "./flows/registry.js";
85
+ export type { CdnFlowId, CdnFlowSpec, FlowEndpoint } from "./flows/registry.js";
86
+ export { CDN_I18N_KEYS, cdnI18nBundleEn, registerCdnI18n } from "./i18n/keys.js";
87
+ export { CDN_ERROR_CODES, CDN_ERRORS, cdnErrorBundleEn, explainCdnError, } from "./i18n/errorsMap.js";
88
+ export type { CdnErrorCode, CdnErrorSpec, Remediation, } from "./i18n/errorsMap.js";
89
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0DG;AAGH,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAC/C,YAAY,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AAC9C,YAAY,EACV,qBAAqB,EACrB,WAAW,EACX,YAAY,EACZ,qBAAqB,EACrB,QAAQ,EACR,sBAAsB,EACtB,MAAM,EACN,cAAc,EACd,QAAQ,EACR,sBAAsB,EACtB,YAAY,EACZ,OAAO,GACR,MAAM,gBAAgB,CAAC;AAGxB,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AACtD,YAAY,EAAE,UAAU,EAAE,uBAAuB,EAAE,MAAM,oBAAoB,CAAC;AAC9E,OAAO,EACL,iBAAiB,EACjB,eAAe,EACf,SAAS,EACT,aAAa,GACd,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AACpD,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAC/C,YAAY,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AACpD,OAAO,EACL,gBAAgB,EAChB,SAAS,EACT,eAAe,EACf,cAAc,GACf,MAAM,mBAAmB,CAAC;AAC3B,YAAY,EACV,eAAe,EACf,qBAAqB,EACrB,eAAe,EACf,gBAAgB,EAChB,aAAa,EACb,WAAW,GACZ,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACL,eAAe,EACf,kBAAkB,EAClB,oBAAoB,EACpB,oBAAoB,EACpB,aAAa,EACb,aAAa,EACb,gBAAgB,EAChB,YAAY,GACb,MAAM,mBAAmB,CAAC;AAC3B,YAAY,EACV,eAAe,EACf,SAAS,EACT,iBAAiB,GAClB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,KAAK,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAClF,OAAO,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAG5D,OAAO,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAC;AACxD,OAAO,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAC;AACxD,OAAO,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAC;AAC5D,OAAO,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAC9D,YAAY,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AACnE,OAAO,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAC9D,YAAY,EACV,UAAU,EACV,cAAc,EACd,qBAAqB,GACtB,MAAM,8BAA8B,CAAC;AACtC,OAAO,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,gCAAgC,CAAC;AACtF,YAAY,EAAE,aAAa,EAAE,MAAM,gCAAgC,CAAC;AAGpE,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAC/D,YAAY,EAAE,SAAS,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAGhF,OAAO,EAAE,aAAa,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AACjF,OAAO,EACL,eAAe,EACf,UAAU,EACV,gBAAgB,EAChB,eAAe,GAChB,MAAM,qBAAqB,CAAC;AAC7B,YAAY,EACV,YAAY,EACZ,YAAY,EACZ,WAAW,GACZ,MAAM,qBAAqB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,83 @@
1
+ /**
2
+ * `@stapel/cdn-react` — the headless React pair for stapel-cdn
3
+ * (frontend-standard §2). Business + state only, zero visual opinion; the antd
4
+ * skin lives behind the `./default` subpath, so a host that renders its own
5
+ * upload control carries none of it.
6
+ *
7
+ * ── The one-liner ──────────────────────────────────────────────────────────
8
+ *
9
+ * ```tsx
10
+ * const runtime = createCdnRuntime({ baseUrl: "/cdn/api/v1/" });
11
+ * <CdnProvider runtime={runtime}>
12
+ * <MediaUploader max={10} onRefsChange={setImagesDraft}>
13
+ * {(bag) => <YourGrid bag={bag} />}
14
+ * </MediaUploader>
15
+ * </CdnProvider>
16
+ * ```
17
+ *
18
+ * ── The three properties this pair exists to guarantee ─────────────────────
19
+ *
20
+ * 1. **Dedup happens before the bytes move.** The flow hashes the file, asks
21
+ * `file/exists/`, and on a hit returns the reference having sent NOTHING.
22
+ * That is the property the storefront spec asks to be proven, and
23
+ * `test/dedup.test.ts` proves it by counting requests. The pre-check is an
24
+ * optimisation, so it is never allowed to fail the upload: a guest (401 —
25
+ * the endpoint is stricter than the upload endpoints), a page with no
26
+ * `crypto.subtle`, or a check that simply errors all fall through to the
27
+ * POST and say so in `dedupSkipped`.
28
+ * 2. **The unit handed out is the reference, not a URL.** `<type>/<hash>` is
29
+ * what `Profile.avatar` and `Listing.images_draft` store, and it is opaque:
30
+ * the pair does not build URLs out of it and neither should a caller. The
31
+ * row that comes back carries the URLs, and `toStapelImage` converts its
32
+ * ladder for `@stapel/image` in the single place that knows the two
33
+ * contracts spell `tier` differently.
34
+ * 3. **A switched-off control states its reason.** `canAdd` and `settled` are
35
+ * `ActionAvailability`, so "the gallery is full", "wait for the uploads"
36
+ * and "retry the ones that failed" are three different sentences rather
37
+ * than one grey button.
38
+ *
39
+ * ── What this pair does NOT do ─────────────────────────────────────────────
40
+ *
41
+ * No progress percentage: `fetch` cannot observe request-body progress and
42
+ * `crypto.subtle` reports nothing mid-digest, so the bag names the PHASE and a
43
+ * skin shows an indeterminate indicator. Forking onto `XMLHttpRequest` to get
44
+ * a number would mean a second transport with its own auth, refresh and error
45
+ * handling — the exact duplication this package was extracted to end.
46
+ *
47
+ * No `refs/sync/`: that endpoint is `IsServiceRequest` and unreachable from a
48
+ * browser by construction. Reference bookkeeping belongs to the module that
49
+ * owns the entity.
50
+ *
51
+ * No public read-by-reference: `file/exists/` is owner-scoped, so `useCdnRef`
52
+ * resolves the CALLER's own references (a reopened draft) and cannot render a
53
+ * stranger's gallery. Recorded as an upstream gap in the README rather than
54
+ * worked around with a URL convention this pair would have invented.
55
+ *
56
+ * Layers: api → model → headless → i18n. Generated surfaces (the typed schema,
57
+ * the error map, the manifest, llms.txt) are produced by the monorepo `gen:*`
58
+ * drivers from stapel-cdn's own `docs/` artifacts and stand under drift gates.
59
+ */
60
+ // ── api ──────────────────────────────────────────────────────────────────────
61
+ export { createCdnApi } from "./api/cdnApi.js";
62
+ // ── model: the flow, the mirror, the reference ───────────────────────────────
63
+ export { createCdnRuntime } from "./model/runtime.js";
64
+ export { CdnRuntimeContext, useCdnAnalytics, useCdnApi, useCdnRuntime, } from "./model/context.js";
65
+ export { cdnQueryKeys } from "./model/queryKeys.js";
66
+ export { useCdnRef } from "./model/queries.js";
67
+ export { isUploadCanceled, runUpload, targetAssetType, UploadCanceled, } from "./model/upload.js";
68
+ export { acceptAttribute, CDN_DEFAULT_LIMITS, ERROR_FILE_TOO_LARGE, ERROR_INVALID_FORMAT, ERROR_NO_FILE, fileExtension, resolveCdnLimits, validateFile, } from "./model/limits.js";
69
+ export { formatCdnRef, parseCdnRef, refOf, toStapelImage } from "./model/refs.js";
70
+ export { canHashLocally, sha256Hex } from "./model/hash.js";
71
+ // ── headless ─────────────────────────────────────────────────────────────────
72
+ export { CdnProvider } from "./headless/CdnProvider.js";
73
+ export { ImageUpload } from "./headless/ImageUpload.js";
74
+ export { MediaUploader } from "./headless/MediaUploader.js";
75
+ export { useUploadImage } from "./headless/useUploadImage.js";
76
+ export { useUploadQueue } from "./headless/useUploadQueue.js";
77
+ export { smallestVariantUrl, useUploadPreview } from "./headless/useUploadPreview.js";
78
+ // ── flows (zero-flow shim — stapel-cdn annotates none) ───────────────────────
79
+ export { CDN_FLOWS, flowEndpoints } from "./flows/registry.js";
80
+ // ── i18n ─────────────────────────────────────────────────────────────────────
81
+ export { CDN_I18N_KEYS, cdnI18nBundleEn, registerCdnI18n } from "./i18n/keys.js";
82
+ export { CDN_ERROR_CODES, CDN_ERRORS, cdnErrorBundleEn, explainCdnError, } from "./i18n/errorsMap.js";
83
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0DG;AAEH,gFAAgF;AAChF,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAiB/C,gFAAgF;AAChF,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAEtD,OAAO,EACL,iBAAiB,EACjB,eAAe,EACf,SAAS,EACT,aAAa,GACd,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AACpD,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAE/C,OAAO,EACL,gBAAgB,EAChB,SAAS,EACT,eAAe,EACf,cAAc,GACf,MAAM,mBAAmB,CAAC;AAS3B,OAAO,EACL,eAAe,EACf,kBAAkB,EAClB,oBAAoB,EACpB,oBAAoB,EACpB,aAAa,EACb,aAAa,EACb,gBAAgB,EAChB,YAAY,GACb,MAAM,mBAAmB,CAAC;AAM3B,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,KAAK,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAClF,OAAO,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAE5D,gFAAgF;AAChF,OAAO,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAC;AACxD,OAAO,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAC;AACxD,OAAO,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAC;AAC5D,OAAO,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAE9D,OAAO,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAM9D,OAAO,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,gCAAgC,CAAC;AAGtF,gFAAgF;AAChF,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAG/D,gFAAgF;AAChF,OAAO,EAAE,aAAa,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AACjF,OAAO,EACL,eAAe,EACf,UAAU,EACV,gBAAgB,EAChB,eAAe,GAChB,MAAM,qBAAqB,CAAC"}
@@ -0,0 +1,11 @@
1
+ import type { Context } from "react";
2
+ import type { Analytics, ModuleContextKit } from "@stapel/core";
3
+ import type { CdnApi } from "../api/cdnApi.js";
4
+ import type { CdnRuntime } from "./runtime.js";
5
+ export declare const CdnRuntimeContext: Context<CdnRuntime | null>;
6
+ export declare const useCdnRuntime: () => CdnRuntime;
7
+ export declare const useCdnApi: () => CdnApi;
8
+ export declare const useCdnAnalytics: () => Analytics | null;
9
+ /** @internal Re-exported as `<CdnProvider>` from `headless/`. */
10
+ export declare const ModuleProvider: ModuleContextKit<CdnRuntime>["Provider"];
11
+ //# sourceMappingURL=context.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../src/model/context.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,OAAO,CAAC;AAErC,OAAO,KAAK,EAAE,SAAS,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAChE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAC/C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAU/C,eAAO,MAAM,iBAAiB,EAAE,OAAO,CAAC,UAAU,GAAG,IAAI,CAAsB,CAAC;AAEhF,eAAO,MAAM,aAAa,EAAE,MAAM,UAA2B,CAAC;AAE9D,eAAO,MAAM,SAAS,EAAE,MAAM,MAAmB,CAAC;AAElD,eAAO,MAAM,eAAe,EAAE,MAAM,SAAS,GAAG,IAAuB,CAAC;AAExE,iEAAiE;AACjE,eAAO,MAAM,cAAc,EAAE,gBAAgB,CAAC,UAAU,CAAC,CAAC,UAAU,CACtD,CAAC"}
@@ -0,0 +1,15 @@
1
+ import { createModuleContext } from "@stapel/core";
2
+ /**
3
+ * The wired {@link CdnRuntime} shared through React context by
4
+ * `<CdnProvider>`. Hooks in `model/` and `headless/` read the singletons from
5
+ * here. One reviewed copy of this plumbing lives in `@stapel/core`
6
+ * (`createModuleContext`); this module binds it under the pair's public names.
7
+ */
8
+ const kit = createModuleContext("Cdn");
9
+ export const CdnRuntimeContext = kit.RuntimeContext;
10
+ export const useCdnRuntime = kit.useRuntime;
11
+ export const useCdnApi = kit.useApi;
12
+ export const useCdnAnalytics = kit.useAnalytics;
13
+ /** @internal Re-exported as `<CdnProvider>` from `headless/`. */
14
+ export const ModuleProvider = kit.Provider;
15
+ //# sourceMappingURL=context.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context.js","sourceRoot":"","sources":["../../src/model/context.tsx"],"names":[],"mappings":"AACA,OAAO,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AAKnD;;;;;GAKG;AACH,MAAM,GAAG,GAAiC,mBAAmB,CAAa,KAAK,CAAC,CAAC;AAEjF,MAAM,CAAC,MAAM,iBAAiB,GAA+B,GAAG,CAAC,cAAc,CAAC;AAEhF,MAAM,CAAC,MAAM,aAAa,GAAqB,GAAG,CAAC,UAAU,CAAC;AAE9D,MAAM,CAAC,MAAM,SAAS,GAAiB,GAAG,CAAC,MAAM,CAAC;AAElD,MAAM,CAAC,MAAM,eAAe,GAA2B,GAAG,CAAC,YAAY,CAAC;AAExE,iEAAiE;AACjE,MAAM,CAAC,MAAM,cAAc,GACzB,GAAG,CAAC,QAAQ,CAAC"}
@@ -0,0 +1,36 @@
1
+ /**
2
+ * SHA-256 of a file's bytes — the key the dedup pre-check is asked with.
3
+ *
4
+ * The backend computes the same digest over the same bytes
5
+ * (`Image.calculate_file_hash`, `hashlib.sha256(...).hexdigest()`), so the two
6
+ * agree by construction: 64 lowercase hex characters of the ORIGINAL upload,
7
+ * before any variant exists.
8
+ *
9
+ * ── Why this can be unavailable, and why that is not an error ──────────────
10
+ *
11
+ * `crypto.subtle` exists only in a SECURE CONTEXT. On `http://` (a LAN test
12
+ * box, an old staging host) it is simply not there, and neither is the
13
+ * pre-check. That is a lost optimisation, not a lost upload: the server
14
+ * deduplicates on its own side regardless, so the POST is still correct — it
15
+ * just costs the bytes. {@link canHashLocally} lets the flow say which of the
16
+ * two paths it took instead of failing, and `UploadItem.dedupSkipped` carries
17
+ * the reason all the way to a skin that wants to explain it.
18
+ *
19
+ * ── Why there is no progress here ──────────────────────────────────────────
20
+ *
21
+ * `SubtleCrypto.digest` takes the whole buffer and returns one promise; it
22
+ * reports nothing in between and cannot be chunked without hand-rolling
23
+ * SHA-256, which is not a thing to hand-roll. Hashing is therefore a PHASE
24
+ * with a duration, not a percentage — see `model/upload.ts` on why the whole
25
+ * bag is phase-shaped.
26
+ */
27
+ /** Whether this context can compute the digest at all (see the header). */
28
+ export declare function canHashLocally(): boolean;
29
+ /**
30
+ * The 64-character lowercase hex SHA-256 of `blob`'s bytes.
31
+ *
32
+ * Throws whatever the platform throws when there is no `crypto.subtle` — call
33
+ * {@link canHashLocally} first; the upload flow does.
34
+ */
35
+ export declare function sha256Hex(blob: Blob): Promise<string>;
36
+ //# sourceMappingURL=hash.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hash.d.ts","sourceRoot":"","sources":["../../src/model/hash.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,2EAA2E;AAC3E,wBAAgB,cAAc,IAAI,OAAO,CAKxC;AAoBD;;;;;GAKG;AACH,wBAAsB,SAAS,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC,CAO3D"}