@stapel/reviews-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 +151 -0
  3. package/README.md +207 -0
  4. package/dist/api/generated/schema.d.ts +360 -0
  5. package/dist/api/generated/schema.d.ts.map +1 -0
  6. package/dist/api/generated/schema.js +2 -0
  7. package/dist/api/generated/schema.js.map +1 -0
  8. package/dist/api/reviewsApi.d.ts +89 -0
  9. package/dist/api/reviewsApi.d.ts.map +1 -0
  10. package/dist/api/reviewsApi.js +51 -0
  11. package/dist/api/reviewsApi.js.map +1 -0
  12. package/dist/api/types.d.ts +127 -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 +9 -0
  17. package/dist/default/ErrorAlert.d.ts.map +1 -0
  18. package/dist/default/ErrorAlert.js +27 -0
  19. package/dist/default/ErrorAlert.js.map +1 -0
  20. package/dist/default/RatingBadge.d.ts +17 -0
  21. package/dist/default/RatingBadge.d.ts.map +1 -0
  22. package/dist/default/RatingBadge.js +23 -0
  23. package/dist/default/RatingBadge.js.map +1 -0
  24. package/dist/default/ReviewFormCard.d.ts +25 -0
  25. package/dist/default/ReviewFormCard.d.ts.map +1 -0
  26. package/dist/default/ReviewFormCard.js +42 -0
  27. package/dist/default/ReviewFormCard.js.map +1 -0
  28. package/dist/default/ReviewListPanel.d.ts +29 -0
  29. package/dist/default/ReviewListPanel.d.ts.map +1 -0
  30. package/dist/default/ReviewListPanel.js +47 -0
  31. package/dist/default/ReviewListPanel.js.map +1 -0
  32. package/dist/default/ReviewsPanel.d.ts +33 -0
  33. package/dist/default/ReviewsPanel.d.ts.map +1 -0
  34. package/dist/default/ReviewsPanel.js +17 -0
  35. package/dist/default/ReviewsPanel.js.map +1 -0
  36. package/dist/default/index.d.ts +31 -0
  37. package/dist/default/index.d.ts.map +1 -0
  38. package/dist/default/index.js +25 -0
  39. package/dist/default/index.js.map +1 -0
  40. package/dist/default/theme.d.ts +24 -0
  41. package/dist/default/theme.d.ts.map +1 -0
  42. package/dist/default/theme.js +39 -0
  43. package/dist/default/theme.js.map +1 -0
  44. package/dist/default/types.d.ts +12 -0
  45. package/dist/default/types.d.ts.map +1 -0
  46. package/dist/default/types.js +2 -0
  47. package/dist/default/types.js.map +1 -0
  48. package/dist/headless/ReviewAggregate.d.ts +63 -0
  49. package/dist/headless/ReviewAggregate.d.ts.map +1 -0
  50. package/dist/headless/ReviewAggregate.js +55 -0
  51. package/dist/headless/ReviewAggregate.js.map +1 -0
  52. package/dist/headless/ReviewForm.d.ts +83 -0
  53. package/dist/headless/ReviewForm.d.ts.map +1 -0
  54. package/dist/headless/ReviewForm.js +75 -0
  55. package/dist/headless/ReviewForm.js.map +1 -0
  56. package/dist/headless/ReviewList.d.ts +47 -0
  57. package/dist/headless/ReviewList.d.ts.map +1 -0
  58. package/dist/headless/ReviewList.js +51 -0
  59. package/dist/headless/ReviewList.js.map +1 -0
  60. package/dist/headless/ReviewsProvider.d.ts +19 -0
  61. package/dist/headless/ReviewsProvider.d.ts.map +1 -0
  62. package/dist/headless/ReviewsProvider.js +15 -0
  63. package/dist/headless/ReviewsProvider.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 +19 -0
  69. package/dist/i18n/es.d.ts.map +1 -0
  70. package/dist/i18n/es.js +66 -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 +341 -0
  77. package/dist/i18n/generated/errors.gen.d.ts.map +1 -0
  78. package/dist/i18n/generated/errors.gen.js +174 -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 +59 -0
  85. package/dist/i18n/keys.d.ts.map +1 -0
  86. package/dist/i18n/keys.js +115 -0
  87. package/dist/i18n/keys.js.map +1 -0
  88. package/dist/i18n/ru.d.ts +22 -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 +98 -0
  93. package/dist/index.d.ts.map +1 -0
  94. package/dist/index.js +92 -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 +16 -0
  99. package/dist/model/context.js.map +1 -0
  100. package/dist/model/list.d.ts +52 -0
  101. package/dist/model/list.d.ts.map +1 -0
  102. package/dist/model/list.js +60 -0
  103. package/dist/model/list.js.map +1 -0
  104. package/dist/model/mutations.d.ts +32 -0
  105. package/dist/model/mutations.d.ts.map +1 -0
  106. package/dist/model/mutations.js +44 -0
  107. package/dist/model/mutations.js.map +1 -0
  108. package/dist/model/queries.d.ts +65 -0
  109. package/dist/model/queries.d.ts.map +1 -0
  110. package/dist/model/queries.js +80 -0
  111. package/dist/model/queries.js.map +1 -0
  112. package/dist/model/queryKeys.d.ts +27 -0
  113. package/dist/model/queryKeys.d.ts.map +1 -0
  114. package/dist/model/queryKeys.js +14 -0
  115. package/dist/model/queryKeys.js.map +1 -0
  116. package/dist/model/rating.d.ts +57 -0
  117. package/dist/model/rating.d.ts.map +1 -0
  118. package/dist/model/rating.js +47 -0
  119. package/dist/model/rating.js.map +1 -0
  120. package/dist/model/refusals.d.ts +51 -0
  121. package/dist/model/refusals.d.ts.map +1 -0
  122. package/dist/model/refusals.js +103 -0
  123. package/dist/model/refusals.js.map +1 -0
  124. package/dist/model/runtime.d.ts +44 -0
  125. package/dist/model/runtime.d.ts.map +1 -0
  126. package/dist/model/runtime.js +15 -0
  127. package/dist/model/runtime.js.map +1 -0
  128. package/llms.txt +80 -0
  129. package/manifest.json +577 -0
  130. package/package.json +115 -0
  131. package/src/analytics/generated/events.json +7 -0
  132. package/src/api/generated/schema.ts +362 -0
  133. package/src/api/reviewsApi.ts +170 -0
  134. package/src/api/types.ts +140 -0
  135. package/src/default/ErrorAlert.tsx +46 -0
  136. package/src/default/RatingBadge.tsx +81 -0
  137. package/src/default/ReviewFormCard.tsx +139 -0
  138. package/src/default/ReviewListPanel.tsx +204 -0
  139. package/src/default/ReviewsPanel.tsx +74 -0
  140. package/src/default/index.ts +30 -0
  141. package/src/default/theme.tsx +66 -0
  142. package/src/default/types.ts +12 -0
  143. package/src/headless/ReviewAggregate.tsx +94 -0
  144. package/src/headless/ReviewForm.tsx +163 -0
  145. package/src/headless/ReviewList.tsx +99 -0
  146. package/src/headless/ReviewsProvider.tsx +20 -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 +205 -0
  151. package/src/i18n/generated/errors.json +387 -0
  152. package/src/i18n/generated/errors.ru.gen.ts +64 -0
  153. package/src/i18n/keys.ts +133 -0
  154. package/src/i18n/ru.ts +85 -0
  155. package/src/index.ts +161 -0
  156. package/src/model/context.tsx +28 -0
  157. package/src/model/list.ts +74 -0
  158. package/src/model/mutations.ts +62 -0
  159. package/src/model/queries.ts +122 -0
  160. package/src/model/queryKeys.ts +45 -0
  161. package/src/model/rating.ts +101 -0
  162. package/src/model/refusals.ts +114 -0
  163. package/src/model/runtime.ts +61 -0
  164. package/tsconfig.json +26 -0
@@ -0,0 +1,59 @@
1
+ import type { I18nDictionary, I18nEngine } from "@stapel/core";
2
+ /**
3
+ * reviews-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 REVIEWS_I18N_KEYS: {
10
+ readonly unknownError: "reviews.error.unknown";
11
+ readonly listHeading: "reviews.list.heading";
12
+ readonly listEmpty: "reviews.list.empty";
13
+ readonly listLoadMore: "reviews.list.load_more";
14
+ readonly listRefresh: "reviews.list.refresh";
15
+ readonly moreBlockedEnd: "reviews.list.more.blocked.end";
16
+ readonly moreBlockedPending: "reviews.list.more.blocked.pending";
17
+ readonly authorFallback: "reviews.review.author_fallback";
18
+ readonly responseHeading: "reviews.review.response_heading";
19
+ readonly statusPending: "reviews.status.pending";
20
+ readonly statusHidden: "reviews.status.hidden";
21
+ readonly statusUnknown: "reviews.status.unknown";
22
+ readonly ratingNone: "reviews.rating.none";
23
+ readonly ratingValue: "reviews.rating.value";
24
+ readonly ratingCount: "reviews.rating.count";
25
+ readonly ratingStarLabel: "reviews.rating.star_label";
26
+ readonly formHeading: "reviews.form.heading";
27
+ readonly formRatingLabel: "reviews.form.rating_label";
28
+ readonly formBodyLabel: "reviews.form.body_label";
29
+ readonly formBodyPlaceholder: "reviews.form.body_placeholder";
30
+ readonly formSubmit: "reviews.form.submit";
31
+ readonly formSentPublished: "reviews.form.sent.published";
32
+ readonly formSentPending: "reviews.form.sent.pending";
33
+ readonly formSentHidden: "reviews.form.sent.hidden";
34
+ readonly formSentUnknown: "reviews.form.sent.unknown";
35
+ readonly formSignInRequired: "reviews.form.sign_in_required";
36
+ readonly submitBlockedNoRating: "reviews.submit.blocked.no_rating";
37
+ readonly submitBlockedPending: "reviews.submit.blocked.pending";
38
+ readonly submitBlockedDuplicate: "reviews.submit.blocked.duplicate";
39
+ readonly submitBlockedSubmitted: "reviews.submit.blocked.submitted";
40
+ readonly submitBlockedForbidden: "reviews.submit.blocked.forbidden";
41
+ readonly errorDuplicateReview: "error.400.reviews_duplicate_review";
42
+ readonly errorInvalidModerationAction: "error.400.reviews_invalid_moderation_action";
43
+ readonly errorInvalidRating: "error.400.reviews_invalid_rating";
44
+ readonly errorResponseNotAllowed: "error.400.reviews_response_not_allowed";
45
+ readonly errorUnknownTargetType: "error.400.reviews_unknown_target_type";
46
+ readonly errorCannotModerate: "error.403.reviews_cannot_moderate";
47
+ readonly errorCannotReview: "error.403.reviews_cannot_review";
48
+ readonly errorReviewNotFound: "error.404.reviews_review_not_found";
49
+ readonly errorAlreadyResponded: "error.409.reviews_already_responded";
50
+ };
51
+ /**
52
+ * The English bundle: the generated backend fallbacks first, this pair's own
53
+ * UI copy over them. English is INLINE (not a separate subpath) so a host that
54
+ * registers nothing still renders sentences instead of raw keys.
55
+ */
56
+ export declare const reviewsI18nBundleEn: I18nDictionary;
57
+ /** Register the English bundle into a core i18n engine. */
58
+ export declare function registerReviewsI18n(engine: I18nEngine, locale?: string): void;
59
+ //# 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,iBAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAqEpB,CAAC;AAEX;;;;GAIG;AACH,eAAO,MAAM,mBAAmB,EAAE,cAyCjC,CAAC;AAEF,2DAA2D;AAC3D,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,UAAU,EAAE,MAAM,SAAO,GAAG,IAAI,CAE3E"}
@@ -0,0 +1,115 @@
1
+ import { reviewsErrorBundleEn } from "./generated/errors.gen.js";
2
+ /**
3
+ * reviews-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 REVIEWS_I18N_KEYS = {
10
+ unknownError: "reviews.error.unknown",
11
+ // The list
12
+ listHeading: "reviews.list.heading",
13
+ listEmpty: "reviews.list.empty",
14
+ listLoadMore: "reviews.list.load_more",
15
+ listRefresh: "reviews.list.refresh",
16
+ moreBlockedEnd: "reviews.list.more.blocked.end",
17
+ moreBlockedPending: "reviews.list.more.blocked.pending",
18
+ // One row. The wire carries `author_id` and NOTHING else about the person —
19
+ // no name, no avatar — so the skin renders this unless the host passes a
20
+ // `renderAuthor` slot. Printing the raw id would be both useless and a
21
+ // gratuitous disclosure.
22
+ authorFallback: "reviews.review.author_fallback",
23
+ responseHeading: "reviews.review.response_heading",
24
+ // Visibility, said out loud. A row that is not published is only ever on
25
+ // screen because a moderator asked for `include=all`, and it must not look
26
+ // like an ordinary review.
27
+ statusPending: "reviews.status.pending",
28
+ statusHidden: "reviews.status.hidden",
29
+ statusUnknown: "reviews.status.unknown",
30
+ // The rating
31
+ ratingNone: "reviews.rating.none",
32
+ ratingValue: "reviews.rating.value",
33
+ ratingCount: "reviews.rating.count",
34
+ ratingStarLabel: "reviews.rating.star_label",
35
+ // The form. `formSignInRequired` is the ONLY sign-in key left: since
36
+ // stapel-reviews 0.3.0 both reads are anonymous and only the POST refuses a
37
+ // guest, so "sign in" is copy for the write and never a wall in front of
38
+ // the content.
39
+ formHeading: "reviews.form.heading",
40
+ formRatingLabel: "reviews.form.rating_label",
41
+ formBodyLabel: "reviews.form.body_label",
42
+ formBodyPlaceholder: "reviews.form.body_placeholder",
43
+ formSubmit: "reviews.form.submit",
44
+ formSentPublished: "reviews.form.sent.published",
45
+ formSentPending: "reviews.form.sent.pending",
46
+ formSentHidden: "reviews.form.sent.hidden",
47
+ formSentUnknown: "reviews.form.sent.unknown",
48
+ formSignInRequired: "reviews.form.sign_in_required",
49
+ // Blocked controls — every one of these is the `code` of an
50
+ // ActionAvailability, so a switched-off button always has a sentence.
51
+ submitBlockedNoRating: "reviews.submit.blocked.no_rating",
52
+ submitBlockedPending: "reviews.submit.blocked.pending",
53
+ submitBlockedDuplicate: "reviews.submit.blocked.duplicate",
54
+ submitBlockedSubmitted: "reviews.submit.blocked.submitted",
55
+ submitBlockedForbidden: "reviews.submit.blocked.forbidden",
56
+ // Backend error keys the pair OWNS the localization of. stapel-reviews ships
57
+ // English only (no `translations/` directory at all), so its 9 keys are
58
+ // absent from the generated ru/es bundles and are authored in `./i18n/<lang>`
59
+ // instead — the stapel-forms/`stapel_attributes` precedent, applied before
60
+ // this by forms, chat, cdn and categories. Listed here so `i18n-key-exists`
61
+ // knows them and `test/i18n.test.ts` can prove all three locales carry them.
62
+ errorDuplicateReview: "error.400.reviews_duplicate_review",
63
+ errorInvalidModerationAction: "error.400.reviews_invalid_moderation_action",
64
+ errorInvalidRating: "error.400.reviews_invalid_rating",
65
+ errorResponseNotAllowed: "error.400.reviews_response_not_allowed",
66
+ errorUnknownTargetType: "error.400.reviews_unknown_target_type",
67
+ errorCannotModerate: "error.403.reviews_cannot_moderate",
68
+ errorCannotReview: "error.403.reviews_cannot_review",
69
+ errorReviewNotFound: "error.404.reviews_review_not_found",
70
+ errorAlreadyResponded: "error.409.reviews_already_responded",
71
+ };
72
+ /**
73
+ * The English bundle: the generated backend fallbacks first, this pair's own
74
+ * UI copy over them. English is INLINE (not a separate subpath) so a host that
75
+ * registers nothing still renders sentences instead of raw keys.
76
+ */
77
+ export const reviewsI18nBundleEn = {
78
+ ...reviewsErrorBundleEn,
79
+ "reviews.error.unknown": "Something went wrong with the reviews",
80
+ "reviews.list.heading": "Reviews",
81
+ "reviews.list.empty": "No reviews yet",
82
+ "reviews.list.load_more": "Show more",
83
+ "reviews.list.refresh": "Refresh",
84
+ "reviews.list.more.blocked.end": "That is all of them",
85
+ "reviews.list.more.blocked.pending": "Loading…",
86
+ "reviews.review.author_fallback": "A customer",
87
+ "reviews.review.response_heading": "Reply from the seller",
88
+ "reviews.status.pending": "Awaiting moderation",
89
+ "reviews.status.hidden": "Hidden by moderation",
90
+ "reviews.status.unknown": "Unknown state: {status}",
91
+ "reviews.rating.none": "No reviews yet",
92
+ "reviews.rating.value": "{avg} out of {max}",
93
+ "reviews.rating.count": "{count} reviews",
94
+ "reviews.rating.star_label": "{index} out of {max}",
95
+ "reviews.form.heading": "Rate this",
96
+ "reviews.form.rating_label": "Your rating",
97
+ "reviews.form.body_label": "Your review",
98
+ "reviews.form.body_placeholder": "What was the deal like? (optional)",
99
+ "reviews.form.submit": "Send",
100
+ "reviews.form.sent.published": "Thank you — your review is published",
101
+ "reviews.form.sent.pending": "Thank you — your review will appear once it has been checked",
102
+ "reviews.form.sent.hidden": "Your review was saved but is not shown",
103
+ "reviews.form.sent.unknown": "Your review was saved",
104
+ "reviews.form.sign_in_required": "Sign in to leave a review",
105
+ "reviews.submit.blocked.no_rating": "Choose a rating first",
106
+ "reviews.submit.blocked.pending": "Sending…",
107
+ "reviews.submit.blocked.duplicate": "You have already rated this",
108
+ "reviews.submit.blocked.submitted": "Your review has been sent",
109
+ "reviews.submit.blocked.forbidden": "You cannot review this",
110
+ };
111
+ /** Register the English bundle into a core i18n engine. */
112
+ export function registerReviewsI18n(engine, locale = "en") {
113
+ engine.registerBundle(locale, reviewsI18nBundleEn);
114
+ }
115
+ //# sourceMappingURL=keys.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"keys.js","sourceRoot":"","sources":["../../src/i18n/keys.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAC;AAEjE;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG;IAC/B,YAAY,EAAE,uBAAuB;IAErC,WAAW;IACX,WAAW,EAAE,sBAAsB;IACnC,SAAS,EAAE,oBAAoB;IAC/B,YAAY,EAAE,wBAAwB;IACtC,WAAW,EAAE,sBAAsB;IACnC,cAAc,EAAE,+BAA+B;IAC/C,kBAAkB,EAAE,mCAAmC;IAEvD,4EAA4E;IAC5E,yEAAyE;IACzE,uEAAuE;IACvE,yBAAyB;IACzB,cAAc,EAAE,gCAAgC;IAChD,eAAe,EAAE,iCAAiC;IAElD,yEAAyE;IACzE,2EAA2E;IAC3E,2BAA2B;IAC3B,aAAa,EAAE,wBAAwB;IACvC,YAAY,EAAE,uBAAuB;IACrC,aAAa,EAAE,wBAAwB;IAEvC,aAAa;IACb,UAAU,EAAE,qBAAqB;IACjC,WAAW,EAAE,sBAAsB;IACnC,WAAW,EAAE,sBAAsB;IACnC,eAAe,EAAE,2BAA2B;IAE5C,qEAAqE;IACrE,4EAA4E;IAC5E,yEAAyE;IACzE,eAAe;IACf,WAAW,EAAE,sBAAsB;IACnC,eAAe,EAAE,2BAA2B;IAC5C,aAAa,EAAE,yBAAyB;IACxC,mBAAmB,EAAE,+BAA+B;IACpD,UAAU,EAAE,qBAAqB;IACjC,iBAAiB,EAAE,6BAA6B;IAChD,eAAe,EAAE,2BAA2B;IAC5C,cAAc,EAAE,0BAA0B;IAC1C,eAAe,EAAE,2BAA2B;IAC5C,kBAAkB,EAAE,+BAA+B;IAEnD,4DAA4D;IAC5D,sEAAsE;IACtE,qBAAqB,EAAE,kCAAkC;IACzD,oBAAoB,EAAE,gCAAgC;IACtD,sBAAsB,EAAE,kCAAkC;IAC1D,sBAAsB,EAAE,kCAAkC;IAC1D,sBAAsB,EAAE,kCAAkC;IAE1D,6EAA6E;IAC7E,wEAAwE;IACxE,8EAA8E;IAC9E,2EAA2E;IAC3E,4EAA4E;IAC5E,6EAA6E;IAC7E,oBAAoB,EAAE,oCAAoC;IAC1D,4BAA4B,EAAE,6CAA6C;IAC3E,kBAAkB,EAAE,kCAAkC;IACtD,uBAAuB,EAAE,wCAAwC;IACjE,sBAAsB,EAAE,uCAAuC;IAC/D,mBAAmB,EAAE,mCAAmC;IACxD,iBAAiB,EAAE,iCAAiC;IACpD,mBAAmB,EAAE,oCAAoC;IACzD,qBAAqB,EAAE,qCAAqC;CACpD,CAAC;AAEX;;;;GAIG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAmB;IACjD,GAAG,oBAAoB;IAEvB,uBAAuB,EAAE,uCAAuC;IAEhE,sBAAsB,EAAE,SAAS;IACjC,oBAAoB,EAAE,gBAAgB;IACtC,wBAAwB,EAAE,WAAW;IACrC,sBAAsB,EAAE,SAAS;IACjC,+BAA+B,EAAE,qBAAqB;IACtD,mCAAmC,EAAE,UAAU;IAE/C,gCAAgC,EAAE,YAAY;IAC9C,iCAAiC,EAAE,uBAAuB;IAE1D,wBAAwB,EAAE,qBAAqB;IAC/C,uBAAuB,EAAE,sBAAsB;IAC/C,wBAAwB,EAAE,yBAAyB;IAEnD,qBAAqB,EAAE,gBAAgB;IACvC,sBAAsB,EAAE,oBAAoB;IAC5C,sBAAsB,EAAE,iBAAiB;IACzC,2BAA2B,EAAE,sBAAsB;IAEnD,sBAAsB,EAAE,WAAW;IACnC,2BAA2B,EAAE,aAAa;IAC1C,yBAAyB,EAAE,aAAa;IACxC,+BAA+B,EAAE,oCAAoC;IACrE,qBAAqB,EAAE,MAAM;IAC7B,6BAA6B,EAAE,sCAAsC;IACrE,2BAA2B,EACzB,8DAA8D;IAChE,0BAA0B,EAAE,wCAAwC;IACpE,2BAA2B,EAAE,uBAAuB;IACpD,+BAA+B,EAAE,2BAA2B;IAE5D,kCAAkC,EAAE,uBAAuB;IAC3D,gCAAgC,EAAE,UAAU;IAC5C,kCAAkC,EAAE,6BAA6B;IACjE,kCAAkC,EAAE,2BAA2B;IAC/D,kCAAkC,EAAE,wBAAwB;CAC7D,CAAC;AAEF,2DAA2D;AAC3D,MAAM,UAAU,mBAAmB,CAAC,MAAkB,EAAE,MAAM,GAAG,IAAI;IACnE,MAAM,CAAC,cAAc,CAAC,MAAM,EAAE,mBAAmB,CAAC,CAAC;AACrD,CAAC"}
@@ -0,0 +1,22 @@
1
+ import type { I18nDictionary, I18nEngine } from "@stapel/core";
2
+ export { reviewsErrorBundleRu } from "./generated/errors.ru.gen.js";
3
+ /**
4
+ * Russian bundle for reviews-react — shipped as the
5
+ * `@stapel/reviews-react/i18n/ru` subpath (i18n-shipping.md §2) so the locale
6
+ * is opt-in: hosts that don't register it never carry these strings (the main
7
+ * entry does not import this module — gated by size-limit and the
8
+ * bundle-purity test).
9
+ *
10
+ * TWO SOURCES, ON PURPOSE. The generated `reviewsErrorBundleRu` covers the 42
11
+ * cross-cutting keys stapel-core owns and localizes. The 9 keys stapel-reviews
12
+ * owns are NOT in it, and cannot be: the module ships no `translations/`
13
+ * directory at all, so the generator emits a `Partial` bundle and says so in
14
+ * its own header (`ERRORS_LOCALE_EXEMPT_OWNERS`, the stapel-forms precedent).
15
+ * They are authored below, beside the UI copy. When upstream ships
16
+ * `translations/errors.ru.json`, these nine lines are deleted and the
17
+ * generated bundle covers them — the keys and the texts do not move.
18
+ */
19
+ export declare const reviewsI18nBundleRu: I18nDictionary;
20
+ /** Register the Russian bundle into a core i18n engine. */
21
+ export declare function registerReviewsI18nRu(engine: I18nEngine, locale?: string): void;
22
+ //# 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,oBAAoB,EAAE,MAAM,8BAA8B,CAAC;AAEpE;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,mBAAmB,EAAE,cA0DjC,CAAC;AAEF,2DAA2D;AAC3D,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,UAAU,EAAE,MAAM,SAAO,GAAG,IAAI,CAE7E"}
@@ -0,0 +1,70 @@
1
+ import { reviewsErrorBundleRu } from "./generated/errors.ru.gen.js";
2
+ export { reviewsErrorBundleRu } from "./generated/errors.ru.gen.js";
3
+ /**
4
+ * Russian bundle for reviews-react — shipped as the
5
+ * `@stapel/reviews-react/i18n/ru` subpath (i18n-shipping.md §2) so the locale
6
+ * is opt-in: hosts that don't register it never carry these strings (the main
7
+ * entry does not import this module — gated by size-limit and the
8
+ * bundle-purity test).
9
+ *
10
+ * TWO SOURCES, ON PURPOSE. The generated `reviewsErrorBundleRu` covers the 42
11
+ * cross-cutting keys stapel-core owns and localizes. The 9 keys stapel-reviews
12
+ * owns are NOT in it, and cannot be: the module ships no `translations/`
13
+ * directory at all, so the generator emits a `Partial` bundle and says so in
14
+ * its own header (`ERRORS_LOCALE_EXEMPT_OWNERS`, the stapel-forms precedent).
15
+ * They are authored below, beside the UI copy. When upstream ships
16
+ * `translations/errors.ru.json`, these nine lines are deleted and the
17
+ * generated bundle covers them — the keys and the texts do not move.
18
+ */
19
+ export const reviewsI18nBundleRu = {
20
+ // Backend error codes — generated ru texts for every key core owns.
21
+ ...reviewsErrorBundleRu,
22
+ // Backend error codes stapel-reviews owns — authored here (see the note
23
+ // above).
24
+ "error.400.reviews_duplicate_review": "Вы уже оценили это",
25
+ "error.400.reviews_invalid_moderation_action": "Действие модерации должно быть одним из: скрыть, опубликовать",
26
+ "error.400.reviews_invalid_rating": "Оценка вне допустимого диапазона",
27
+ "error.400.reviews_response_not_allowed": "Для этого типа объекта ответы отключены",
28
+ "error.400.reviews_unknown_target_type": "Неизвестный тип объекта отзыва",
29
+ "error.403.reviews_cannot_moderate": "Вы не можете модерировать отзывы об этом объекте",
30
+ "error.403.reviews_cannot_review": "Вы не можете оставить отзыв об этом объекте",
31
+ "error.404.reviews_review_not_found": "Отзыв не найден",
32
+ "error.409.reviews_already_responded": "На этот отзыв уже есть ответ",
33
+ // UI copy.
34
+ "reviews.error.unknown": "С отзывами что-то пошло не так",
35
+ "reviews.list.heading": "Отзывы",
36
+ "reviews.list.empty": "Отзывов пока нет",
37
+ "reviews.list.load_more": "Показать ещё",
38
+ "reviews.list.refresh": "Обновить",
39
+ "reviews.list.more.blocked.end": "Это все отзывы",
40
+ "reviews.list.more.blocked.pending": "Загружаем…",
41
+ "reviews.review.author_fallback": "Покупатель",
42
+ "reviews.review.response_heading": "Ответ продавца",
43
+ "reviews.status.pending": "Ожидает модерации",
44
+ "reviews.status.hidden": "Скрыт модерацией",
45
+ "reviews.status.unknown": "Неизвестное состояние: {status}",
46
+ "reviews.rating.none": "Отзывов пока нет",
47
+ "reviews.rating.value": "{avg} из {max}",
48
+ "reviews.rating.count": "Отзывов: {count}",
49
+ "reviews.rating.star_label": "{index} из {max}",
50
+ "reviews.form.heading": "Оцените",
51
+ "reviews.form.rating_label": "Ваша оценка",
52
+ "reviews.form.body_label": "Ваш отзыв",
53
+ "reviews.form.body_placeholder": "Как прошла сделка? (необязательно)",
54
+ "reviews.form.submit": "Отправить",
55
+ "reviews.form.sent.published": "Спасибо — ваш отзыв опубликован",
56
+ "reviews.form.sent.pending": "Спасибо — отзыв появится после проверки модератором",
57
+ "reviews.form.sent.hidden": "Отзыв сохранён, но не показывается",
58
+ "reviews.form.sent.unknown": "Отзыв сохранён",
59
+ "reviews.form.sign_in_required": "Войдите, чтобы оставить отзыв",
60
+ "reviews.submit.blocked.no_rating": "Сначала выберите оценку",
61
+ "reviews.submit.blocked.pending": "Отправляем…",
62
+ "reviews.submit.blocked.duplicate": "Вы уже оценили это",
63
+ "reviews.submit.blocked.submitted": "Ваш отзыв отправлен",
64
+ "reviews.submit.blocked.forbidden": "Вы не можете оставить здесь отзыв",
65
+ };
66
+ /** Register the Russian bundle into a core i18n engine. */
67
+ export function registerReviewsI18nRu(engine, locale = "ru") {
68
+ engine.registerBundle(locale, reviewsI18nBundleRu);
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,oBAAoB,EAAE,MAAM,8BAA8B,CAAC;AAEpE,OAAO,EAAE,oBAAoB,EAAE,MAAM,8BAA8B,CAAC;AAEpE;;;;;;;;;;;;;;;GAeG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAmB;IACjD,oEAAoE;IACpE,GAAG,oBAAoB;IAEvB,wEAAwE;IACxE,UAAU;IACV,oCAAoC,EAAE,oBAAoB;IAC1D,6CAA6C,EAC3C,+DAA+D;IACjE,kCAAkC,EAAE,kCAAkC;IACtE,wCAAwC,EACtC,yCAAyC;IAC3C,uCAAuC,EAAE,gCAAgC;IACzE,mCAAmC,EACjC,kDAAkD;IACpD,iCAAiC,EAAE,6CAA6C;IAChF,oCAAoC,EAAE,iBAAiB;IACvD,qCAAqC,EAAE,8BAA8B;IAErE,WAAW;IACX,uBAAuB,EAAE,gCAAgC;IAEzD,sBAAsB,EAAE,QAAQ;IAChC,oBAAoB,EAAE,kBAAkB;IACxC,wBAAwB,EAAE,cAAc;IACxC,sBAAsB,EAAE,UAAU;IAClC,+BAA+B,EAAE,gBAAgB;IACjD,mCAAmC,EAAE,YAAY;IAEjD,gCAAgC,EAAE,YAAY;IAC9C,iCAAiC,EAAE,gBAAgB;IAEnD,wBAAwB,EAAE,mBAAmB;IAC7C,uBAAuB,EAAE,kBAAkB;IAC3C,wBAAwB,EAAE,iCAAiC;IAE3D,qBAAqB,EAAE,kBAAkB;IACzC,sBAAsB,EAAE,gBAAgB;IACxC,sBAAsB,EAAE,kBAAkB;IAC1C,2BAA2B,EAAE,kBAAkB;IAE/C,sBAAsB,EAAE,SAAS;IACjC,2BAA2B,EAAE,aAAa;IAC1C,yBAAyB,EAAE,WAAW;IACtC,+BAA+B,EAAE,oCAAoC;IACrE,qBAAqB,EAAE,WAAW;IAClC,6BAA6B,EAAE,iCAAiC;IAChE,2BAA2B,EACzB,qDAAqD;IACvD,0BAA0B,EAAE,oCAAoC;IAChE,2BAA2B,EAAE,gBAAgB;IAC7C,+BAA+B,EAAE,+BAA+B;IAEhE,kCAAkC,EAAE,yBAAyB;IAC7D,gCAAgC,EAAE,aAAa;IAC/C,kCAAkC,EAAE,oBAAoB;IACxD,kCAAkC,EAAE,qBAAqB;IACzD,kCAAkC,EAAE,mCAAmC;CACxE,CAAC;AAEF,2DAA2D;AAC3D,MAAM,UAAU,qBAAqB,CAAC,MAAkB,EAAE,MAAM,GAAG,IAAI;IACrE,MAAM,CAAC,cAAc,CAAC,MAAM,EAAE,mBAAmB,CAAC,CAAC;AACrD,CAAC"}
@@ -0,0 +1,98 @@
1
+ /**
2
+ * `@stapel/reviews-react` — the headless React pair for stapel-reviews
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
+ * review block carries none of it.
6
+ *
7
+ * ── The one-liner ──────────────────────────────────────────────────────────
8
+ *
9
+ * ```tsx
10
+ * const runtime = createReviewsRuntime({ baseUrl: "/reviews/api/v1" });
11
+ * const target = { targetType: "listing", targetKey: listingId };
12
+ * <ReviewsProvider runtime={runtime}>
13
+ * <ReviewAggregate target={target}>{(bag) => <YourStars bag={bag} />}</ReviewAggregate>
14
+ * <ReviewList target={target}>{(bag) => <YourRows bag={bag} />}</ReviewList>
15
+ * <ReviewForm target={target}>{(bag) => <YourForm bag={bag} />}</ReviewForm>
16
+ * </ReviewsProvider>
17
+ * ```
18
+ *
19
+ * `"listing"` in that snippet is the HOST's registry key, not this library's:
20
+ * stapel-reviews ships an EMPTY `TARGET_TYPES` registry and this package
21
+ * exports no target-type constants (see `api/types.ts`, `ReviewTarget`).
22
+ *
23
+ * ── The four properties this pair exists to guarantee ──────────────────────
24
+ *
25
+ * 1. **A zero average is not a zero rating.** `avg` is `0.0` when `count` is
26
+ * `0`, by the module's own contract. `ratingSummary()` answers
27
+ * `rated: false` there and gives a skin nothing to draw a star row from, so
28
+ * "nobody has rated this yet" can never be rendered as the worst possible
29
+ * score. Same class as `data ?? []`.
30
+ * 2. **The refusal is read by CODE, never by status.** "You have already
31
+ * reviewed this" is `error.400.reviews_duplicate_review` — a 400 — while
32
+ * the module's only 409 says the owner's reply already exists. Branching on
33
+ * the number would miss the first and mishandle the second
34
+ * (`model/refusals.ts`).
35
+ * 3. **A 401 is not an empty list.** Every stapel-reviews endpoint is
36
+ * `IsAuthenticated`, including both reads, so a signed-out visitor to a
37
+ * public listing page gets 401 — and the empty state would tell them a
38
+ * well-reviewed seller has never been reviewed. `signInRequired` is a
39
+ * named state on both read bags.
40
+ * 4. **A review that is not published says so.** `status` is carried to the
41
+ * screen (`pending` / `hidden`, plus an explicit `unknown` arm for a state
42
+ * a future backend adds), and the submit outcome reports the created row's
43
+ * status, so a pre-moderating deployment tells the author their review is
44
+ * waiting instead of leaving them to look for it.
45
+ *
46
+ * ── What this pair does NOT do ─────────────────────────────────────────────
47
+ *
48
+ * No moderation and no owner reply. `POST {id}/moderate` and
49
+ * `POST {id}/response` are both gated on the target type's fail-closed
50
+ * `can_moderate` callback and belong to consoles this pair does not ship
51
+ * (storefront spec §4.4: the reply is DISPLAYED, and the button to write one
52
+ * does not exist rather than existing switched off). Both stay in
53
+ * `manifest.json`, which lists the whole contract.
54
+ *
55
+ * No seller-wide rating fetch. The product model (spec fork F5) reviews the
56
+ * SELLER for a specific listing, so a seller's rating is a roll-up across
57
+ * their listings — which stapel-reviews cannot compute (one
58
+ * `(target_type, target_key)` per call) and the shop composite can
59
+ * (`shop.listing_review_summary`). `<ReviewAggregate aggregate={…}>` renders
60
+ * those two numbers from wherever the host got them; it does not invent an
61
+ * N+1 loop to fake the roll-up.
62
+ *
63
+ * No nav manifest: this pair has no route of its own. It renders INSIDE the
64
+ * listing detail page and the public seller profile, both of which belong to
65
+ * other pairs (the cdn-react precedent — a menu entry leading nowhere is
66
+ * worse than no entry).
67
+ *
68
+ * Layers: api → model → headless → i18n. Generated surfaces (the typed schema,
69
+ * the error map, the manifest, llms.txt) are produced by the monorepo `gen:*`
70
+ * drivers from stapel-reviews' own `docs/` artifacts and stand under drift
71
+ * gates.
72
+ */
73
+ export { createReviewsApi } from "./api/reviewsApi.js";
74
+ export type { ReviewsApi } from "./api/reviewsApi.js";
75
+ export type { RatingAggregate, Review, ReviewAggregate as ReviewAggregateResponse, ReviewAnchorDirection, ReviewCreateRequest, ReviewListParams, ReviewOwnerResponse, ReviewPage, ReviewStatus, ReviewTarget, Schemas, } from "./api/types.js";
76
+ export { createReviewsRuntime, DEFAULT_RATING_BOUNDS } from "./model/runtime.js";
77
+ export type { CreateReviewsRuntimeOptions, ReviewRatingBounds, ReviewsRuntime, } from "./model/runtime.js";
78
+ export { ReviewsRuntimeContext, useReviewsAnalytics, useReviewsApi, useReviewsRuntime, } from "./model/context.js";
79
+ export { reviewsQueryKeys } from "./model/queryKeys.js";
80
+ export { REVIEWS_PAGE, useReviewAggregate, useReviewList } from "./model/queries.js";
81
+ export type { UseReviewListOptions } from "./model/queries.js";
82
+ export { useSubmitReview } from "./model/mutations.js";
83
+ export type { SubmitReviewVariables } from "./model/mutations.js";
84
+ export { ratingSummary, starBreakdown } from "./model/rating.js";
85
+ export type { RatingSummary, StarBreakdown } from "./model/rating.js";
86
+ export { findOwnReview, isModeratedOut, reviewsFromPages, reviewVisibility, } from "./model/list.js";
87
+ export { isDuplicateReview, isReviewingForbidden, isSignInRequired, isUnknownTargetType, REVIEWS_ERROR_ALREADY_RESPONDED, REVIEWS_ERROR_CANNOT_MODERATE, REVIEWS_ERROR_CANNOT_REVIEW, REVIEWS_ERROR_DUPLICATE, REVIEWS_ERROR_INVALID_RATING, REVIEWS_ERROR_NOT_FOUND, REVIEWS_ERROR_RESPONSE_NOT_ALLOWED, REVIEWS_ERROR_UNKNOWN_TARGET_TYPE, toReviewsError, } from "./model/refusals.js";
88
+ export { ReviewsProvider } from "./headless/ReviewsProvider.js";
89
+ export { ReviewList } from "./headless/ReviewList.js";
90
+ export type { ReviewListBag, ReviewListProps } from "./headless/ReviewList.js";
91
+ export { ReviewAggregate } from "./headless/ReviewAggregate.js";
92
+ export type { ReviewAggregateBag, ReviewAggregateProps, } from "./headless/ReviewAggregate.js";
93
+ export { ReviewForm } from "./headless/ReviewForm.js";
94
+ export type { ReviewFormBag, ReviewFormProps } from "./headless/ReviewForm.js";
95
+ export { REVIEWS_I18N_KEYS, registerReviewsI18n, reviewsI18nBundleEn, } from "./i18n/keys.js";
96
+ export { explainReviewsError, REVIEWS_ERROR_CODES, REVIEWS_ERRORS, reviewsErrorBundleEn, } from "./i18n/errorsMap.js";
97
+ export type { Remediation, ReviewsErrorCode, ReviewsErrorSpec, } from "./i18n/errorsMap.js";
98
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuEG;AAGH,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AACvD,YAAY,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AACtD,YAAY,EACV,eAAe,EACf,MAAM,EACN,eAAe,IAAI,uBAAuB,EAC1C,qBAAqB,EACrB,mBAAmB,EACnB,gBAAgB,EAChB,mBAAmB,EACnB,UAAU,EACV,YAAY,EACZ,YAAY,EACZ,OAAO,GACR,MAAM,gBAAgB,CAAC;AAGxB,OAAO,EAAE,oBAAoB,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AACjF,YAAY,EACV,2BAA2B,EAC3B,kBAAkB,EAClB,cAAc,GACf,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,qBAAqB,EACrB,mBAAmB,EACnB,aAAa,EACb,iBAAiB,GAClB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AACxD,OAAO,EAAE,YAAY,EAAE,kBAAkB,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACrF,YAAY,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAC/D,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AACvD,YAAY,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAClE,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AACjE,YAAY,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AACtE,OAAO,EACL,aAAa,EACb,cAAc,EACd,gBAAgB,EAChB,gBAAgB,GACjB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACL,iBAAiB,EACjB,oBAAoB,EACpB,gBAAgB,EAChB,mBAAmB,EACnB,+BAA+B,EAC/B,6BAA6B,EAC7B,2BAA2B,EAC3B,uBAAuB,EACvB,4BAA4B,EAC5B,uBAAuB,EACvB,kCAAkC,EAClC,iCAAiC,EACjC,cAAc,GACf,MAAM,qBAAqB,CAAC;AAG7B,OAAO,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAC;AAChE,OAAO,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AACtD,YAAY,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAC/E,OAAO,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAC;AAChE,YAAY,EACV,kBAAkB,EAClB,oBAAoB,GACrB,MAAM,+BAA+B,CAAC;AACvC,OAAO,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AACtD,YAAY,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAG/E,OAAO,EACL,iBAAiB,EACjB,mBAAmB,EACnB,mBAAmB,GACpB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACL,mBAAmB,EACnB,mBAAmB,EACnB,cAAc,EACd,oBAAoB,GACrB,MAAM,qBAAqB,CAAC;AAC7B,YAAY,EACV,WAAW,EACX,gBAAgB,EAChB,gBAAgB,GACjB,MAAM,qBAAqB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,92 @@
1
+ /**
2
+ * `@stapel/reviews-react` — the headless React pair for stapel-reviews
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
+ * review block carries none of it.
6
+ *
7
+ * ── The one-liner ──────────────────────────────────────────────────────────
8
+ *
9
+ * ```tsx
10
+ * const runtime = createReviewsRuntime({ baseUrl: "/reviews/api/v1" });
11
+ * const target = { targetType: "listing", targetKey: listingId };
12
+ * <ReviewsProvider runtime={runtime}>
13
+ * <ReviewAggregate target={target}>{(bag) => <YourStars bag={bag} />}</ReviewAggregate>
14
+ * <ReviewList target={target}>{(bag) => <YourRows bag={bag} />}</ReviewList>
15
+ * <ReviewForm target={target}>{(bag) => <YourForm bag={bag} />}</ReviewForm>
16
+ * </ReviewsProvider>
17
+ * ```
18
+ *
19
+ * `"listing"` in that snippet is the HOST's registry key, not this library's:
20
+ * stapel-reviews ships an EMPTY `TARGET_TYPES` registry and this package
21
+ * exports no target-type constants (see `api/types.ts`, `ReviewTarget`).
22
+ *
23
+ * ── The four properties this pair exists to guarantee ──────────────────────
24
+ *
25
+ * 1. **A zero average is not a zero rating.** `avg` is `0.0` when `count` is
26
+ * `0`, by the module's own contract. `ratingSummary()` answers
27
+ * `rated: false` there and gives a skin nothing to draw a star row from, so
28
+ * "nobody has rated this yet" can never be rendered as the worst possible
29
+ * score. Same class as `data ?? []`.
30
+ * 2. **The refusal is read by CODE, never by status.** "You have already
31
+ * reviewed this" is `error.400.reviews_duplicate_review` — a 400 — while
32
+ * the module's only 409 says the owner's reply already exists. Branching on
33
+ * the number would miss the first and mishandle the second
34
+ * (`model/refusals.ts`).
35
+ * 3. **A 401 is not an empty list.** Every stapel-reviews endpoint is
36
+ * `IsAuthenticated`, including both reads, so a signed-out visitor to a
37
+ * public listing page gets 401 — and the empty state would tell them a
38
+ * well-reviewed seller has never been reviewed. `signInRequired` is a
39
+ * named state on both read bags.
40
+ * 4. **A review that is not published says so.** `status` is carried to the
41
+ * screen (`pending` / `hidden`, plus an explicit `unknown` arm for a state
42
+ * a future backend adds), and the submit outcome reports the created row's
43
+ * status, so a pre-moderating deployment tells the author their review is
44
+ * waiting instead of leaving them to look for it.
45
+ *
46
+ * ── What this pair does NOT do ─────────────────────────────────────────────
47
+ *
48
+ * No moderation and no owner reply. `POST {id}/moderate` and
49
+ * `POST {id}/response` are both gated on the target type's fail-closed
50
+ * `can_moderate` callback and belong to consoles this pair does not ship
51
+ * (storefront spec §4.4: the reply is DISPLAYED, and the button to write one
52
+ * does not exist rather than existing switched off). Both stay in
53
+ * `manifest.json`, which lists the whole contract.
54
+ *
55
+ * No seller-wide rating fetch. The product model (spec fork F5) reviews the
56
+ * SELLER for a specific listing, so a seller's rating is a roll-up across
57
+ * their listings — which stapel-reviews cannot compute (one
58
+ * `(target_type, target_key)` per call) and the shop composite can
59
+ * (`shop.listing_review_summary`). `<ReviewAggregate aggregate={…}>` renders
60
+ * those two numbers from wherever the host got them; it does not invent an
61
+ * N+1 loop to fake the roll-up.
62
+ *
63
+ * No nav manifest: this pair has no route of its own. It renders INSIDE the
64
+ * listing detail page and the public seller profile, both of which belong to
65
+ * other pairs (the cdn-react precedent — a menu entry leading nowhere is
66
+ * worse than no entry).
67
+ *
68
+ * Layers: api → model → headless → i18n. Generated surfaces (the typed schema,
69
+ * the error map, the manifest, llms.txt) are produced by the monorepo `gen:*`
70
+ * drivers from stapel-reviews' own `docs/` artifacts and stand under drift
71
+ * gates.
72
+ */
73
+ // ── api ──────────────────────────────────────────────────────────────────────
74
+ export { createReviewsApi } from "./api/reviewsApi.js";
75
+ // ── model ────────────────────────────────────────────────────────────────────
76
+ export { createReviewsRuntime, DEFAULT_RATING_BOUNDS } from "./model/runtime.js";
77
+ export { ReviewsRuntimeContext, useReviewsAnalytics, useReviewsApi, useReviewsRuntime, } from "./model/context.js";
78
+ export { reviewsQueryKeys } from "./model/queryKeys.js";
79
+ export { REVIEWS_PAGE, useReviewAggregate, useReviewList } from "./model/queries.js";
80
+ export { useSubmitReview } from "./model/mutations.js";
81
+ export { ratingSummary, starBreakdown } from "./model/rating.js";
82
+ export { findOwnReview, isModeratedOut, reviewsFromPages, reviewVisibility, } from "./model/list.js";
83
+ export { isDuplicateReview, isReviewingForbidden, isSignInRequired, isUnknownTargetType, REVIEWS_ERROR_ALREADY_RESPONDED, REVIEWS_ERROR_CANNOT_MODERATE, REVIEWS_ERROR_CANNOT_REVIEW, REVIEWS_ERROR_DUPLICATE, REVIEWS_ERROR_INVALID_RATING, REVIEWS_ERROR_NOT_FOUND, REVIEWS_ERROR_RESPONSE_NOT_ALLOWED, REVIEWS_ERROR_UNKNOWN_TARGET_TYPE, toReviewsError, } from "./model/refusals.js";
84
+ // ── headless ─────────────────────────────────────────────────────────────────
85
+ export { ReviewsProvider } from "./headless/ReviewsProvider.js";
86
+ export { ReviewList } from "./headless/ReviewList.js";
87
+ export { ReviewAggregate } from "./headless/ReviewAggregate.js";
88
+ export { ReviewForm } from "./headless/ReviewForm.js";
89
+ // ── i18n ─────────────────────────────────────────────────────────────────────
90
+ export { REVIEWS_I18N_KEYS, registerReviewsI18n, reviewsI18nBundleEn, } from "./i18n/keys.js";
91
+ export { explainReviewsError, REVIEWS_ERROR_CODES, REVIEWS_ERRORS, reviewsErrorBundleEn, } from "./i18n/errorsMap.js";
92
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuEG;AAEH,gFAAgF;AAChF,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAgBvD,gFAAgF;AAChF,OAAO,EAAE,oBAAoB,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AAMjF,OAAO,EACL,qBAAqB,EACrB,mBAAmB,EACnB,aAAa,EACb,iBAAiB,GAClB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AACxD,OAAO,EAAE,YAAY,EAAE,kBAAkB,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAErF,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAEvD,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAEjE,OAAO,EACL,aAAa,EACb,cAAc,EACd,gBAAgB,EAChB,gBAAgB,GACjB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACL,iBAAiB,EACjB,oBAAoB,EACpB,gBAAgB,EAChB,mBAAmB,EACnB,+BAA+B,EAC/B,6BAA6B,EAC7B,2BAA2B,EAC3B,uBAAuB,EACvB,4BAA4B,EAC5B,uBAAuB,EACvB,kCAAkC,EAClC,iCAAiC,EACjC,cAAc,GACf,MAAM,qBAAqB,CAAC;AAE7B,gFAAgF;AAChF,OAAO,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAC;AAChE,OAAO,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AAEtD,OAAO,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAC;AAKhE,OAAO,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AAGtD,gFAAgF;AAChF,OAAO,EACL,iBAAiB,EACjB,mBAAmB,EACnB,mBAAmB,GACpB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACL,mBAAmB,EACnB,mBAAmB,EACnB,cAAc,EACd,oBAAoB,GACrB,MAAM,qBAAqB,CAAC"}
@@ -0,0 +1,11 @@
1
+ import type { Context } from "react";
2
+ import type { Analytics, ModuleContextKit } from "@stapel/core";
3
+ import type { ReviewsApi } from "../api/reviewsApi.js";
4
+ import type { ReviewsRuntime } from "./runtime.js";
5
+ export declare const ReviewsRuntimeContext: Context<ReviewsRuntime | null>;
6
+ export declare const useReviewsRuntime: () => ReviewsRuntime;
7
+ export declare const useReviewsApi: () => ReviewsApi;
8
+ export declare const useReviewsAnalytics: () => Analytics | null;
9
+ /** @internal Re-exported as `<ReviewsProvider>` from `headless/`. */
10
+ export declare const ModuleProvider: ModuleContextKit<ReviewsRuntime>["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,UAAU,EAAE,MAAM,sBAAsB,CAAC;AACvD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAYnD,eAAO,MAAM,qBAAqB,EAAE,OAAO,CAAC,cAAc,GAAG,IAAI,CAC7C,CAAC;AAErB,eAAO,MAAM,iBAAiB,EAAE,MAAM,cAA+B,CAAC;AAEtE,eAAO,MAAM,aAAa,EAAE,MAAM,UAAuB,CAAC;AAE1D,eAAO,MAAM,mBAAmB,EAAE,MAAM,SAAS,GAAG,IAAuB,CAAC;AAE5E,qEAAqE;AACrE,eAAO,MAAM,cAAc,EAAE,gBAAgB,CAAC,cAAc,CAAC,CAAC,UAAU,CAC1D,CAAC"}
@@ -0,0 +1,16 @@
1
+ import { createModuleContext } from "@stapel/core";
2
+ /**
3
+ * The wired ReviewsRuntime shared through React context by
4
+ * `<ReviewsProvider>`. Hooks in `model/` and `headless/` read the singletons
5
+ * from here. One reviewed copy of this plumbing lives in `@stapel/core`
6
+ * (`createModuleContext`, slim wave §21/S2); this module binds it under the
7
+ * pair's public names.
8
+ */
9
+ const kit = createModuleContext("Reviews");
10
+ export const ReviewsRuntimeContext = kit.RuntimeContext;
11
+ export const useReviewsRuntime = kit.useRuntime;
12
+ export const useReviewsApi = kit.useApi;
13
+ export const useReviewsAnalytics = kit.useAnalytics;
14
+ /** @internal Re-exported as `<ReviewsProvider>` from `headless/`. */
15
+ export const ModuleProvider = kit.Provider;
16
+ //# 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;;;;;;GAMG;AACH,MAAM,GAAG,GACP,mBAAmB,CAAiB,SAAS,CAAC,CAAC;AAEjD,MAAM,CAAC,MAAM,qBAAqB,GAChC,GAAG,CAAC,cAAc,CAAC;AAErB,MAAM,CAAC,MAAM,iBAAiB,GAAyB,GAAG,CAAC,UAAU,CAAC;AAEtE,MAAM,CAAC,MAAM,aAAa,GAAqB,GAAG,CAAC,MAAM,CAAC;AAE1D,MAAM,CAAC,MAAM,mBAAmB,GAA2B,GAAG,CAAC,YAAY,CAAC;AAE5E,qEAAqE;AACrE,MAAM,CAAC,MAAM,cAAc,GACzB,GAAG,CAAC,QAAQ,CAAC"}
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Pure readers over a loaded list of reviews — no React, no queries, so a
3
+ * card, a test and a server-rendered page can all use them.
4
+ */
5
+ import type { InfiniteData } from "@tanstack/react-query";
6
+ import type { Review, ReviewPage, ReviewStatus } from "../api/types.js";
7
+ /**
8
+ * Flatten the pages of the infinite list into one newest-first run.
9
+ *
10
+ * Deliberately takes `InfiniteData | undefined` and answers `undefined` for
11
+ * "not loaded", never `[]`: an empty array here is a claim that the target
12
+ * has no reviews, and the caller must get that claim from a READY load state
13
+ * (`stapel/no-flattened-load-state` — the rule exists because a total outage
14
+ * once rendered as "you have no workspaces").
15
+ */
16
+ export declare function reviewsFromPages(data: InfiniteData<ReviewPage, string | undefined> | undefined): readonly Review[] | undefined;
17
+ /**
18
+ * A review's visibility, narrowed — with a fourth arm for a state this build
19
+ * does not know.
20
+ *
21
+ * `status` is a bare `string` on the wire. A build that met a new upstream
22
+ * state and silently rendered it as published would be showing something the
23
+ * server may have hidden; one that crashed on it would take the page down.
24
+ * `"unknown"` is neither: the skin shows the row with an explicit "this
25
+ * version does not know this state" badge, the same shape attributes-react
26
+ * uses for an unsupported feature type.
27
+ */
28
+ export declare function reviewVisibility(status: string): ReviewStatus | "unknown";
29
+ /** Is this row one a moderator sees only because they asked for `include=all`? */
30
+ export declare function isModeratedOut(review: Review): boolean;
31
+ /**
32
+ * The viewer's own review of this target, if it is in the loaded rows.
33
+ *
34
+ * THIS IS AN OPTIMISTIC PRE-CHECK, AND IT HAS A KNOWN HOLE — recorded here
35
+ * because the hole is in the contract, not in the code. The list a
36
+ * non-moderator reads is published-only, so under `moderation: "pre"` the
37
+ * author's own review is INVISIBLE TO ITS AUTHOR while it waits: this
38
+ * function answers `undefined`, the form offers itself again, and the server
39
+ * refuses the second attempt with `error.400.reviews_duplicate_review`.
40
+ *
41
+ * That is why the refusal is a first-class outcome of the form
42
+ * (`headless/ReviewForm.tsx`) rather than something the pre-check was
43
+ * supposed to prevent. The pre-check saves a pointless round trip in the
44
+ * common (post-moderation) case; it is never the authority.
45
+ *
46
+ * `viewerId` must be the id the backend puts in `author_id` — the user id,
47
+ * NOT a profile id. A host that passes the wrong one gets `undefined`, i.e.
48
+ * the form offered where it need not have been, and the server still holds
49
+ * the line.
50
+ */
51
+ export declare function findOwnReview(reviews: readonly Review[] | undefined, viewerId: string | null | undefined): Review | undefined;
52
+ //# sourceMappingURL=list.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"list.d.ts","sourceRoot":"","sources":["../../src/model/list.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAC1D,OAAO,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAExE;;;;;;;;GAQG;AACH,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,YAAY,CAAC,UAAU,EAAE,MAAM,GAAG,SAAS,CAAC,GAAG,SAAS,GAC7D,SAAS,MAAM,EAAE,GAAG,SAAS,CAG/B;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,gBAAgB,CAC9B,MAAM,EAAE,MAAM,GACb,YAAY,GAAG,SAAS,CAI1B;AAED,kFAAkF;AAClF,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAEtD;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,aAAa,CAC3B,OAAO,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,EACtC,QAAQ,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAClC,MAAM,GAAG,SAAS,CAGpB"}