@intlayer/api 9.3.3 → 9.4.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 (337) hide show
  1. package/README.md +4 -1
  2. package/dist/cjs/cms/createAuthManager.cjs +10 -1
  3. package/dist/cjs/cms/createAuthManager.cjs.map +1 -1
  4. package/dist/cjs/getIntlayerAPI/analytics.cjs +5 -4
  5. package/dist/cjs/getIntlayerAPI/analytics.cjs.map +1 -1
  6. package/dist/cjs/public/createPublicClient.cjs +123 -0
  7. package/dist/cjs/public/createPublicClient.cjs.map +1 -0
  8. package/dist/cjs/public/index.cjs +4 -0
  9. package/dist/esm/cms/createAuthManager.mjs +10 -1
  10. package/dist/esm/cms/createAuthManager.mjs.map +1 -1
  11. package/dist/esm/getIntlayerAPI/analytics.mjs +5 -4
  12. package/dist/esm/getIntlayerAPI/analytics.mjs.map +1 -1
  13. package/dist/esm/public/createPublicClient.mjs +122 -0
  14. package/dist/esm/public/createPublicClient.mjs.map +1 -0
  15. package/dist/esm/public/index.mjs +3 -0
  16. package/dist/types/cms/createAuthManager.d.ts.map +1 -1
  17. package/dist/types/distantDictionary/fetchDistantDictionaries.d.ts +1 -2
  18. package/dist/types/distantDictionary/fetchDistantDictionaries.d.ts.map +1 -1
  19. package/dist/types/distantDictionary/fetchDistantDictionary.d.ts +1 -2
  20. package/dist/types/distantDictionary/fetchDistantDictionary.d.ts.map +1 -1
  21. package/dist/types/getIntlayerAPI/ai.d.ts +1 -3
  22. package/dist/types/getIntlayerAPI/ai.d.ts.map +1 -1
  23. package/dist/types/getIntlayerAPI/analytics.d.ts +3 -4
  24. package/dist/types/getIntlayerAPI/analytics.d.ts.map +1 -1
  25. package/dist/types/getIntlayerAPI/asset.d.ts +0 -2
  26. package/dist/types/getIntlayerAPI/asset.d.ts.map +1 -1
  27. package/dist/types/getIntlayerAPI/audit.d.ts +1 -3
  28. package/dist/types/getIntlayerAPI/audit.d.ts.map +1 -1
  29. package/dist/types/getIntlayerAPI/dictionary.d.ts +1 -2
  30. package/dist/types/getIntlayerAPI/dictionary.d.ts.map +1 -1
  31. package/dist/types/getIntlayerAPI/editor.d.ts +3 -3
  32. package/dist/types/getIntlayerAPI/environment.d.ts +1 -2
  33. package/dist/types/getIntlayerAPI/environment.d.ts.map +1 -1
  34. package/dist/types/getIntlayerAPI/newsletter.d.ts +1 -2
  35. package/dist/types/getIntlayerAPI/newsletter.d.ts.map +1 -1
  36. package/dist/types/getIntlayerAPI/oAuth.d.ts +0 -3
  37. package/dist/types/getIntlayerAPI/oAuth.d.ts.map +1 -1
  38. package/dist/types/getIntlayerAPI/organization.d.ts +1 -2
  39. package/dist/types/getIntlayerAPI/organization.d.ts.map +1 -1
  40. package/dist/types/getIntlayerAPI/project.d.ts +1 -5
  41. package/dist/types/getIntlayerAPI/project.d.ts.map +1 -1
  42. package/dist/types/getIntlayerAPI/reviewer.d.ts +31 -636
  43. package/dist/types/getIntlayerAPI/reviewer.d.ts.map +1 -1
  44. package/dist/types/getIntlayerAPI/search.d.ts +1 -2
  45. package/dist/types/getIntlayerAPI/search.d.ts.map +1 -1
  46. package/dist/types/getIntlayerAPI/showcaseProject.d.ts +1 -2
  47. package/dist/types/getIntlayerAPI/showcaseProject.d.ts.map +1 -1
  48. package/dist/types/getIntlayerAPI/status.d.ts +0 -2
  49. package/dist/types/getIntlayerAPI/status.d.ts.map +1 -1
  50. package/dist/types/getIntlayerAPI/stripe.d.ts +1 -2
  51. package/dist/types/getIntlayerAPI/stripe.d.ts.map +1 -1
  52. package/dist/types/getIntlayerAPI/tag.d.ts +1 -2
  53. package/dist/types/getIntlayerAPI/tag.d.ts.map +1 -1
  54. package/dist/types/getIntlayerAPI/translate.d.ts +1 -2
  55. package/dist/types/getIntlayerAPI/translate.d.ts.map +1 -1
  56. package/dist/types/getIntlayerAPI/user.d.ts +3 -7
  57. package/dist/types/getIntlayerAPI/user.d.ts.map +1 -1
  58. package/dist/types/index.d.ts +4 -9
  59. package/dist/types/public/createPublicClient.d.ts +34 -0
  60. package/dist/types/public/createPublicClient.d.ts.map +1 -0
  61. package/dist/types/public/index.d.ts +2 -0
  62. package/dist/types/types.d.ts +1 -2
  63. package/package.json +12 -4
  64. package/dist/types/apps/backend/dist/types/controllers/ai.controller.d.ts +0 -78
  65. package/dist/types/apps/backend/dist/types/controllers/ai.controller.d.ts.map +0 -1
  66. package/dist/types/apps/backend/dist/types/controllers/analytics.controller.d.ts +0 -21
  67. package/dist/types/apps/backend/dist/types/controllers/analytics.controller.d.ts.map +0 -1
  68. package/dist/types/apps/backend/dist/types/controllers/asset.controller.d.ts +0 -13
  69. package/dist/types/apps/backend/dist/types/controllers/asset.controller.d.ts.map +0 -1
  70. package/dist/types/apps/backend/dist/types/controllers/audit.controller.d.ts +0 -1
  71. package/dist/types/apps/backend/dist/types/controllers/bitbucket.controller.d.ts +0 -1
  72. package/dist/types/apps/backend/dist/types/controllers/cliSessionToken.controller.d.ts +0 -15
  73. package/dist/types/apps/backend/dist/types/controllers/cliSessionToken.controller.d.ts.map +0 -1
  74. package/dist/types/apps/backend/dist/types/controllers/dictionary.controller.d.ts +0 -63
  75. package/dist/types/apps/backend/dist/types/controllers/dictionary.controller.d.ts.map +0 -1
  76. package/dist/types/apps/backend/dist/types/controllers/environment.controller.d.ts +0 -27
  77. package/dist/types/apps/backend/dist/types/controllers/environment.controller.d.ts.map +0 -1
  78. package/dist/types/apps/backend/dist/types/controllers/eventListener.controller.d.ts +0 -2
  79. package/dist/types/apps/backend/dist/types/controllers/github.controller.d.ts +0 -2
  80. package/dist/types/apps/backend/dist/types/controllers/gitlab.controller.d.ts +0 -1
  81. package/dist/types/apps/backend/dist/types/controllers/newsletter.controller.d.ts +0 -17
  82. package/dist/types/apps/backend/dist/types/controllers/newsletter.controller.d.ts.map +0 -1
  83. package/dist/types/apps/backend/dist/types/controllers/oAuth2.controller.d.ts +0 -8
  84. package/dist/types/apps/backend/dist/types/controllers/oAuth2.controller.d.ts.map +0 -1
  85. package/dist/types/apps/backend/dist/types/controllers/organization.controller.d.ts +0 -39
  86. package/dist/types/apps/backend/dist/types/controllers/organization.controller.d.ts.map +0 -1
  87. package/dist/types/apps/backend/dist/types/controllers/project.controller.d.ts +0 -51
  88. package/dist/types/apps/backend/dist/types/controllers/project.controller.d.ts.map +0 -1
  89. package/dist/types/apps/backend/dist/types/controllers/projectAccessKey.controller.d.ts +0 -18
  90. package/dist/types/apps/backend/dist/types/controllers/projectAccessKey.controller.d.ts.map +0 -1
  91. package/dist/types/apps/backend/dist/types/controllers/projectMemberAccess.controller.d.ts +0 -14
  92. package/dist/types/apps/backend/dist/types/controllers/projectMemberAccess.controller.d.ts.map +0 -1
  93. package/dist/types/apps/backend/dist/types/controllers/recursiveAudit.controller.d.ts +0 -32
  94. package/dist/types/apps/backend/dist/types/controllers/recursiveAudit.controller.d.ts.map +0 -1
  95. package/dist/types/apps/backend/dist/types/controllers/reviewer.controller.d.ts +0 -70
  96. package/dist/types/apps/backend/dist/types/controllers/reviewer.controller.d.ts.map +0 -1
  97. package/dist/types/apps/backend/dist/types/controllers/searchDoc.controller.d.ts +0 -20
  98. package/dist/types/apps/backend/dist/types/controllers/searchDoc.controller.d.ts.map +0 -1
  99. package/dist/types/apps/backend/dist/types/controllers/showcaseProject.controller.d.ts +0 -51
  100. package/dist/types/apps/backend/dist/types/controllers/showcaseProject.controller.d.ts.map +0 -1
  101. package/dist/types/apps/backend/dist/types/controllers/stripe.controller.d.ts +0 -92
  102. package/dist/types/apps/backend/dist/types/controllers/stripe.controller.d.ts.map +0 -1
  103. package/dist/types/apps/backend/dist/types/controllers/tag.controller.d.ts +0 -23
  104. package/dist/types/apps/backend/dist/types/controllers/tag.controller.d.ts.map +0 -1
  105. package/dist/types/apps/backend/dist/types/controllers/translation.controller.d.ts +0 -16
  106. package/dist/types/apps/backend/dist/types/controllers/translation.controller.d.ts.map +0 -1
  107. package/dist/types/apps/backend/dist/types/controllers/user.controller.d.ts +0 -31
  108. package/dist/types/apps/backend/dist/types/controllers/user.controller.d.ts.map +0 -1
  109. package/dist/types/apps/backend/dist/types/export.d.ts +0 -65
  110. package/dist/types/apps/backend/dist/types/routes/ai.routes.d.ts +0 -1
  111. package/dist/types/apps/backend/dist/types/routes/analytics.routes.d.ts +0 -1
  112. package/dist/types/apps/backend/dist/types/routes/asset.routes.d.ts +0 -1
  113. package/dist/types/apps/backend/dist/types/routes/audit.routes.d.ts +0 -1
  114. package/dist/types/apps/backend/dist/types/routes/bitbucket.routes.d.ts +0 -1
  115. package/dist/types/apps/backend/dist/types/routes/dictionary.routes.d.ts +0 -1
  116. package/dist/types/apps/backend/dist/types/routes/environment.routes.d.ts +0 -1
  117. package/dist/types/apps/backend/dist/types/routes/github.routes.d.ts +0 -1
  118. package/dist/types/apps/backend/dist/types/routes/gitlab.routes.d.ts +0 -1
  119. package/dist/types/apps/backend/dist/types/routes/newsletter.routes.d.ts +0 -1
  120. package/dist/types/apps/backend/dist/types/routes/organization.routes.d.ts +0 -1
  121. package/dist/types/apps/backend/dist/types/routes/project.routes.d.ts +0 -1
  122. package/dist/types/apps/backend/dist/types/routes/reviewer.routes.d.ts +0 -1
  123. package/dist/types/apps/backend/dist/types/routes/showcaseProject.routes.d.ts +0 -1
  124. package/dist/types/apps/backend/dist/types/routes/stripe.routes.d.ts +0 -1
  125. package/dist/types/apps/backend/dist/types/routes/translate.routes.d.ts +0 -1
  126. package/dist/types/apps/backend/dist/types/routes/user.routes.d.ts +0 -1
  127. package/dist/types/apps/backend/dist/types/services/audit/types.d.ts +0 -52
  128. package/dist/types/apps/backend/dist/types/services/audit/types.d.ts.map +0 -1
  129. package/dist/types/apps/backend/dist/types/services/ci.service.d.ts +0 -2
  130. package/dist/types/apps/backend/dist/types/services/github.service.d.ts +0 -2
  131. package/dist/types/apps/backend/dist/types/services/subscription.service.d.ts +0 -15
  132. package/dist/types/apps/backend/dist/types/services/subscription.service.d.ts.map +0 -1
  133. package/dist/types/apps/backend/dist/types/types/affiliate.types.d.ts +0 -40
  134. package/dist/types/apps/backend/dist/types/types/affiliate.types.d.ts.map +0 -1
  135. package/dist/types/apps/backend/dist/types/types/affiliateInvitation.types.d.ts +0 -26
  136. package/dist/types/apps/backend/dist/types/types/affiliateInvitation.types.d.ts.map +0 -1
  137. package/dist/types/apps/backend/dist/types/types/aiStats.types.d.ts +0 -35
  138. package/dist/types/apps/backend/dist/types/types/aiStats.types.d.ts.map +0 -1
  139. package/dist/types/apps/backend/dist/types/types/analytics.types.d.ts +0 -108
  140. package/dist/types/apps/backend/dist/types/types/analytics.types.d.ts.map +0 -1
  141. package/dist/types/apps/backend/dist/types/types/asset.types.d.ts +0 -31
  142. package/dist/types/apps/backend/dist/types/types/asset.types.d.ts.map +0 -1
  143. package/dist/types/apps/backend/dist/types/types/dictionary.types.d.ts +0 -67
  144. package/dist/types/apps/backend/dist/types/types/dictionary.types.d.ts.map +0 -1
  145. package/dist/types/apps/backend/dist/types/types/discussion.types.d.ts +0 -42
  146. package/dist/types/apps/backend/dist/types/types/discussion.types.d.ts.map +0 -1
  147. package/dist/types/apps/backend/dist/types/types/oAuth2.types.d.ts +0 -15
  148. package/dist/types/apps/backend/dist/types/types/oAuth2.types.d.ts.map +0 -1
  149. package/dist/types/apps/backend/dist/types/types/organization.types.d.ts +0 -106
  150. package/dist/types/apps/backend/dist/types/types/organization.types.d.ts.map +0 -1
  151. package/dist/types/apps/backend/dist/types/types/plan.types.d.ts +0 -23
  152. package/dist/types/apps/backend/dist/types/types/plan.types.d.ts.map +0 -1
  153. package/dist/types/apps/backend/dist/types/types/project.types.d.ts +0 -128
  154. package/dist/types/apps/backend/dist/types/types/project.types.d.ts.map +0 -1
  155. package/dist/types/apps/backend/dist/types/types/projectInsights.types.d.ts +0 -79
  156. package/dist/types/apps/backend/dist/types/types/projectInsights.types.d.ts.map +0 -1
  157. package/dist/types/apps/backend/dist/types/types/promoCode.types.d.ts +0 -26
  158. package/dist/types/apps/backend/dist/types/types/promoCode.types.d.ts.map +0 -1
  159. package/dist/types/apps/backend/dist/types/types/reviewer.types.d.ts +0 -60
  160. package/dist/types/apps/backend/dist/types/types/reviewer.types.d.ts.map +0 -1
  161. package/dist/types/apps/backend/dist/types/types/session.types.d.ts +0 -8
  162. package/dist/types/apps/backend/dist/types/types/showcaseProject.types.d.ts +0 -58
  163. package/dist/types/apps/backend/dist/types/types/showcaseProject.types.d.ts.map +0 -1
  164. package/dist/types/apps/backend/dist/types/types/tag.types.d.ts +0 -31
  165. package/dist/types/apps/backend/dist/types/types/tag.types.d.ts.map +0 -1
  166. package/dist/types/apps/backend/dist/types/types/user.types.d.ts +0 -31
  167. package/dist/types/apps/backend/dist/types/types/user.types.d.ts.map +0 -1
  168. package/dist/types/apps/backend/dist/types/utils/AI/askDocQuestion/askDocQuestion.d.ts +0 -9
  169. package/dist/types/apps/backend/dist/types/utils/AI/askDocQuestion/askDocQuestion.d.ts.map +0 -1
  170. package/dist/types/apps/backend/dist/types/utils/AI/auditDictionary/index.d.ts +0 -15
  171. package/dist/types/apps/backend/dist/types/utils/AI/auditDictionary/index.d.ts.map +0 -1
  172. package/dist/types/apps/backend/dist/types/utils/AI/auditDictionaryField/index.d.ts +0 -12
  173. package/dist/types/apps/backend/dist/types/utils/AI/auditDictionaryField/index.d.ts.map +0 -1
  174. package/dist/types/apps/backend/dist/types/utils/AI/auditDictionaryMetadata/index.d.ts +0 -4
  175. package/dist/types/apps/backend/dist/types/utils/AI/auditTag/index.d.ts +0 -11
  176. package/dist/types/apps/backend/dist/types/utils/AI/auditTag/index.d.ts.map +0 -1
  177. package/dist/types/apps/backend/dist/types/utils/AI/chat/index.d.ts +0 -17
  178. package/dist/types/apps/backend/dist/types/utils/AI/chat/index.d.ts.map +0 -1
  179. package/dist/types/apps/backend/dist/types/utils/AI/customQuery/index.d.ts +0 -12
  180. package/dist/types/apps/backend/dist/types/utils/AI/customQuery/index.d.ts.map +0 -1
  181. package/dist/types/apps/backend/dist/types/utils/AI/translateJSON/index.d.ts +0 -21
  182. package/dist/types/apps/backend/dist/types/utils/AI/translateJSON/index.d.ts.map +0 -1
  183. package/dist/types/apps/backend/dist/types/utils/auth/getAuth.d.ts +0 -3
  184. package/dist/types/apps/backend/dist/types/utils/filtersAndPagination/getDictionaryFiltersAndPagination.d.ts +0 -43
  185. package/dist/types/apps/backend/dist/types/utils/filtersAndPagination/getDictionaryFiltersAndPagination.d.ts.map +0 -1
  186. package/dist/types/apps/backend/dist/types/utils/filtersAndPagination/getDiscussionFiltersAndPagination.d.ts +0 -21
  187. package/dist/types/apps/backend/dist/types/utils/filtersAndPagination/getDiscussionFiltersAndPagination.d.ts.map +0 -1
  188. package/dist/types/apps/backend/dist/types/utils/filtersAndPagination/getFiltersAndPaginationFromBody.d.ts +0 -12
  189. package/dist/types/apps/backend/dist/types/utils/filtersAndPagination/getFiltersAndPaginationFromBody.d.ts.map +0 -1
  190. package/dist/types/apps/backend/dist/types/utils/filtersAndPagination/getOrganizationFiltersAndPagination.d.ts +0 -29
  191. package/dist/types/apps/backend/dist/types/utils/filtersAndPagination/getOrganizationFiltersAndPagination.d.ts.map +0 -1
  192. package/dist/types/apps/backend/dist/types/utils/filtersAndPagination/getProjectFiltersAndPagination.d.ts +0 -22
  193. package/dist/types/apps/backend/dist/types/utils/filtersAndPagination/getProjectFiltersAndPagination.d.ts.map +0 -1
  194. package/dist/types/apps/backend/dist/types/utils/filtersAndPagination/getTagFiltersAndPagination.d.ts +0 -20
  195. package/dist/types/apps/backend/dist/types/utils/filtersAndPagination/getTagFiltersAndPagination.d.ts.map +0 -1
  196. package/dist/types/apps/backend/dist/types/utils/filtersAndPagination/getUserFiltersAndPagination.d.ts +0 -24
  197. package/dist/types/apps/backend/dist/types/utils/filtersAndPagination/getUserFiltersAndPagination.d.ts.map +0 -1
  198. package/dist/types/apps/backend/dist/types/utils/httpStatusCodes.d.ts +0 -408
  199. package/dist/types/apps/backend/dist/types/utils/httpStatusCodes.d.ts.map +0 -1
  200. package/dist/types/apps/backend/dist/types/utils/mongoDB/types.d.ts +0 -9
  201. package/dist/types/apps/backend/dist/types/utils/mongoDB/types.d.ts.map +0 -1
  202. package/dist/types/apps/backend/dist/types/utils/permissions.d.ts +0 -7
  203. package/dist/types/apps/backend/dist/types/utils/responseData.d.ts +0 -26
  204. package/dist/types/apps/backend/dist/types/utils/responseData.d.ts.map +0 -1
  205. package/dist/types/packages/@intlayer/ai/dist/types/aiSdk.d.ts +0 -119
  206. package/dist/types/packages/@intlayer/ai/dist/types/aiSdk.d.ts.map +0 -1
  207. package/dist/types/packages/@intlayer/ai/dist/types/auditDictionaryMetadata/index.d.ts +0 -13
  208. package/dist/types/packages/@intlayer/ai/dist/types/auditDictionaryMetadata/index.d.ts.map +0 -1
  209. package/dist/types/packages/@intlayer/ai/dist/types/customQuery.d.ts +0 -10
  210. package/dist/types/packages/@intlayer/ai/dist/types/customQuery.d.ts.map +0 -1
  211. package/dist/types/packages/@intlayer/ai/dist/types/index.d.ts +0 -30
  212. package/dist/types/packages/@intlayer/ai/dist/types/index.d.ts.map +0 -1
  213. package/dist/types/packages/@intlayer/ai/dist/types/translateJSON/index.d.ts +0 -10
  214. package/dist/types/packages/@intlayer/ai/dist/types/translateJSON/index.d.ts.map +0 -1
  215. package/dist/types/packages/@intlayer/core/dist/types/formatters/compact.d.ts +0 -1
  216. package/dist/types/packages/@intlayer/core/dist/types/formatters/currency.d.ts +0 -1
  217. package/dist/types/packages/@intlayer/core/dist/types/formatters/date.d.ts +0 -1
  218. package/dist/types/packages/@intlayer/core/dist/types/formatters/index.d.ts +0 -9
  219. package/dist/types/packages/@intlayer/core/dist/types/formatters/list.d.ts +0 -1
  220. package/dist/types/packages/@intlayer/core/dist/types/formatters/number.d.ts +0 -1
  221. package/dist/types/packages/@intlayer/core/dist/types/formatters/percentage.d.ts +0 -1
  222. package/dist/types/packages/@intlayer/core/dist/types/formatters/relativeTime.d.ts +0 -1
  223. package/dist/types/packages/@intlayer/core/dist/types/formatters/units.d.ts +0 -1
  224. package/dist/types/packages/@intlayer/core/dist/types/interpreter/getCondition.d.ts +0 -2
  225. package/dist/types/packages/@intlayer/core/dist/types/interpreter/getContent/deepTransform.d.ts +0 -1
  226. package/dist/types/packages/@intlayer/core/dist/types/interpreter/getContent/getContent.d.ts +0 -3
  227. package/dist/types/packages/@intlayer/core/dist/types/interpreter/getContent/index.d.ts +0 -3
  228. package/dist/types/packages/@intlayer/core/dist/types/interpreter/getContent/plugins.d.ts +0 -7
  229. package/dist/types/packages/@intlayer/core/dist/types/interpreter/getDictionary.d.ts +0 -4
  230. package/dist/types/packages/@intlayer/core/dist/types/interpreter/getEnumeration.d.ts +0 -2
  231. package/dist/types/packages/@intlayer/core/dist/types/interpreter/getGender.d.ts +0 -2
  232. package/dist/types/packages/@intlayer/core/dist/types/interpreter/getInsertion.d.ts +0 -2
  233. package/dist/types/packages/@intlayer/core/dist/types/interpreter/getIntlayer.d.ts +0 -4
  234. package/dist/types/packages/@intlayer/core/dist/types/interpreter/getNesting.d.ts +0 -6
  235. package/dist/types/packages/@intlayer/core/dist/types/interpreter/getPlural.d.ts +0 -3
  236. package/dist/types/packages/@intlayer/core/dist/types/interpreter/getSelect.d.ts +0 -2
  237. package/dist/types/packages/@intlayer/core/dist/types/interpreter/getTranslation.d.ts +0 -1
  238. package/dist/types/packages/@intlayer/core/dist/types/interpreter/index.d.ts +0 -15
  239. package/dist/types/packages/@intlayer/core/dist/types/interpreter/interpolableNode.d.ts +0 -2
  240. package/dist/types/packages/@intlayer/core/dist/types/localization/comparePaths.d.ts +0 -1
  241. package/dist/types/packages/@intlayer/core/dist/types/localization/domainUtils.d.ts +0 -2
  242. package/dist/types/packages/@intlayer/core/dist/types/localization/generateSitemap.d.ts +0 -2
  243. package/dist/types/packages/@intlayer/core/dist/types/localization/getBrowserLocale.d.ts +0 -1
  244. package/dist/types/packages/@intlayer/core/dist/types/localization/getHTMLTextDir.d.ts +0 -1
  245. package/dist/types/packages/@intlayer/core/dist/types/localization/getLocale.d.ts +0 -1
  246. package/dist/types/packages/@intlayer/core/dist/types/localization/getLocaleFromPath.d.ts +0 -2
  247. package/dist/types/packages/@intlayer/core/dist/types/localization/getLocaleLang.d.ts +0 -1
  248. package/dist/types/packages/@intlayer/core/dist/types/localization/getLocaleName.d.ts +0 -1
  249. package/dist/types/packages/@intlayer/core/dist/types/localization/getLocalizedPath.d.ts +0 -2
  250. package/dist/types/packages/@intlayer/core/dist/types/localization/getLocalizedUrl.d.ts +0 -2
  251. package/dist/types/packages/@intlayer/core/dist/types/localization/getMultilingualUrls.d.ts +0 -2
  252. package/dist/types/packages/@intlayer/core/dist/types/localization/getPathWithoutLocale.d.ts +0 -1
  253. package/dist/types/packages/@intlayer/core/dist/types/localization/getPrefix.d.ts +0 -2
  254. package/dist/types/packages/@intlayer/core/dist/types/localization/index.d.ts +0 -20
  255. package/dist/types/packages/@intlayer/core/dist/types/localization/localeDetector.d.ts +0 -2
  256. package/dist/types/packages/@intlayer/core/dist/types/localization/localeMapper.d.ts +0 -1
  257. package/dist/types/packages/@intlayer/core/dist/types/localization/localeResolver.d.ts +0 -1
  258. package/dist/types/packages/@intlayer/core/dist/types/localization/resolveBrowserLocale.d.ts +0 -2
  259. package/dist/types/packages/@intlayer/core/dist/types/localization/rewriteUtils.d.ts +0 -2
  260. package/dist/types/packages/@intlayer/core/dist/types/localization/validatePrefix.d.ts +0 -2
  261. package/dist/types/packages/@intlayer/core/dist/types/markdown/index.d.ts +0 -2
  262. package/dist/types/packages/@intlayer/core/dist/types/messageFormat/ICU.d.ts +0 -1
  263. package/dist/types/packages/@intlayer/core/dist/types/messageFormat/i18next.d.ts +0 -2
  264. package/dist/types/packages/@intlayer/core/dist/types/messageFormat/index.d.ts +0 -5
  265. package/dist/types/packages/@intlayer/core/dist/types/messageFormat/po.d.ts +0 -2
  266. package/dist/types/packages/@intlayer/core/dist/types/messageFormat/resolveMessage.d.ts +0 -1
  267. package/dist/types/packages/@intlayer/core/dist/types/messageFormat/vue-i18n.d.ts +0 -2
  268. package/dist/types/packages/@intlayer/core/dist/types/transpiler/condition/condition.d.ts +0 -1
  269. package/dist/types/packages/@intlayer/core/dist/types/transpiler/condition/index.d.ts +0 -1
  270. package/dist/types/packages/@intlayer/core/dist/types/transpiler/enumeration/enumeration.d.ts +0 -1
  271. package/dist/types/packages/@intlayer/core/dist/types/transpiler/enumeration/index.d.ts +0 -1
  272. package/dist/types/packages/@intlayer/core/dist/types/transpiler/file/file.d.ts +0 -1
  273. package/dist/types/packages/@intlayer/core/dist/types/transpiler/file/index.d.ts +0 -1
  274. package/dist/types/packages/@intlayer/core/dist/types/transpiler/gender/gender.d.ts +0 -1
  275. package/dist/types/packages/@intlayer/core/dist/types/transpiler/gender/index.d.ts +0 -1
  276. package/dist/types/packages/@intlayer/core/dist/types/transpiler/html/html.d.ts +0 -1
  277. package/dist/types/packages/@intlayer/core/dist/types/transpiler/html/index.d.ts +0 -1
  278. package/dist/types/packages/@intlayer/core/dist/types/transpiler/index.d.ts +0 -22
  279. package/dist/types/packages/@intlayer/core/dist/types/transpiler/insertion/index.d.ts +0 -1
  280. package/dist/types/packages/@intlayer/core/dist/types/transpiler/insertion/insertion.d.ts +0 -1
  281. package/dist/types/packages/@intlayer/core/dist/types/transpiler/markdown/index.d.ts +0 -1
  282. package/dist/types/packages/@intlayer/core/dist/types/transpiler/markdown/markdown.d.ts +0 -1
  283. package/dist/types/packages/@intlayer/core/dist/types/transpiler/nesting/index.d.ts +0 -1
  284. package/dist/types/packages/@intlayer/core/dist/types/transpiler/nesting/nesting.d.ts +0 -4
  285. package/dist/types/packages/@intlayer/core/dist/types/transpiler/plural/index.d.ts +0 -1
  286. package/dist/types/packages/@intlayer/core/dist/types/transpiler/plural/plural.d.ts +0 -1
  287. package/dist/types/packages/@intlayer/core/dist/types/transpiler/select/index.d.ts +0 -1
  288. package/dist/types/packages/@intlayer/core/dist/types/transpiler/select/select.d.ts +0 -1
  289. package/dist/types/packages/@intlayer/core/dist/types/transpiler/translation/index.d.ts +0 -1
  290. package/dist/types/packages/@intlayer/core/dist/types/transpiler/translation/translation.d.ts +0 -2
  291. package/dist/types/packages/@intlayer/core/dist/types/utils/index.d.ts +0 -3
  292. package/dist/types/packages/@intlayer/core/dist/types/utils/intl.d.ts +0 -1
  293. package/dist/types/packages/@intlayer/core/dist/types/utils/isSameKeyPath.d.ts +0 -1
  294. package/dist/types/packages/@intlayer/core/dist/types/utils/localeStorage.d.ts +0 -2
  295. package/dist/types/packages/@intlayer/engine/dist/types/build.d.ts +0 -27
  296. package/dist/types/packages/@intlayer/engine/dist/types/buildIntlayerDictionary/buildIntlayerDictionary.d.ts +0 -6
  297. package/dist/types/packages/@intlayer/engine/dist/types/buildIntlayerDictionary/index.d.ts +0 -2
  298. package/dist/types/packages/@intlayer/engine/dist/types/buildIntlayerDictionary/processContentDeclaration.d.ts +0 -2
  299. package/dist/types/packages/@intlayer/engine/dist/types/buildIntlayerDictionary/writeDynamicDictionary.d.ts +0 -4
  300. package/dist/types/packages/@intlayer/engine/dist/types/buildIntlayerDictionary/writeMergedDictionary.d.ts +0 -3
  301. package/dist/types/packages/@intlayer/engine/dist/types/buildIntlayerDictionary/writeUnmergedDictionary.d.ts +0 -2
  302. package/dist/types/packages/@intlayer/engine/dist/types/cleanOutputDir.d.ts +0 -1
  303. package/dist/types/packages/@intlayer/engine/dist/types/createDictionaryEntryPoint/getBuiltDictionariesPath.d.ts +0 -1
  304. package/dist/types/packages/@intlayer/engine/dist/types/createDictionaryEntryPoint/getBuiltDynamicDictionariesPath.d.ts +0 -1
  305. package/dist/types/packages/@intlayer/engine/dist/types/createDictionaryEntryPoint/getBuiltFetchDictionariesPath.d.ts +0 -1
  306. package/dist/types/packages/@intlayer/engine/dist/types/createDictionaryEntryPoint/getBuiltRemoteDictionariesPath.d.ts +0 -1
  307. package/dist/types/packages/@intlayer/engine/dist/types/createDictionaryEntryPoint/getBuiltUnmergedDictionariesPath.d.ts +0 -1
  308. package/dist/types/packages/@intlayer/engine/dist/types/createDictionaryEntryPoint/index.d.ts +0 -5
  309. package/dist/types/packages/@intlayer/engine/dist/types/createType/createModuleAugmentation.d.ts +0 -1
  310. package/dist/types/packages/@intlayer/engine/dist/types/createType/createType.d.ts +0 -2
  311. package/dist/types/packages/@intlayer/engine/dist/types/createType/index.d.ts +0 -2
  312. package/dist/types/packages/@intlayer/engine/dist/types/formatDictionary.d.ts +0 -2
  313. package/dist/types/packages/@intlayer/engine/dist/types/loadDictionaries/index.d.ts +0 -4
  314. package/dist/types/packages/@intlayer/engine/dist/types/loadDictionaries/loadContentDeclaration.d.ts +0 -3
  315. package/dist/types/packages/@intlayer/engine/dist/types/loadDictionaries/loadDictionaries.d.ts +0 -2
  316. package/dist/types/packages/@intlayer/engine/dist/types/loadDictionaries/loadLocalDictionaries.d.ts +0 -2
  317. package/dist/types/packages/@intlayer/engine/dist/types/loadDictionaries/loadRemoteDictionaries.d.ts +0 -3
  318. package/dist/types/packages/@intlayer/engine/dist/types/prepareIntlayer.d.ts +0 -1
  319. package/dist/types/packages/@intlayer/engine/dist/types/writeConfiguration/index.d.ts +0 -1
  320. package/dist/types/packages/@intlayer/engine/dist/types/writeContentDeclaration/dictionaryStatus.d.ts +0 -6
  321. package/dist/types/packages/@intlayer/engine/dist/types/writeContentDeclaration/dictionaryStatus.d.ts.map +0 -1
  322. package/dist/types/packages/@intlayer/engine/dist/types/writeContentDeclaration/index.d.ts +0 -4
  323. package/dist/types/packages/@intlayer/engine/dist/types/writeContentDeclaration/transformJSFile.d.ts +0 -2
  324. package/dist/types/packages/@intlayer/engine/dist/types/writeContentDeclaration/writeContentDeclaration.d.ts +0 -4
  325. package/dist/types/packages/@intlayer/engine/dist/types/writeContentDeclaration/writeJSFile.d.ts +0 -2
  326. package/dist/types/packages/@intlayer/unmerged-dictionaries-entry/dist/types/index.d.ts +0 -9
  327. package/dist/types/packages/@intlayer/unmerged-dictionaries-entry/dist/types/index.d.ts.map +0 -1
  328. package/dist/types/packages/intlayer/dist/types/index.d.ts +0 -13
  329. package/dist/types/packages/intlayer-editor/server/dist/controllers/configuration.controller.d.ts +0 -9
  330. package/dist/types/packages/intlayer-editor/server/dist/controllers/configuration.controller.d.ts.map +0 -1
  331. package/dist/types/packages/intlayer-editor/server/dist/controllers/dictionary.controller.d.ts +0 -20
  332. package/dist/types/packages/intlayer-editor/server/dist/controllers/dictionary.controller.d.ts.map +0 -1
  333. package/dist/types/packages/intlayer-editor/server/dist/export.d.ts +0 -3
  334. package/dist/types/packages/intlayer-editor/server/dist/utils/httpStatusCodes.d.ts +0 -408
  335. package/dist/types/packages/intlayer-editor/server/dist/utils/httpStatusCodes.d.ts.map +0 -1
  336. package/dist/types/packages/intlayer-editor/server/dist/utils/responseData.d.ts +0 -19
  337. package/dist/types/packages/intlayer-editor/server/dist/utils/responseData.d.ts.map +0 -1
package/README.md CHANGED
@@ -214,6 +214,7 @@ Explore our comprehensive documentation to get started with Intlayer and learn h
214
214
  <li><a href="https://intlayer.org/doc/environment/vite-and-vue" rel=''>Vite + Vue</a></li>
215
215
  <li><a href="https://intlayer.org/doc/environment/nuxt-and-vue" rel=''>Vite + Nuxt</a></li>
216
216
  <li><a href="https://intlayer.org/doc/environment/vite-and-solid" rel=''>Vite + Solid</a></li>
217
+ <li><a href="https://intlayer.org/doc/environment/solid-start" rel=''>Solid Start</a></li>
217
218
  <li><a href="https://intlayer.org/doc/environment/angular" rel=''>Angular</a></li>
218
219
  <li>
219
220
  <a href="https://intlayer.org/doc/environment/express" rel=''>Backend</a>
@@ -223,7 +224,9 @@ Explore our comprehensive documentation to get started with Intlayer and learn h
223
224
  <li><a href="https://intlayer.org/doc/environment/fastify" rel=''>Fastify</a></li>
224
225
  <li><a href="https://intlayer.org/doc/environment/adonisjs" rel=''>AdonisJS</a></li>
225
226
  <li><a href="https://intlayer.org/doc/environment/hono" rel=''>Hono</a></li>
227
+ <li><a href="https://intlayer.org/doc/environment/elysia" rel=''>Elysia</a></li>
226
228
  </ul>
229
+
227
230
  </li>
228
231
  </ul>
229
232
  </details>
@@ -327,4 +330,4 @@ Contribute on [GitHub](https://github.com/aymericzip/intlayer), [GitLab](https:/
327
330
 
328
331
  If you like Intlayer, give us a ⭐ on GitHub. It helps others discover the project! [See why GitHub Stars matter](https://github.com/aymericzip/intlayer/blob/main/CONTRIBUTING.md#why-github-stars-matter-).
329
332
 
330
- [![Star History Chart](https://api.star-history.com/svg?repos=aymericzip/intlayer&type=Date)](https://star-history.com/#aymericzip/intlayer&Date)
333
+ [![Star History Chart](https://star-history.dera.page/svg?repos=aymericzip/intlayer&type=Date)](https://star-history.dera.page/#aymericzip/intlayer&Date)
@@ -1,10 +1,13 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
2
  const require_getIntlayerAPI_oAuth = require('../getIntlayerAPI/oAuth.cjs');
3
3
  let _intlayer_config_built = require("@intlayer/config/built");
4
+ let _intlayer_config_secrets = require("@intlayer/config/secrets");
4
5
  let defu = require("defu");
5
6
 
6
7
  //#region src/cms/createAuthManager.ts
7
8
  const ONE_MINUTE_MS = 6e4;
9
+ /** Whether the current runtime is a browser rather than a server. */
10
+ const isBrowser = () => typeof window !== "undefined";
8
11
  /**
9
12
  * Resolves the expiration timestamp (in ms since epoch) from an OAuth2
10
13
  * token-like payload, supporting both absolute dates and relative durations.
@@ -32,7 +35,10 @@ const getExpiryTimestamp = (token) => {
32
35
  */
33
36
  const createAuthManager = (intlayerConfig = { editor: _intlayer_config_built.editor }, options = {}) => {
34
37
  const { baseFetcherOptions = {}, sessionToken } = options;
35
- const resolvedConfig = (0, defu.defu)(intlayerConfig ?? {}, { editor: _intlayer_config_built.editor });
38
+ const resolvedConfig = (0, defu.defu)(intlayerConfig ?? {}, { editor: {
39
+ ..._intlayer_config_built.editor,
40
+ clientSecret: _intlayer_config_built.editor?.clientId ? (0, _intlayer_config_secrets.getEditorClientSecret)() : void 0
41
+ } });
36
42
  const fetcherOptions = {
37
43
  ...baseFetcherOptions,
38
44
  credentials: "omit"
@@ -78,6 +84,9 @@ const createAuthManager = (intlayerConfig = { editor: _intlayer_config_built.edi
78
84
  expiryTimestamp = void 0;
79
85
  };
80
86
  const withAuth = (method, skipAuth = !hasCredentials) => {
87
+ if (skipAuth && !sessionToken && isBrowser()) return async () => {
88
+ throw new Error("Intlayer CMS credentials are not available in the browser. `clientSecret` is a server-side credential and is never bundled. Call this from a server route, or pass a user session token: createIntlayerCMS(config, { sessionToken }).");
89
+ };
81
90
  if (skipAuth) return method;
82
91
  return async (...args) => {
83
92
  await ensureAuth();
@@ -1 +1 @@
1
- {"version":3,"file":"createAuthManager.cjs","names":["getOAuthAPI"],"sources":["../../../src/cms/createAuthManager.ts"],"sourcesContent":["import { editor } from '@intlayer/config/built';\nimport type { IntlayerConfig } from '@intlayer/types/config';\nimport { defu } from 'defu';\nimport type { FetcherOptions } from '../fetcher';\nimport { getOAuthAPI } from '../getIntlayerAPI/oAuth';\n\n/**\n * Shape of the various OAuth2 token payloads the backend may return. Only the\n * fields used to derive the expiration timestamp are described here.\n */\ntype OAuthTokenLike = {\n accessToken?: string;\n accessTokenExpiresAt?: string | Date;\n expires_in?: number;\n expiresIn?: number;\n expiresAt?: string | Date;\n};\n\n/**\n * Any asynchronous API method exposed by a domain section\n * (e.g. `getDictionaries`, `pushDictionaries`).\n */\ntype AsyncMethod<Arguments extends unknown[], Result> = (\n ...args: Arguments\n) => Promise<Result>;\n\n/**\n * A domain section factory: builds a plain object of async methods from shared\n * fetcher options and the Intlayer configuration.\n */\ntype SectionFactory<Section extends Record<string, unknown>> = (\n fetcherOptions: FetcherOptions,\n intlayerConfig: IntlayerConfig\n) => Section;\n\nconst ONE_MINUTE_MS = 60_000;\n\n/**\n * Resolves the expiration timestamp (in ms since epoch) from an OAuth2\n * token-like payload, supporting both absolute dates and relative durations.\n */\nconst getExpiryTimestamp = (\n token: OAuthTokenLike | undefined\n): number | undefined => {\n if (!token) return undefined;\n\n const dateLike = (token.accessTokenExpiresAt ?? token.expiresAt) as\n | string\n | Date\n | undefined;\n\n if (dateLike) {\n const timestamp =\n typeof dateLike === 'string'\n ? Date.parse(dateLike)\n : dateLike.getTime?.();\n if (typeof timestamp === 'number' && Number.isFinite(timestamp)) {\n return timestamp;\n }\n }\n\n const seconds = token.expires_in ?? token.expiresIn;\n if (typeof seconds === 'number' && Number.isFinite(seconds)) {\n return Date.now() + seconds * 1000;\n }\n\n return undefined;\n};\n\n/**\n * Options accepted when creating an {@link AuthManager}.\n */\nexport type AuthManagerOptions = {\n /**\n * Base fetcher options merged into every request (e.g. custom headers).\n */\n baseFetcherOptions?: FetcherOptions;\n /**\n * A pre-issued CLI session token (prefixed with `clisession_`). When provided,\n * it is used directly as the Bearer token and the OAuth2 `client_credentials`\n * exchange is skipped entirely.\n */\n sessionToken?: string;\n};\n\n/**\n * Options accepted when binding a domain section to an {@link AuthManager}.\n */\nexport type BindSectionOptions = {\n /**\n * Forces auth injection on (`false`) or off (`true`). Defaults to enabled\n * whenever credentials are available.\n */\n skipAuth?: boolean;\n};\n\n/**\n * Manages CMS authentication for a set of API calls.\n *\n * The manager owns a single mutable {@link FetcherOptions} object\n * (`fetcherOptions`). Domain factories bound through it share the same\n * reference, so refreshing the token in place transparently authenticates every\n * call — without rebuilding the API clients.\n */\nexport type AuthManager = {\n /**\n * Mutable fetcher options carrying the `Authorization` header. Bound sections\n * share this reference so the injected token is reused everywhere.\n */\n fetcherOptions: FetcherOptions;\n /**\n * The resolved Intlayer configuration (merged with the build-time defaults).\n */\n intlayerConfig: IntlayerConfig;\n /**\n * `true` when credentials are available — either a session token or a\n * `clientId`/`clientSecret` pair in the configuration.\n */\n hasCredentials: boolean;\n /**\n * Ensures a valid access token is available and applies it to\n * `fetcherOptions`. Called automatically by {@link withAuth}.\n */\n ensureAuth: () => Promise<void>;\n /**\n * Forces a token refresh on the next call by clearing the cached token.\n */\n reset: () => void;\n /**\n * Wraps a single API method so it injects (and, on failure, refreshes) the\n * access token on every invocation while preserving the method's signature.\n */\n withAuth: <Arguments extends unknown[], Result>(\n method: AsyncMethod<Arguments, Result>,\n skipAuth?: boolean\n ) => AsyncMethod<Arguments, Result>;\n /**\n * Instantiates a domain factory with the managed fetcher options and config,\n * returning a section whose every method authenticates automatically.\n *\n * @example\n * ```ts\n * const dictionary = authManager.bindSection(getDictionaryAPI);\n * await dictionary.pushDictionaries(dictionaries);\n * ```\n */\n bindSection: <Section extends Record<string, unknown>>(\n factory: SectionFactory<Section>,\n options?: BindSectionOptions\n ) => Section;\n};\n\n/**\n * Creates an instance-scoped {@link AuthManager} handling the OAuth2\n * `client_credentials` flow (or a pre-issued CLI session token).\n *\n * Unlike a global token cache, each manager keeps its own token state, so\n * independent SDK instances never interfere with one another.\n *\n * @param intlayerConfig - Intlayer configuration carrying the `editor`\n * credentials. Defaults to the build-time configuration.\n * @param options - Optional session token and base fetcher options.\n */\nexport const createAuthManager = (\n intlayerConfig: Pick<IntlayerConfig, 'editor'> = { editor },\n options: AuthManagerOptions = {}\n): AuthManager => {\n const { baseFetcherOptions = {}, sessionToken } = options;\n\n const resolvedConfig = defu(intlayerConfig ?? {}, {\n editor,\n }) as IntlayerConfig;\n\n // `credentials: 'omit'` prevents the browser from attaching session cookies;\n // authentication is handled exclusively through the injected Bearer token.\n // This is required because the backend only allows credentialed requests for\n // whitelisted first-party origins.\n const fetcherOptions: FetcherOptions = {\n ...baseFetcherOptions,\n credentials: 'omit',\n };\n\n const hasCredentials =\n Boolean(sessionToken) ||\n Boolean(\n resolvedConfig?.editor?.clientId && resolvedConfig?.editor?.clientSecret\n );\n\n // A session token is used as-is and never expires within the SDK's concern,\n // so inject it once up-front and skip the OAuth2 exchange.\n if (sessionToken) {\n fetcherOptions.headers = {\n ...(fetcherOptions.headers ?? {}),\n Authorization: `Bearer ${sessionToken}`,\n } as HeadersInit;\n }\n\n let accessToken: string | undefined;\n let expiryTimestamp: number | undefined;\n let pendingRefresh: Promise<void> | undefined;\n\n const needsRefresh = (): boolean => {\n if (!accessToken) return true;\n if (!expiryTimestamp) return false; // Unknown expiry: assume usable until failure.\n\n // Refresh one minute before the actual expiry to avoid edge-of-expiry races.\n return Date.now() + ONE_MINUTE_MS >= expiryTimestamp;\n };\n\n const refreshToken = async (): Promise<void> => {\n const doRefresh = async (): Promise<void> => {\n const oAuthAPI = getOAuthAPI({}, resolvedConfig);\n const response = await oAuthAPI.getOAuth2AccessToken();\n const tokenData = response?.data as OAuthTokenLike | undefined;\n\n accessToken = tokenData?.accessToken;\n expiryTimestamp = getExpiryTimestamp(tokenData);\n };\n\n // De-duplicate concurrent refreshes so a burst of calls triggers one fetch.\n pendingRefresh ??= doRefresh().finally(() => {\n pendingRefresh = undefined;\n });\n\n await pendingRefresh;\n };\n\n const applyAuthHeader = (): void => {\n if (!accessToken) return;\n fetcherOptions.headers = {\n ...(fetcherOptions.headers ?? {}),\n Authorization: `Bearer ${accessToken}`,\n } as HeadersInit;\n };\n\n const ensureAuth = async (): Promise<void> => {\n if (sessionToken) return; // Already injected; nothing to refresh.\n if (needsRefresh()) {\n await refreshToken();\n }\n applyAuthHeader();\n };\n\n const reset = (): void => {\n accessToken = undefined;\n expiryTimestamp = undefined;\n };\n\n const withAuth = <Arguments extends unknown[], Result>(\n method: AsyncMethod<Arguments, Result>,\n skipAuth = !hasCredentials\n ): AsyncMethod<Arguments, Result> => {\n if (skipAuth) return method;\n\n return async (...args: Arguments): Promise<Result> => {\n await ensureAuth();\n\n try {\n return await method(...args);\n } catch (error) {\n // The token may have been revoked or expired early: refresh once and\n // retry before surfacing the error. A session token cannot be\n // refreshed, so re-throw immediately in that case.\n if (sessionToken) throw error;\n\n await refreshToken();\n applyAuthHeader();\n return await method(...args);\n }\n };\n };\n\n const bindSection = <Section extends Record<string, unknown>>(\n factory: SectionFactory<Section>,\n bindOptions: BindSectionOptions = {}\n ): Section => {\n const section = factory(fetcherOptions, resolvedConfig);\n const skipAuth = bindOptions.skipAuth ?? !hasCredentials;\n\n return new Proxy(section, {\n get(target, property, receiver) {\n const value = Reflect.get(target, property, receiver);\n\n if (typeof value !== 'function') return value;\n\n return withAuth(\n (value as AsyncMethod<unknown[], unknown>).bind(target),\n skipAuth\n );\n },\n });\n };\n\n return {\n fetcherOptions,\n intlayerConfig: resolvedConfig,\n hasCredentials,\n ensureAuth,\n reset,\n withAuth,\n bindSection,\n };\n};\n"],"mappings":";;;;;;AAmCA,MAAM,gBAAgB;;;;;AAMtB,MAAM,sBACJ,UACuB;CACvB,IAAI,CAAC,OAAO,OAAO;CAEnB,MAAM,WAAY,MAAM,wBAAwB,MAAM;CAKtD,IAAI,UAAU;EACZ,MAAM,YACJ,OAAO,aAAa,WAChB,KAAK,MAAM,QAAQ,IACnB,SAAS,UAAU;EACzB,IAAI,OAAO,cAAc,YAAY,OAAO,SAAS,SAAS,GAC5D,OAAO;CAEX;CAEA,MAAM,UAAU,MAAM,cAAc,MAAM;CAC1C,IAAI,OAAO,YAAY,YAAY,OAAO,SAAS,OAAO,GACxD,OAAO,KAAK,IAAI,IAAI,UAAU;AAIlC;;;;;;;;;;;;AAgGA,MAAa,qBACX,iBAAiD,EAAE,sCAAO,GAC1D,UAA8B,CAAC,MACf;CAChB,MAAM,EAAE,qBAAqB,CAAC,GAAG,iBAAiB;CAElD,MAAM,qBAAiB,WAAK,kBAAkB,CAAC,GAAG,EAChD,sCACF,CAAC;CAMD,MAAM,iBAAiC;EACrC,GAAG;EACH,aAAa;CACf;CAEA,MAAM,iBACJ,QAAQ,YAAY,KACpB,QACE,gBAAgB,QAAQ,YAAY,gBAAgB,QAAQ,YAC9D;CAIF,IAAI,cACF,eAAe,UAAU;EACvB,GAAI,eAAe,WAAW,CAAC;EAC/B,eAAe,UAAU;CAC3B;CAGF,IAAI;CACJ,IAAI;CACJ,IAAI;CAEJ,MAAM,qBAA8B;EAClC,IAAI,CAAC,aAAa,OAAO;EACzB,IAAI,CAAC,iBAAiB,OAAO;EAG7B,OAAO,KAAK,IAAI,IAAI,iBAAiB;CACvC;CAEA,MAAM,eAAe,YAA2B;EAC9C,MAAM,YAAY,YAA2B;GAG3C,MAAM,aAAY,MAFDA,yCAAY,CAAC,GAAG,cACH,CAAC,CAAC,qBAAqB,EAC3B,EAAE;GAE5B,cAAc,WAAW;GACzB,kBAAkB,mBAAmB,SAAS;EAChD;EAGA,mBAAmB,UAAU,CAAC,CAAC,cAAc;GAC3C,iBAAiB;EACnB,CAAC;EAED,MAAM;CACR;CAEA,MAAM,wBAA8B;EAClC,IAAI,CAAC,aAAa;EAClB,eAAe,UAAU;GACvB,GAAI,eAAe,WAAW,CAAC;GAC/B,eAAe,UAAU;EAC3B;CACF;CAEA,MAAM,aAAa,YAA2B;EAC5C,IAAI,cAAc;EAClB,IAAI,aAAa,GACf,MAAM,aAAa;EAErB,gBAAgB;CAClB;CAEA,MAAM,cAAoB;EACxB,cAAc;EACd,kBAAkB;CACpB;CAEA,MAAM,YACJ,QACA,WAAW,CAAC,mBACuB;EACnC,IAAI,UAAU,OAAO;EAErB,OAAO,OAAO,GAAG,SAAqC;GACpD,MAAM,WAAW;GAEjB,IAAI;IACF,OAAO,MAAM,OAAO,GAAG,IAAI;GAC7B,SAAS,OAAO;IAId,IAAI,cAAc,MAAM;IAExB,MAAM,aAAa;IACnB,gBAAgB;IAChB,OAAO,MAAM,OAAO,GAAG,IAAI;GAC7B;EACF;CACF;CAEA,MAAM,eACJ,SACA,cAAkC,CAAC,MACvB;EACZ,MAAM,UAAU,QAAQ,gBAAgB,cAAc;EACtD,MAAM,WAAW,YAAY,YAAY,CAAC;EAE1C,OAAO,IAAI,MAAM,SAAS,EACxB,IAAI,QAAQ,UAAU,UAAU;GAC9B,MAAM,QAAQ,QAAQ,IAAI,QAAQ,UAAU,QAAQ;GAEpD,IAAI,OAAO,UAAU,YAAY,OAAO;GAExC,OAAO,SACJ,MAA0C,KAAK,MAAM,GACtD,QACF;EACF,EACF,CAAC;CACH;CAEA,OAAO;EACL;EACA,gBAAgB;EAChB;EACA;EACA;EACA;EACA;CACF;AACF"}
1
+ {"version":3,"file":"createAuthManager.cjs","names":["editor","getEditorClientSecret","getOAuthAPI"],"sources":["../../../src/cms/createAuthManager.ts"],"sourcesContent":["import { editor } from '@intlayer/config/built';\nimport { getEditorClientSecret } from '@intlayer/config/secrets';\nimport type { IntlayerConfig } from '@intlayer/types/config';\nimport { defu } from 'defu';\nimport type { FetcherOptions } from '../fetcher';\nimport { getOAuthAPI } from '../getIntlayerAPI/oAuth';\n\n/**\n * Shape of the various OAuth2 token payloads the backend may return. Only the\n * fields used to derive the expiration timestamp are described here.\n */\ntype OAuthTokenLike = {\n accessToken?: string;\n accessTokenExpiresAt?: string | Date;\n expires_in?: number;\n expiresIn?: number;\n expiresAt?: string | Date;\n};\n\n/**\n * Any asynchronous API method exposed by a domain section\n * (e.g. `getDictionaries`, `pushDictionaries`).\n */\ntype AsyncMethod<Arguments extends unknown[], Result> = (\n ...args: Arguments\n) => Promise<Result>;\n\n/**\n * A domain section factory: builds a plain object of async methods from shared\n * fetcher options and the Intlayer configuration.\n */\ntype SectionFactory<Section extends Record<string, unknown>> = (\n fetcherOptions: FetcherOptions,\n intlayerConfig: IntlayerConfig\n) => Section;\n\nconst ONE_MINUTE_MS = 60_000;\n\n/** Whether the current runtime is a browser rather than a server. */\nconst isBrowser = (): boolean => typeof window !== 'undefined';\n\n/**\n * Resolves the expiration timestamp (in ms since epoch) from an OAuth2\n * token-like payload, supporting both absolute dates and relative durations.\n */\nconst getExpiryTimestamp = (\n token: OAuthTokenLike | undefined\n): number | undefined => {\n if (!token) return undefined;\n\n const dateLike = (token.accessTokenExpiresAt ?? token.expiresAt) as\n | string\n | Date\n | undefined;\n\n if (dateLike) {\n const timestamp =\n typeof dateLike === 'string'\n ? Date.parse(dateLike)\n : dateLike.getTime?.();\n if (typeof timestamp === 'number' && Number.isFinite(timestamp)) {\n return timestamp;\n }\n }\n\n const seconds = token.expires_in ?? token.expiresIn;\n if (typeof seconds === 'number' && Number.isFinite(seconds)) {\n return Date.now() + seconds * 1000;\n }\n\n return undefined;\n};\n\n/**\n * Options accepted when creating an {@link AuthManager}.\n */\nexport type AuthManagerOptions = {\n /**\n * Base fetcher options merged into every request (e.g. custom headers).\n */\n baseFetcherOptions?: FetcherOptions;\n /**\n * A pre-issued CLI session token (prefixed with `clisession_`). When provided,\n * it is used directly as the Bearer token and the OAuth2 `client_credentials`\n * exchange is skipped entirely.\n */\n sessionToken?: string;\n};\n\n/**\n * Options accepted when binding a domain section to an {@link AuthManager}.\n */\nexport type BindSectionOptions = {\n /**\n * Forces auth injection on (`false`) or off (`true`). Defaults to enabled\n * whenever credentials are available.\n */\n skipAuth?: boolean;\n};\n\n/**\n * Manages CMS authentication for a set of API calls.\n *\n * The manager owns a single mutable {@link FetcherOptions} object\n * (`fetcherOptions`). Domain factories bound through it share the same\n * reference, so refreshing the token in place transparently authenticates every\n * call — without rebuilding the API clients.\n */\nexport type AuthManager = {\n /**\n * Mutable fetcher options carrying the `Authorization` header. Bound sections\n * share this reference so the injected token is reused everywhere.\n */\n fetcherOptions: FetcherOptions;\n /**\n * The resolved Intlayer configuration (merged with the build-time defaults).\n */\n intlayerConfig: IntlayerConfig;\n /**\n * `true` when credentials are available — either a session token or a\n * `clientId`/`clientSecret` pair in the configuration.\n */\n hasCredentials: boolean;\n /**\n * Ensures a valid access token is available and applies it to\n * `fetcherOptions`. Called automatically by {@link withAuth}.\n */\n ensureAuth: () => Promise<void>;\n /**\n * Forces a token refresh on the next call by clearing the cached token.\n */\n reset: () => void;\n /**\n * Wraps a single API method so it injects (and, on failure, refreshes) the\n * access token on every invocation while preserving the method's signature.\n */\n withAuth: <Arguments extends unknown[], Result>(\n method: AsyncMethod<Arguments, Result>,\n skipAuth?: boolean\n ) => AsyncMethod<Arguments, Result>;\n /**\n * Instantiates a domain factory with the managed fetcher options and config,\n * returning a section whose every method authenticates automatically.\n *\n * @example\n * ```ts\n * const dictionary = authManager.bindSection(getDictionaryAPI);\n * await dictionary.pushDictionaries(dictionaries);\n * ```\n */\n bindSection: <Section extends Record<string, unknown>>(\n factory: SectionFactory<Section>,\n options?: BindSectionOptions\n ) => Section;\n};\n\n/**\n * Creates an instance-scoped {@link AuthManager} handling the OAuth2\n * `client_credentials` flow (or a pre-issued CLI session token).\n *\n * Unlike a global token cache, each manager keeps its own token state, so\n * independent SDK instances never interfere with one another.\n *\n * @param intlayerConfig - Intlayer configuration carrying the `editor`\n * credentials. Defaults to the build-time configuration.\n * @param options - Optional session token and base fetcher options.\n */\nexport const createAuthManager = (\n intlayerConfig: Pick<IntlayerConfig, 'editor'> = { editor },\n options: AuthManagerOptions = {}\n): AuthManager => {\n const { baseFetcherOptions = {}, sessionToken } = options;\n\n // `editor` comes from the generated build-time configuration, which never\n // carries `clientSecret` — it would be inlined into client bundles. The\n // secret is supplied server-side by `@intlayer/config/secrets`, which\n // resolves to an inert stub in the browser; `hasCredentials` then stays false\n // there and the OAuth2 exchange never runs.\n //\n // Only consulted when the configuration declares a `clientId`, so a project\n // that comments its credentials out stays uncredentialed even with the\n // environment variables still defined.\n const resolvedConfig = defu(intlayerConfig ?? {}, {\n editor: {\n ...editor,\n clientSecret: editor?.clientId ? getEditorClientSecret() : undefined,\n },\n }) as IntlayerConfig;\n\n // `credentials: 'omit'` prevents the browser from attaching session cookies;\n // authentication is handled exclusively through the injected Bearer token.\n // This is required because the backend only allows credentialed requests for\n // whitelisted first-party origins.\n const fetcherOptions: FetcherOptions = {\n ...baseFetcherOptions,\n credentials: 'omit',\n };\n\n const hasCredentials =\n Boolean(sessionToken) ||\n Boolean(\n resolvedConfig?.editor?.clientId && resolvedConfig?.editor?.clientSecret\n );\n\n // A session token is used as-is and never expires within the SDK's concern,\n // so inject it once up-front and skip the OAuth2 exchange.\n if (sessionToken) {\n fetcherOptions.headers = {\n ...(fetcherOptions.headers ?? {}),\n Authorization: `Bearer ${sessionToken}`,\n } as HeadersInit;\n }\n\n let accessToken: string | undefined;\n let expiryTimestamp: number | undefined;\n let pendingRefresh: Promise<void> | undefined;\n\n const needsRefresh = (): boolean => {\n if (!accessToken) return true;\n if (!expiryTimestamp) return false; // Unknown expiry: assume usable until failure.\n\n // Refresh one minute before the actual expiry to avoid edge-of-expiry races.\n return Date.now() + ONE_MINUTE_MS >= expiryTimestamp;\n };\n\n const refreshToken = async (): Promise<void> => {\n const doRefresh = async (): Promise<void> => {\n const oAuthAPI = getOAuthAPI({}, resolvedConfig);\n const response = await oAuthAPI.getOAuth2AccessToken();\n const tokenData = response?.data as OAuthTokenLike | undefined;\n\n accessToken = tokenData?.accessToken;\n expiryTimestamp = getExpiryTimestamp(tokenData);\n };\n\n // De-duplicate concurrent refreshes so a burst of calls triggers one fetch.\n pendingRefresh ??= doRefresh().finally(() => {\n pendingRefresh = undefined;\n });\n\n await pendingRefresh;\n };\n\n const applyAuthHeader = (): void => {\n if (!accessToken) return;\n fetcherOptions.headers = {\n ...(fetcherOptions.headers ?? {}),\n Authorization: `Bearer ${accessToken}`,\n } as HeadersInit;\n };\n\n const ensureAuth = async (): Promise<void> => {\n if (sessionToken) return; // Already injected; nothing to refresh.\n if (needsRefresh()) {\n await refreshToken();\n }\n applyAuthHeader();\n };\n\n const reset = (): void => {\n accessToken = undefined;\n expiryTimestamp = undefined;\n };\n\n const withAuth = <Arguments extends unknown[], Result>(\n method: AsyncMethod<Arguments, Result>,\n skipAuth = !hasCredentials\n ): AsyncMethod<Arguments, Result> => {\n // In a browser the project credentials are structurally unavailable, so an\n // authenticated call would silently go out unauthenticated and come back\n // 401. Fail with an actionable message instead: a browser must authenticate\n // as the signed-in *user* (a session token, or the session cookie on a\n // first-party origin), never as the project.\n if (skipAuth && !sessionToken && isBrowser()) {\n return async () => {\n throw new Error(\n 'Intlayer CMS credentials are not available in the browser. ' +\n '`clientSecret` is a server-side credential and is never bundled. ' +\n 'Call this from a server route, or pass a user session token: ' +\n 'createIntlayerCMS(config, { sessionToken }).'\n );\n };\n }\n\n if (skipAuth) return method;\n\n return async (...args: Arguments): Promise<Result> => {\n await ensureAuth();\n\n try {\n return await method(...args);\n } catch (error) {\n // The token may have been revoked or expired early: refresh once and\n // retry before surfacing the error. A session token cannot be\n // refreshed, so re-throw immediately in that case.\n if (sessionToken) throw error;\n\n await refreshToken();\n applyAuthHeader();\n return await method(...args);\n }\n };\n };\n\n const bindSection = <Section extends Record<string, unknown>>(\n factory: SectionFactory<Section>,\n bindOptions: BindSectionOptions = {}\n ): Section => {\n const section = factory(fetcherOptions, resolvedConfig);\n const skipAuth = bindOptions.skipAuth ?? !hasCredentials;\n\n return new Proxy(section, {\n get(target, property, receiver) {\n const value = Reflect.get(target, property, receiver);\n\n if (typeof value !== 'function') return value;\n\n return withAuth(\n (value as AsyncMethod<unknown[], unknown>).bind(target),\n skipAuth\n );\n },\n });\n };\n\n return {\n fetcherOptions,\n intlayerConfig: resolvedConfig,\n hasCredentials,\n ensureAuth,\n reset,\n withAuth,\n bindSection,\n };\n};\n"],"mappings":";;;;;;;AAoCA,MAAM,gBAAgB;;AAGtB,MAAM,kBAA2B,OAAO,WAAW;;;;;AAMnD,MAAM,sBACJ,UACuB;CACvB,IAAI,CAAC,OAAO,OAAO;CAEnB,MAAM,WAAY,MAAM,wBAAwB,MAAM;CAKtD,IAAI,UAAU;EACZ,MAAM,YACJ,OAAO,aAAa,WAChB,KAAK,MAAM,QAAQ,IACnB,SAAS,UAAU;EACzB,IAAI,OAAO,cAAc,YAAY,OAAO,SAAS,SAAS,GAC5D,OAAO;CAEX;CAEA,MAAM,UAAU,MAAM,cAAc,MAAM;CAC1C,IAAI,OAAO,YAAY,YAAY,OAAO,SAAS,OAAO,GACxD,OAAO,KAAK,IAAI,IAAI,UAAU;AAIlC;;;;;;;;;;;;AAgGA,MAAa,qBACX,iBAAiD,EAAE,sCAAO,GAC1D,UAA8B,CAAC,MACf;CAChB,MAAM,EAAE,qBAAqB,CAAC,GAAG,iBAAiB;CAWlD,MAAM,qBAAiB,WAAK,kBAAkB,CAAC,GAAG,EAChD,QAAQ;EACN,GAAGA;EACH,cAAcA,+BAAQ,eAAWC,gDAAsB,IAAI;CAC7D,EACF,CAAC;CAMD,MAAM,iBAAiC;EACrC,GAAG;EACH,aAAa;CACf;CAEA,MAAM,iBACJ,QAAQ,YAAY,KACpB,QACE,gBAAgB,QAAQ,YAAY,gBAAgB,QAAQ,YAC9D;CAIF,IAAI,cACF,eAAe,UAAU;EACvB,GAAI,eAAe,WAAW,CAAC;EAC/B,eAAe,UAAU;CAC3B;CAGF,IAAI;CACJ,IAAI;CACJ,IAAI;CAEJ,MAAM,qBAA8B;EAClC,IAAI,CAAC,aAAa,OAAO;EACzB,IAAI,CAAC,iBAAiB,OAAO;EAG7B,OAAO,KAAK,IAAI,IAAI,iBAAiB;CACvC;CAEA,MAAM,eAAe,YAA2B;EAC9C,MAAM,YAAY,YAA2B;GAG3C,MAAM,aAAY,MAFDC,yCAAY,CAAC,GAAG,cACH,CAAC,CAAC,qBAAqB,EAC3B,EAAE;GAE5B,cAAc,WAAW;GACzB,kBAAkB,mBAAmB,SAAS;EAChD;EAGA,mBAAmB,UAAU,CAAC,CAAC,cAAc;GAC3C,iBAAiB;EACnB,CAAC;EAED,MAAM;CACR;CAEA,MAAM,wBAA8B;EAClC,IAAI,CAAC,aAAa;EAClB,eAAe,UAAU;GACvB,GAAI,eAAe,WAAW,CAAC;GAC/B,eAAe,UAAU;EAC3B;CACF;CAEA,MAAM,aAAa,YAA2B;EAC5C,IAAI,cAAc;EAClB,IAAI,aAAa,GACf,MAAM,aAAa;EAErB,gBAAgB;CAClB;CAEA,MAAM,cAAoB;EACxB,cAAc;EACd,kBAAkB;CACpB;CAEA,MAAM,YACJ,QACA,WAAW,CAAC,mBACuB;EAMnC,IAAI,YAAY,CAAC,gBAAgB,UAAU,GACzC,OAAO,YAAY;GACjB,MAAM,IAAI,MACR,uOAIF;EACF;EAGF,IAAI,UAAU,OAAO;EAErB,OAAO,OAAO,GAAG,SAAqC;GACpD,MAAM,WAAW;GAEjB,IAAI;IACF,OAAO,MAAM,OAAO,GAAG,IAAI;GAC7B,SAAS,OAAO;IAId,IAAI,cAAc,MAAM;IAExB,MAAM,aAAa;IACnB,gBAAgB;IAChB,OAAO,MAAM,OAAO,GAAG,IAAI;GAC7B;EACF;CACF;CAEA,MAAM,eACJ,SACA,cAAkC,CAAC,MACvB;EACZ,MAAM,UAAU,QAAQ,gBAAgB,cAAc;EACtD,MAAM,WAAW,YAAY,YAAY,CAAC;EAE1C,OAAO,IAAI,MAAM,SAAS,EACxB,IAAI,QAAQ,UAAU,UAAU;GAC9B,MAAM,QAAQ,QAAQ,IAAI,QAAQ,UAAU,QAAQ;GAEpD,IAAI,OAAO,UAAU,YAAY,OAAO;GAExC,OAAO,SACJ,MAA0C,KAAK,MAAM,GACtD,QACF;EACF,EACF,CAAC;CACH;CAEA,OAAO;EACL;EACA,gBAAgB;EAChB;EACA;EACA;EACA;EACA;CACF;AACF"}
@@ -23,13 +23,14 @@ const getAnalyticsAPI = (authAPIOptions = {}, intlayerConfig) => {
23
23
  const getOverview = async (otherOptions = {}) => await require_fetcher.fetcher(`${ANALYTICS_API_ROUTE}/overview`, authAPIOptions, otherOptions, { method: "GET" });
24
24
  /**
25
25
  * Audience report: distinct visitors (today / 7d / window), page views, the
26
- * daily evolution series, and locale + country breakdowns.
27
- * @param days - Rolling window size in days (default 30).
26
+ * evolution series, and locale + country breakdowns.
27
+ * @param range - Rolling window, either a named range (`1h`, `24h`, `7d`,
28
+ * `30d`, `90d`, `6mo`, `1y`, `3y`) or a number of days (default 30).
28
29
  * @returns The audience statistics.
29
30
  */
30
- const getAudience = async (days = 30, otherOptions = {}) => await require_fetcher.fetcher(`${ANALYTICS_API_ROUTE}/audience`, authAPIOptions, otherOptions, {
31
+ const getAudience = async (range = 30, otherOptions = {}) => await require_fetcher.fetcher(`${ANALYTICS_API_ROUTE}/audience`, authAPIOptions, otherOptions, {
31
32
  method: "GET",
32
- params: { days: String(days) }
33
+ params: typeof range === "number" ? { days: String(range) } : { range }
33
34
  });
34
35
  /**
35
36
  * Per-content exposure totals — "which content is actually shown".
@@ -1 +1 @@
1
- {"version":3,"file":"analytics.cjs","names":["editor","fetcher","createEndpoint"],"sources":["../../../src/getIntlayerAPI/analytics.ts"],"sourcesContent":["import type {\n GetAnalyticsOverviewResult,\n GetAudienceResult,\n GetContentStatsResult,\n GetExperimentResultsResult,\n IngestAnalyticsBody,\n IngestAnalyticsResult,\n} from '@intlayer/backend';\nimport { editor } from '@intlayer/config/built';\nimport type { IntlayerConfig } from '@intlayer/types/config';\nimport { createEndpoint } from '../cms/createIntlayerCMS';\nimport { type FetcherOptions, fetcher } from '../fetcher';\n\nexport const getAnalyticsAPI = (\n authAPIOptions: FetcherOptions = {},\n intlayerConfig?: IntlayerConfig\n) => {\n const backendURL = intlayerConfig?.editor?.backendURL ?? editor.backendURL;\n\n const ANALYTICS_API_ROUTE = `${backendURL}/api/analytics`;\n\n /**\n * Ingest a batch of analytics events. Public — attribution is by the\n * project's `clientId` in the body.\n * @param body - Session id, sdk version, and the collected events.\n * @returns The number of accepted events.\n */\n const sendEvents = async (\n body: IngestAnalyticsBody,\n otherOptions: FetcherOptions = {}\n ) =>\n await fetcher<IngestAnalyticsResult>(\n `${ANALYTICS_API_ROUTE}/events`,\n authAPIOptions,\n otherOptions,\n {\n method: 'POST',\n body,\n }\n );\n\n /**\n * Page/locale totals for the authenticated project.\n * @returns Views grouped by url and locale.\n */\n const getOverview = async (otherOptions: FetcherOptions = {}) =>\n await fetcher<GetAnalyticsOverviewResult>(\n `${ANALYTICS_API_ROUTE}/overview`,\n authAPIOptions,\n otherOptions,\n { method: 'GET' }\n );\n\n /**\n * Audience report: distinct visitors (today / 7d / window), page views, the\n * daily evolution series, and locale + country breakdowns.\n * @param days - Rolling window size in days (default 30).\n * @returns The audience statistics.\n */\n const getAudience = async (days = 30, otherOptions: FetcherOptions = {}) =>\n await fetcher<GetAudienceResult>(\n `${ANALYTICS_API_ROUTE}/audience`,\n authAPIOptions,\n otherOptions,\n {\n method: 'GET',\n params: { days: String(days) },\n }\n );\n\n /**\n * Per-content exposure totals — \"which content is actually shown\".\n * @returns Exposures grouped by dictionary key, key path, and locale.\n */\n const getContentStats = async (otherOptions: FetcherOptions = {}) =>\n await fetcher<GetContentStatsResult>(\n `${ANALYTICS_API_ROUTE}/content-stats`,\n authAPIOptions,\n otherOptions,\n { method: 'GET' }\n );\n\n /**\n * A/B experiment results with per-variant conversion rates and significance.\n * @param experimentKey - The experiment to evaluate.\n * @returns The experiment result set.\n */\n const getExperimentResults = async (\n experimentKey: string,\n otherOptions: FetcherOptions = {}\n ) =>\n await fetcher<GetExperimentResultsResult>(\n `${ANALYTICS_API_ROUTE}/experiments/${experimentKey}`,\n authAPIOptions,\n otherOptions,\n { method: 'GET' }\n );\n\n return {\n sendEvents,\n getOverview,\n getAudience,\n getContentStats,\n getExperimentResults,\n };\n};\n\n/**\n * Authenticated `analytics` endpoint bound to an Intlayer CMS authenticator.\n *\n * Pass an authenticator created with `createIntlayerCMS`, or omit it to use\n * the build-time configuration (`@intlayer/config/built`).\n */\nexport const analyticsEndpoint = createEndpoint(getAnalyticsAPI);\n"],"mappings":";;;;;;AAaA,MAAa,mBACX,iBAAiC,CAAC,GAClC,mBACG;CAGH,MAAM,sBAAsB,GAFT,gBAAgB,QAAQ,cAAcA,8BAAO,WAEtB;;;;;;;CAQ1C,MAAM,aAAa,OACjB,MACA,eAA+B,CAAC,MAEhC,MAAMC,wBACJ,GAAG,oBAAoB,UACvB,gBACA,cACA;EACE,QAAQ;EACR;CACF,CACF;;;;;CAMF,MAAM,cAAc,OAAO,eAA+B,CAAC,MACzD,MAAMA,wBACJ,GAAG,oBAAoB,YACvB,gBACA,cACA,EAAE,QAAQ,MAAM,CAClB;;;;;;;CAQF,MAAM,cAAc,OAAO,OAAO,IAAI,eAA+B,CAAC,MACpE,MAAMA,wBACJ,GAAG,oBAAoB,YACvB,gBACA,cACA;EACE,QAAQ;EACR,QAAQ,EAAE,MAAM,OAAO,IAAI,EAAE;CAC/B,CACF;;;;;CAMF,MAAM,kBAAkB,OAAO,eAA+B,CAAC,MAC7D,MAAMA,wBACJ,GAAG,oBAAoB,iBACvB,gBACA,cACA,EAAE,QAAQ,MAAM,CAClB;;;;;;CAOF,MAAM,uBAAuB,OAC3B,eACA,eAA+B,CAAC,MAEhC,MAAMA,wBACJ,GAAG,oBAAoB,eAAe,iBACtC,gBACA,cACA,EAAE,QAAQ,MAAM,CAClB;CAEF,OAAO;EACL;EACA;EACA;EACA;EACA;CACF;AACF;;;;;;;AAQA,MAAa,oBAAoBC,6CAAe,eAAe"}
1
+ {"version":3,"file":"analytics.cjs","names":["editor","fetcher","createEndpoint"],"sources":["../../../src/getIntlayerAPI/analytics.ts"],"sourcesContent":["import type {\n AudienceRange,\n GetAnalyticsOverviewResult,\n GetAudienceResult,\n GetContentStatsResult,\n GetExperimentResultsResult,\n IngestAnalyticsBody,\n IngestAnalyticsResult,\n} from '@intlayer/backend';\nimport { editor } from '@intlayer/config/built';\nimport type { IntlayerConfig } from '@intlayer/types/config';\nimport { createEndpoint } from '../cms/createIntlayerCMS';\nimport { type FetcherOptions, fetcher } from '../fetcher';\n\nexport const getAnalyticsAPI = (\n authAPIOptions: FetcherOptions = {},\n intlayerConfig?: IntlayerConfig\n) => {\n const backendURL = intlayerConfig?.editor?.backendURL ?? editor.backendURL;\n\n const ANALYTICS_API_ROUTE = `${backendURL}/api/analytics`;\n\n /**\n * Ingest a batch of analytics events. Public — attribution is by the\n * project's `clientId` in the body.\n * @param body - Session id, sdk version, and the collected events.\n * @returns The number of accepted events.\n */\n const sendEvents = async (\n body: IngestAnalyticsBody,\n otherOptions: FetcherOptions = {}\n ) =>\n await fetcher<IngestAnalyticsResult>(\n `${ANALYTICS_API_ROUTE}/events`,\n authAPIOptions,\n otherOptions,\n {\n method: 'POST',\n body,\n }\n );\n\n /**\n * Page/locale totals for the authenticated project.\n * @returns Views grouped by url and locale.\n */\n const getOverview = async (otherOptions: FetcherOptions = {}) =>\n await fetcher<GetAnalyticsOverviewResult>(\n `${ANALYTICS_API_ROUTE}/overview`,\n authAPIOptions,\n otherOptions,\n { method: 'GET' }\n );\n\n /**\n * Audience report: distinct visitors (today / 7d / window), page views, the\n * evolution series, and locale + country breakdowns.\n * @param range - Rolling window, either a named range (`1h`, `24h`, `7d`,\n * `30d`, `90d`, `6mo`, `1y`, `3y`) or a number of days (default 30).\n * @returns The audience statistics.\n */\n const getAudience = async (\n range: AudienceRange | number = 30,\n otherOptions: FetcherOptions = {}\n ) =>\n await fetcher<GetAudienceResult>(\n `${ANALYTICS_API_ROUTE}/audience`,\n authAPIOptions,\n otherOptions,\n {\n method: 'GET',\n params: typeof range === 'number' ? { days: String(range) } : { range },\n }\n );\n\n /**\n * Per-content exposure totals — \"which content is actually shown\".\n * @returns Exposures grouped by dictionary key, key path, and locale.\n */\n const getContentStats = async (otherOptions: FetcherOptions = {}) =>\n await fetcher<GetContentStatsResult>(\n `${ANALYTICS_API_ROUTE}/content-stats`,\n authAPIOptions,\n otherOptions,\n { method: 'GET' }\n );\n\n /**\n * A/B experiment results with per-variant conversion rates and significance.\n * @param experimentKey - The experiment to evaluate.\n * @returns The experiment result set.\n */\n const getExperimentResults = async (\n experimentKey: string,\n otherOptions: FetcherOptions = {}\n ) =>\n await fetcher<GetExperimentResultsResult>(\n `${ANALYTICS_API_ROUTE}/experiments/${experimentKey}`,\n authAPIOptions,\n otherOptions,\n { method: 'GET' }\n );\n\n return {\n sendEvents,\n getOverview,\n getAudience,\n getContentStats,\n getExperimentResults,\n };\n};\n\n/**\n * Authenticated `analytics` endpoint bound to an Intlayer CMS authenticator.\n *\n * Pass an authenticator created with `createIntlayerCMS`, or omit it to use\n * the build-time configuration (`@intlayer/config/built`).\n */\nexport const analyticsEndpoint = createEndpoint(getAnalyticsAPI);\n"],"mappings":";;;;;;AAcA,MAAa,mBACX,iBAAiC,CAAC,GAClC,mBACG;CAGH,MAAM,sBAAsB,GAFT,gBAAgB,QAAQ,cAAcA,8BAAO,WAEtB;;;;;;;CAQ1C,MAAM,aAAa,OACjB,MACA,eAA+B,CAAC,MAEhC,MAAMC,wBACJ,GAAG,oBAAoB,UACvB,gBACA,cACA;EACE,QAAQ;EACR;CACF,CACF;;;;;CAMF,MAAM,cAAc,OAAO,eAA+B,CAAC,MACzD,MAAMA,wBACJ,GAAG,oBAAoB,YACvB,gBACA,cACA,EAAE,QAAQ,MAAM,CAClB;;;;;;;;CASF,MAAM,cAAc,OAClB,QAAgC,IAChC,eAA+B,CAAC,MAEhC,MAAMA,wBACJ,GAAG,oBAAoB,YACvB,gBACA,cACA;EACE,QAAQ;EACR,QAAQ,OAAO,UAAU,WAAW,EAAE,MAAM,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM;CACxE,CACF;;;;;CAMF,MAAM,kBAAkB,OAAO,eAA+B,CAAC,MAC7D,MAAMA,wBACJ,GAAG,oBAAoB,iBACvB,gBACA,cACA,EAAE,QAAQ,MAAM,CAClB;;;;;;CAOF,MAAM,uBAAuB,OAC3B,eACA,eAA+B,CAAC,MAEhC,MAAMA,wBACJ,GAAG,oBAAoB,eAAe,iBACtC,gBACA,cACA,EAAE,QAAQ,MAAM,CAClB;CAEF,OAAO;EACL;EACA;EACA;EACA;EACA;CACF;AACF;;;;;;;AAQA,MAAa,oBAAoBC,6CAAe,eAAe"}
@@ -0,0 +1,123 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
+ let _intlayer_config_built = require("@intlayer/config/built");
3
+
4
+ //#region src/public/createPublicClient.ts
5
+ /**
6
+ * Browser-safe Intlayer client, authenticated without any confidential
7
+ * credential.
8
+ *
9
+ * This is the Google-Analytics model applied to the whole SDK: the page ships
10
+ * only the **public** `editor.clientId`, exchanges it for a short-lived scoped
11
+ * token, and calls the API directly. No server route, no server action, and no
12
+ * `clientSecret` in the bundle.
13
+ *
14
+ * What it can do is deliberately narrow, and that is what makes shipping the
15
+ * key safe: read the project's published dictionary content — the content the
16
+ * page already renders — and submit analytics events. Writing content, reading
17
+ * a project, an organization or a user still require a real credential
18
+ * (server-side) or a signed-in user.
19
+ *
20
+ * @example
21
+ * ```ts
22
+ * import { createPublicClient } from '@intlayer/api/public';
23
+ *
24
+ * const client = createPublicClient();
25
+ *
26
+ * const keys = await client.getDictionaryKeys();
27
+ * const [dictionary] = await client.getDictionaries(['navbar']);
28
+ * ```
29
+ */
30
+ /** `sessionStorage` key holding the cached token for this tab. */
31
+ const TOKEN_STORAGE_KEY = "__intlayer_public_token__";
32
+ /**
33
+ * Refresh margin: a token within this window of expiry is treated as expired,
34
+ * so a request is never sent with a token that dies in flight.
35
+ */
36
+ const EXPIRY_MARGIN_MS = 6e4;
37
+ /** Reads the cached token for this tab, ignoring an unusable cache. */
38
+ const readStoredToken = () => {
39
+ try {
40
+ const raw = globalThis.sessionStorage?.getItem(TOKEN_STORAGE_KEY);
41
+ if (!raw) return null;
42
+ const parsed = JSON.parse(raw);
43
+ if (typeof parsed?.token !== "string") return null;
44
+ if (typeof parsed?.expiresAt !== "number") return null;
45
+ return parsed;
46
+ } catch {
47
+ return null;
48
+ }
49
+ };
50
+ /** Persists the token for this tab. Best-effort: storage may be unavailable. */
51
+ const writeStoredToken = (cached) => {
52
+ try {
53
+ globalThis.sessionStorage?.setItem(TOKEN_STORAGE_KEY, JSON.stringify(cached));
54
+ } catch {}
55
+ };
56
+ /**
57
+ * Creates a {@link PublicClient} for the configured project.
58
+ *
59
+ * @param options - Optional configuration override.
60
+ * @returns A client that authenticates itself with the public project key.
61
+ */
62
+ const createPublicClient = ({ intlayerConfig } = {}) => {
63
+ const resolvedEditor = intlayerConfig?.editor ?? _intlayer_config_built.editor;
64
+ const backendURL = (resolvedEditor?.backendURL ?? "").replace(/\/$/, "");
65
+ const clientId = resolvedEditor?.clientId;
66
+ let cached = null;
67
+ let pendingExchange = null;
68
+ const isUsable = (candidate) => candidate !== null && candidate.expiresAt - EXPIRY_MARGIN_MS > Date.now();
69
+ const exchange = async () => {
70
+ const response = await fetch(`${backendURL}/api/public/token`, {
71
+ method: "POST",
72
+ headers: { "Content-Type": "application/json" },
73
+ body: JSON.stringify({ clientId }),
74
+ credentials: "omit",
75
+ mode: "cors"
76
+ });
77
+ if (!response.ok) return null;
78
+ const payload = await response.json();
79
+ const token = payload?.data?.token;
80
+ const expiresIn = payload?.data?.expiresIn ?? 0;
81
+ if (!token || expiresIn <= 0) return null;
82
+ cached = {
83
+ token,
84
+ expiresAt: Date.now() + expiresIn * 1e3
85
+ };
86
+ writeStoredToken(cached);
87
+ return token;
88
+ };
89
+ const getToken = async () => {
90
+ if (isUsable(cached)) return cached.token;
91
+ const stored = readStoredToken();
92
+ if (isUsable(stored)) {
93
+ cached = stored;
94
+ return stored.token;
95
+ }
96
+ if (!clientId) return null;
97
+ pendingExchange ??= exchange().finally(() => {
98
+ pendingExchange = null;
99
+ });
100
+ return await pendingExchange;
101
+ };
102
+ /** Issues an authorised GET against the public surface. */
103
+ const request = async (path, fallback) => {
104
+ const token = await getToken();
105
+ if (!token) return fallback;
106
+ const response = await fetch(`${backendURL}/api/public${path}`, {
107
+ headers: { Authorization: `Bearer ${token}` },
108
+ credentials: "omit",
109
+ mode: "cors"
110
+ });
111
+ if (!response.ok) return fallback;
112
+ return (await response.json())?.data ?? fallback;
113
+ };
114
+ return {
115
+ getToken,
116
+ getDictionaryKeys: () => request("/dictionaries/keys", []),
117
+ getDictionaries: (keys) => request(`/dictionaries?keys=${encodeURIComponent(keys.join(","))}`, [])
118
+ };
119
+ };
120
+
121
+ //#endregion
122
+ exports.createPublicClient = createPublicClient;
123
+ //# sourceMappingURL=createPublicClient.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"createPublicClient.cjs","names":["editor"],"sources":["../../../src/public/createPublicClient.ts"],"sourcesContent":["import type { DictionaryAPI } from '@intlayer/backend';\nimport { editor } from '@intlayer/config/built';\nimport type { IntlayerConfig } from '@intlayer/types/config';\n\n/**\n * Browser-safe Intlayer client, authenticated without any confidential\n * credential.\n *\n * This is the Google-Analytics model applied to the whole SDK: the page ships\n * only the **public** `editor.clientId`, exchanges it for a short-lived scoped\n * token, and calls the API directly. No server route, no server action, and no\n * `clientSecret` in the bundle.\n *\n * What it can do is deliberately narrow, and that is what makes shipping the\n * key safe: read the project's published dictionary content — the content the\n * page already renders — and submit analytics events. Writing content, reading\n * a project, an organization or a user still require a real credential\n * (server-side) or a signed-in user.\n *\n * @example\n * ```ts\n * import { createPublicClient } from '@intlayer/api/public';\n *\n * const client = createPublicClient();\n *\n * const keys = await client.getDictionaryKeys();\n * const [dictionary] = await client.getDictionaries(['navbar']);\n * ```\n */\n\n/** `sessionStorage` key holding the cached token for this tab. */\nconst TOKEN_STORAGE_KEY = '__intlayer_public_token__';\n\n/**\n * Refresh margin: a token within this window of expiry is treated as expired,\n * so a request is never sent with a token that dies in flight.\n */\nconst EXPIRY_MARGIN_MS = 60 * 1000;\n\ntype CachedToken = {\n token: string;\n /** Epoch milliseconds. */\n expiresAt: number;\n};\n\n/** Reads the cached token for this tab, ignoring an unusable cache. */\nconst readStoredToken = (): CachedToken | null => {\n try {\n const raw = globalThis.sessionStorage?.getItem(TOKEN_STORAGE_KEY);\n if (!raw) return null;\n\n const parsed = JSON.parse(raw) as CachedToken;\n\n if (typeof parsed?.token !== 'string') return null;\n if (typeof parsed?.expiresAt !== 'number') return null;\n\n return parsed;\n } catch {\n // Private mode, disabled storage, or a corrupted entry — start fresh.\n return null;\n }\n};\n\n/** Persists the token for this tab. Best-effort: storage may be unavailable. */\nconst writeStoredToken = (cached: CachedToken): void => {\n try {\n globalThis.sessionStorage?.setItem(\n TOKEN_STORAGE_KEY,\n JSON.stringify(cached)\n );\n } catch {\n /* ignore — the in-memory cache still applies for this page */\n }\n};\n\n/** Options accepted by {@link createPublicClient}. */\nexport type PublicClientOptions = {\n /** Overrides the build-time `editor` configuration. */\n intlayerConfig?: Pick<IntlayerConfig, 'editor'>;\n};\n\n/** The credential-free client returned by {@link createPublicClient}. */\nexport type PublicClient = {\n /**\n * Returns the current token, exchanging the public `clientId` for one when\n * needed. `null` when the project is unknown or the origin is not allowed.\n */\n getToken: () => Promise<string | null>;\n /** Every dictionary key published for the project. */\n getDictionaryKeys: () => Promise<string[]>;\n /**\n * Published dictionary content for the given keys.\n *\n * @param keys - Dictionary keys to fetch (at most 100 per call).\n */\n getDictionaries: (keys: string[]) => Promise<DictionaryAPI[]>;\n};\n\n/**\n * Creates a {@link PublicClient} for the configured project.\n *\n * @param options - Optional configuration override.\n * @returns A client that authenticates itself with the public project key.\n */\nexport const createPublicClient = ({\n intlayerConfig,\n}: PublicClientOptions = {}): PublicClient => {\n const resolvedEditor = intlayerConfig?.editor ?? editor;\n const backendURL = (resolvedEditor?.backendURL ?? '').replace(/\\/$/, '');\n const clientId = resolvedEditor?.clientId;\n\n let cached: CachedToken | null = null;\n let pendingExchange: Promise<string | null> | null = null;\n\n const isUsable = (candidate: CachedToken | null): candidate is CachedToken =>\n candidate !== null && candidate.expiresAt - EXPIRY_MARGIN_MS > Date.now();\n\n const exchange = async (): Promise<string | null> => {\n const response = await fetch(`${backendURL}/api/public/token`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ clientId }),\n // Authorised by `Origin`, never by cookies.\n credentials: 'omit',\n mode: 'cors',\n });\n\n if (!response.ok) return null;\n\n const payload = (await response.json()) as {\n data?: { token?: string | null; expiresIn?: number };\n };\n\n const token = payload?.data?.token;\n const expiresIn = payload?.data?.expiresIn ?? 0;\n\n if (!token || expiresIn <= 0) return null;\n\n cached = { token, expiresAt: Date.now() + expiresIn * 1000 };\n writeStoredToken(cached);\n\n return token;\n };\n\n const getToken = async (): Promise<string | null> => {\n if (isUsable(cached)) return cached.token;\n\n const stored = readStoredToken();\n if (isUsable(stored)) {\n cached = stored;\n return stored.token;\n }\n\n if (!clientId) return null;\n\n // De-duplicate concurrent exchanges so a burst of calls triggers one fetch.\n pendingExchange ??= exchange().finally(() => {\n pendingExchange = null;\n });\n\n return await pendingExchange;\n };\n\n /** Issues an authorised GET against the public surface. */\n const request = async <Result>(\n path: string,\n fallback: Result\n ): Promise<Result> => {\n const token = await getToken();\n\n if (!token) return fallback;\n\n const response = await fetch(`${backendURL}/api/public${path}`, {\n headers: { Authorization: `Bearer ${token}` },\n credentials: 'omit',\n mode: 'cors',\n });\n\n if (!response.ok) return fallback;\n\n const payload = (await response.json()) as { data?: Result };\n\n return payload?.data ?? fallback;\n };\n\n return {\n getToken,\n getDictionaryKeys: () => request<string[]>('/dictionaries/keys', []),\n getDictionaries: (keys) =>\n request<DictionaryAPI[]>(\n `/dictionaries?keys=${encodeURIComponent(keys.join(','))}`,\n []\n ),\n };\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA+BA,MAAM,oBAAoB;;;;;AAM1B,MAAM,mBAAmB;;AASzB,MAAM,wBAA4C;CAChD,IAAI;EACF,MAAM,MAAM,WAAW,gBAAgB,QAAQ,iBAAiB;EAChE,IAAI,CAAC,KAAK,OAAO;EAEjB,MAAM,SAAS,KAAK,MAAM,GAAG;EAE7B,IAAI,OAAO,QAAQ,UAAU,UAAU,OAAO;EAC9C,IAAI,OAAO,QAAQ,cAAc,UAAU,OAAO;EAElD,OAAO;CACT,QAAQ;EAEN,OAAO;CACT;AACF;;AAGA,MAAM,oBAAoB,WAA8B;CACtD,IAAI;EACF,WAAW,gBAAgB,QACzB,mBACA,KAAK,UAAU,MAAM,CACvB;CACF,QAAQ,CAER;AACF;;;;;;;AA+BA,MAAa,sBAAsB,EACjC,mBACuB,CAAC,MAAoB;CAC5C,MAAM,iBAAiB,gBAAgB,UAAUA;CACjD,MAAM,cAAc,gBAAgB,cAAc,GAAE,CAAE,QAAQ,OAAO,EAAE;CACvE,MAAM,WAAW,gBAAgB;CAEjC,IAAI,SAA6B;CACjC,IAAI,kBAAiD;CAErD,MAAM,YAAY,cAChB,cAAc,QAAQ,UAAU,YAAY,mBAAmB,KAAK,IAAI;CAE1E,MAAM,WAAW,YAAoC;EACnD,MAAM,WAAW,MAAM,MAAM,GAAG,WAAW,oBAAoB;GAC7D,QAAQ;GACR,SAAS,EAAE,gBAAgB,mBAAmB;GAC9C,MAAM,KAAK,UAAU,EAAE,SAAS,CAAC;GAEjC,aAAa;GACb,MAAM;EACR,CAAC;EAED,IAAI,CAAC,SAAS,IAAI,OAAO;EAEzB,MAAM,UAAW,MAAM,SAAS,KAAK;EAIrC,MAAM,QAAQ,SAAS,MAAM;EAC7B,MAAM,YAAY,SAAS,MAAM,aAAa;EAE9C,IAAI,CAAC,SAAS,aAAa,GAAG,OAAO;EAErC,SAAS;GAAE;GAAO,WAAW,KAAK,IAAI,IAAI,YAAY;EAAK;EAC3D,iBAAiB,MAAM;EAEvB,OAAO;CACT;CAEA,MAAM,WAAW,YAAoC;EACnD,IAAI,SAAS,MAAM,GAAG,OAAO,OAAO;EAEpC,MAAM,SAAS,gBAAgB;EAC/B,IAAI,SAAS,MAAM,GAAG;GACpB,SAAS;GACT,OAAO,OAAO;EAChB;EAEA,IAAI,CAAC,UAAU,OAAO;EAGtB,oBAAoB,SAAS,CAAC,CAAC,cAAc;GAC3C,kBAAkB;EACpB,CAAC;EAED,OAAO,MAAM;CACf;;CAGA,MAAM,UAAU,OACd,MACA,aACoB;EACpB,MAAM,QAAQ,MAAM,SAAS;EAE7B,IAAI,CAAC,OAAO,OAAO;EAEnB,MAAM,WAAW,MAAM,MAAM,GAAG,WAAW,aAAa,QAAQ;GAC9D,SAAS,EAAE,eAAe,UAAU,QAAQ;GAC5C,aAAa;GACb,MAAM;EACR,CAAC;EAED,IAAI,CAAC,SAAS,IAAI,OAAO;EAIzB,QAAO,MAFgB,SAAS,KAAK,EAEvB,EAAE,QAAQ;CAC1B;CAEA,OAAO;EACL;EACA,yBAAyB,QAAkB,sBAAsB,CAAC,CAAC;EACnE,kBAAkB,SAChB,QACE,sBAAsB,mBAAmB,KAAK,KAAK,GAAG,CAAC,KACvD,CAAC,CACH;CACJ;AACF"}
@@ -0,0 +1,4 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
+ const require_public_createPublicClient = require('./createPublicClient.cjs');
3
+
4
+ exports.createPublicClient = require_public_createPublicClient.createPublicClient;
@@ -1,9 +1,12 @@
1
1
  import { getOAuthAPI } from "../getIntlayerAPI/oAuth.mjs";
2
2
  import { editor } from "@intlayer/config/built";
3
+ import { getEditorClientSecret } from "@intlayer/config/secrets";
3
4
  import { defu } from "defu";
4
5
 
5
6
  //#region src/cms/createAuthManager.ts
6
7
  const ONE_MINUTE_MS = 6e4;
8
+ /** Whether the current runtime is a browser rather than a server. */
9
+ const isBrowser = () => typeof window !== "undefined";
7
10
  /**
8
11
  * Resolves the expiration timestamp (in ms since epoch) from an OAuth2
9
12
  * token-like payload, supporting both absolute dates and relative durations.
@@ -31,7 +34,10 @@ const getExpiryTimestamp = (token) => {
31
34
  */
32
35
  const createAuthManager = (intlayerConfig = { editor }, options = {}) => {
33
36
  const { baseFetcherOptions = {}, sessionToken } = options;
34
- const resolvedConfig = defu(intlayerConfig ?? {}, { editor });
37
+ const resolvedConfig = defu(intlayerConfig ?? {}, { editor: {
38
+ ...editor,
39
+ clientSecret: editor?.clientId ? getEditorClientSecret() : void 0
40
+ } });
35
41
  const fetcherOptions = {
36
42
  ...baseFetcherOptions,
37
43
  credentials: "omit"
@@ -77,6 +83,9 @@ const createAuthManager = (intlayerConfig = { editor }, options = {}) => {
77
83
  expiryTimestamp = void 0;
78
84
  };
79
85
  const withAuth = (method, skipAuth = !hasCredentials) => {
86
+ if (skipAuth && !sessionToken && isBrowser()) return async () => {
87
+ throw new Error("Intlayer CMS credentials are not available in the browser. `clientSecret` is a server-side credential and is never bundled. Call this from a server route, or pass a user session token: createIntlayerCMS(config, { sessionToken }).");
88
+ };
80
89
  if (skipAuth) return method;
81
90
  return async (...args) => {
82
91
  await ensureAuth();
@@ -1 +1 @@
1
- {"version":3,"file":"createAuthManager.mjs","names":[],"sources":["../../../src/cms/createAuthManager.ts"],"sourcesContent":["import { editor } from '@intlayer/config/built';\nimport type { IntlayerConfig } from '@intlayer/types/config';\nimport { defu } from 'defu';\nimport type { FetcherOptions } from '../fetcher';\nimport { getOAuthAPI } from '../getIntlayerAPI/oAuth';\n\n/**\n * Shape of the various OAuth2 token payloads the backend may return. Only the\n * fields used to derive the expiration timestamp are described here.\n */\ntype OAuthTokenLike = {\n accessToken?: string;\n accessTokenExpiresAt?: string | Date;\n expires_in?: number;\n expiresIn?: number;\n expiresAt?: string | Date;\n};\n\n/**\n * Any asynchronous API method exposed by a domain section\n * (e.g. `getDictionaries`, `pushDictionaries`).\n */\ntype AsyncMethod<Arguments extends unknown[], Result> = (\n ...args: Arguments\n) => Promise<Result>;\n\n/**\n * A domain section factory: builds a plain object of async methods from shared\n * fetcher options and the Intlayer configuration.\n */\ntype SectionFactory<Section extends Record<string, unknown>> = (\n fetcherOptions: FetcherOptions,\n intlayerConfig: IntlayerConfig\n) => Section;\n\nconst ONE_MINUTE_MS = 60_000;\n\n/**\n * Resolves the expiration timestamp (in ms since epoch) from an OAuth2\n * token-like payload, supporting both absolute dates and relative durations.\n */\nconst getExpiryTimestamp = (\n token: OAuthTokenLike | undefined\n): number | undefined => {\n if (!token) return undefined;\n\n const dateLike = (token.accessTokenExpiresAt ?? token.expiresAt) as\n | string\n | Date\n | undefined;\n\n if (dateLike) {\n const timestamp =\n typeof dateLike === 'string'\n ? Date.parse(dateLike)\n : dateLike.getTime?.();\n if (typeof timestamp === 'number' && Number.isFinite(timestamp)) {\n return timestamp;\n }\n }\n\n const seconds = token.expires_in ?? token.expiresIn;\n if (typeof seconds === 'number' && Number.isFinite(seconds)) {\n return Date.now() + seconds * 1000;\n }\n\n return undefined;\n};\n\n/**\n * Options accepted when creating an {@link AuthManager}.\n */\nexport type AuthManagerOptions = {\n /**\n * Base fetcher options merged into every request (e.g. custom headers).\n */\n baseFetcherOptions?: FetcherOptions;\n /**\n * A pre-issued CLI session token (prefixed with `clisession_`). When provided,\n * it is used directly as the Bearer token and the OAuth2 `client_credentials`\n * exchange is skipped entirely.\n */\n sessionToken?: string;\n};\n\n/**\n * Options accepted when binding a domain section to an {@link AuthManager}.\n */\nexport type BindSectionOptions = {\n /**\n * Forces auth injection on (`false`) or off (`true`). Defaults to enabled\n * whenever credentials are available.\n */\n skipAuth?: boolean;\n};\n\n/**\n * Manages CMS authentication for a set of API calls.\n *\n * The manager owns a single mutable {@link FetcherOptions} object\n * (`fetcherOptions`). Domain factories bound through it share the same\n * reference, so refreshing the token in place transparently authenticates every\n * call — without rebuilding the API clients.\n */\nexport type AuthManager = {\n /**\n * Mutable fetcher options carrying the `Authorization` header. Bound sections\n * share this reference so the injected token is reused everywhere.\n */\n fetcherOptions: FetcherOptions;\n /**\n * The resolved Intlayer configuration (merged with the build-time defaults).\n */\n intlayerConfig: IntlayerConfig;\n /**\n * `true` when credentials are available — either a session token or a\n * `clientId`/`clientSecret` pair in the configuration.\n */\n hasCredentials: boolean;\n /**\n * Ensures a valid access token is available and applies it to\n * `fetcherOptions`. Called automatically by {@link withAuth}.\n */\n ensureAuth: () => Promise<void>;\n /**\n * Forces a token refresh on the next call by clearing the cached token.\n */\n reset: () => void;\n /**\n * Wraps a single API method so it injects (and, on failure, refreshes) the\n * access token on every invocation while preserving the method's signature.\n */\n withAuth: <Arguments extends unknown[], Result>(\n method: AsyncMethod<Arguments, Result>,\n skipAuth?: boolean\n ) => AsyncMethod<Arguments, Result>;\n /**\n * Instantiates a domain factory with the managed fetcher options and config,\n * returning a section whose every method authenticates automatically.\n *\n * @example\n * ```ts\n * const dictionary = authManager.bindSection(getDictionaryAPI);\n * await dictionary.pushDictionaries(dictionaries);\n * ```\n */\n bindSection: <Section extends Record<string, unknown>>(\n factory: SectionFactory<Section>,\n options?: BindSectionOptions\n ) => Section;\n};\n\n/**\n * Creates an instance-scoped {@link AuthManager} handling the OAuth2\n * `client_credentials` flow (or a pre-issued CLI session token).\n *\n * Unlike a global token cache, each manager keeps its own token state, so\n * independent SDK instances never interfere with one another.\n *\n * @param intlayerConfig - Intlayer configuration carrying the `editor`\n * credentials. Defaults to the build-time configuration.\n * @param options - Optional session token and base fetcher options.\n */\nexport const createAuthManager = (\n intlayerConfig: Pick<IntlayerConfig, 'editor'> = { editor },\n options: AuthManagerOptions = {}\n): AuthManager => {\n const { baseFetcherOptions = {}, sessionToken } = options;\n\n const resolvedConfig = defu(intlayerConfig ?? {}, {\n editor,\n }) as IntlayerConfig;\n\n // `credentials: 'omit'` prevents the browser from attaching session cookies;\n // authentication is handled exclusively through the injected Bearer token.\n // This is required because the backend only allows credentialed requests for\n // whitelisted first-party origins.\n const fetcherOptions: FetcherOptions = {\n ...baseFetcherOptions,\n credentials: 'omit',\n };\n\n const hasCredentials =\n Boolean(sessionToken) ||\n Boolean(\n resolvedConfig?.editor?.clientId && resolvedConfig?.editor?.clientSecret\n );\n\n // A session token is used as-is and never expires within the SDK's concern,\n // so inject it once up-front and skip the OAuth2 exchange.\n if (sessionToken) {\n fetcherOptions.headers = {\n ...(fetcherOptions.headers ?? {}),\n Authorization: `Bearer ${sessionToken}`,\n } as HeadersInit;\n }\n\n let accessToken: string | undefined;\n let expiryTimestamp: number | undefined;\n let pendingRefresh: Promise<void> | undefined;\n\n const needsRefresh = (): boolean => {\n if (!accessToken) return true;\n if (!expiryTimestamp) return false; // Unknown expiry: assume usable until failure.\n\n // Refresh one minute before the actual expiry to avoid edge-of-expiry races.\n return Date.now() + ONE_MINUTE_MS >= expiryTimestamp;\n };\n\n const refreshToken = async (): Promise<void> => {\n const doRefresh = async (): Promise<void> => {\n const oAuthAPI = getOAuthAPI({}, resolvedConfig);\n const response = await oAuthAPI.getOAuth2AccessToken();\n const tokenData = response?.data as OAuthTokenLike | undefined;\n\n accessToken = tokenData?.accessToken;\n expiryTimestamp = getExpiryTimestamp(tokenData);\n };\n\n // De-duplicate concurrent refreshes so a burst of calls triggers one fetch.\n pendingRefresh ??= doRefresh().finally(() => {\n pendingRefresh = undefined;\n });\n\n await pendingRefresh;\n };\n\n const applyAuthHeader = (): void => {\n if (!accessToken) return;\n fetcherOptions.headers = {\n ...(fetcherOptions.headers ?? {}),\n Authorization: `Bearer ${accessToken}`,\n } as HeadersInit;\n };\n\n const ensureAuth = async (): Promise<void> => {\n if (sessionToken) return; // Already injected; nothing to refresh.\n if (needsRefresh()) {\n await refreshToken();\n }\n applyAuthHeader();\n };\n\n const reset = (): void => {\n accessToken = undefined;\n expiryTimestamp = undefined;\n };\n\n const withAuth = <Arguments extends unknown[], Result>(\n method: AsyncMethod<Arguments, Result>,\n skipAuth = !hasCredentials\n ): AsyncMethod<Arguments, Result> => {\n if (skipAuth) return method;\n\n return async (...args: Arguments): Promise<Result> => {\n await ensureAuth();\n\n try {\n return await method(...args);\n } catch (error) {\n // The token may have been revoked or expired early: refresh once and\n // retry before surfacing the error. A session token cannot be\n // refreshed, so re-throw immediately in that case.\n if (sessionToken) throw error;\n\n await refreshToken();\n applyAuthHeader();\n return await method(...args);\n }\n };\n };\n\n const bindSection = <Section extends Record<string, unknown>>(\n factory: SectionFactory<Section>,\n bindOptions: BindSectionOptions = {}\n ): Section => {\n const section = factory(fetcherOptions, resolvedConfig);\n const skipAuth = bindOptions.skipAuth ?? !hasCredentials;\n\n return new Proxy(section, {\n get(target, property, receiver) {\n const value = Reflect.get(target, property, receiver);\n\n if (typeof value !== 'function') return value;\n\n return withAuth(\n (value as AsyncMethod<unknown[], unknown>).bind(target),\n skipAuth\n );\n },\n });\n };\n\n return {\n fetcherOptions,\n intlayerConfig: resolvedConfig,\n hasCredentials,\n ensureAuth,\n reset,\n withAuth,\n bindSection,\n };\n};\n"],"mappings":";;;;;AAmCA,MAAM,gBAAgB;;;;;AAMtB,MAAM,sBACJ,UACuB;CACvB,IAAI,CAAC,OAAO,OAAO;CAEnB,MAAM,WAAY,MAAM,wBAAwB,MAAM;CAKtD,IAAI,UAAU;EACZ,MAAM,YACJ,OAAO,aAAa,WAChB,KAAK,MAAM,QAAQ,IACnB,SAAS,UAAU;EACzB,IAAI,OAAO,cAAc,YAAY,OAAO,SAAS,SAAS,GAC5D,OAAO;CAEX;CAEA,MAAM,UAAU,MAAM,cAAc,MAAM;CAC1C,IAAI,OAAO,YAAY,YAAY,OAAO,SAAS,OAAO,GACxD,OAAO,KAAK,IAAI,IAAI,UAAU;AAIlC;;;;;;;;;;;;AAgGA,MAAa,qBACX,iBAAiD,EAAE,OAAO,GAC1D,UAA8B,CAAC,MACf;CAChB,MAAM,EAAE,qBAAqB,CAAC,GAAG,iBAAiB;CAElD,MAAM,iBAAiB,KAAK,kBAAkB,CAAC,GAAG,EAChD,OACF,CAAC;CAMD,MAAM,iBAAiC;EACrC,GAAG;EACH,aAAa;CACf;CAEA,MAAM,iBACJ,QAAQ,YAAY,KACpB,QACE,gBAAgB,QAAQ,YAAY,gBAAgB,QAAQ,YAC9D;CAIF,IAAI,cACF,eAAe,UAAU;EACvB,GAAI,eAAe,WAAW,CAAC;EAC/B,eAAe,UAAU;CAC3B;CAGF,IAAI;CACJ,IAAI;CACJ,IAAI;CAEJ,MAAM,qBAA8B;EAClC,IAAI,CAAC,aAAa,OAAO;EACzB,IAAI,CAAC,iBAAiB,OAAO;EAG7B,OAAO,KAAK,IAAI,IAAI,iBAAiB;CACvC;CAEA,MAAM,eAAe,YAA2B;EAC9C,MAAM,YAAY,YAA2B;GAG3C,MAAM,aAAY,MAFD,YAAY,CAAC,GAAG,cACH,CAAC,CAAC,qBAAqB,EAC3B,EAAE;GAE5B,cAAc,WAAW;GACzB,kBAAkB,mBAAmB,SAAS;EAChD;EAGA,mBAAmB,UAAU,CAAC,CAAC,cAAc;GAC3C,iBAAiB;EACnB,CAAC;EAED,MAAM;CACR;CAEA,MAAM,wBAA8B;EAClC,IAAI,CAAC,aAAa;EAClB,eAAe,UAAU;GACvB,GAAI,eAAe,WAAW,CAAC;GAC/B,eAAe,UAAU;EAC3B;CACF;CAEA,MAAM,aAAa,YAA2B;EAC5C,IAAI,cAAc;EAClB,IAAI,aAAa,GACf,MAAM,aAAa;EAErB,gBAAgB;CAClB;CAEA,MAAM,cAAoB;EACxB,cAAc;EACd,kBAAkB;CACpB;CAEA,MAAM,YACJ,QACA,WAAW,CAAC,mBACuB;EACnC,IAAI,UAAU,OAAO;EAErB,OAAO,OAAO,GAAG,SAAqC;GACpD,MAAM,WAAW;GAEjB,IAAI;IACF,OAAO,MAAM,OAAO,GAAG,IAAI;GAC7B,SAAS,OAAO;IAId,IAAI,cAAc,MAAM;IAExB,MAAM,aAAa;IACnB,gBAAgB;IAChB,OAAO,MAAM,OAAO,GAAG,IAAI;GAC7B;EACF;CACF;CAEA,MAAM,eACJ,SACA,cAAkC,CAAC,MACvB;EACZ,MAAM,UAAU,QAAQ,gBAAgB,cAAc;EACtD,MAAM,WAAW,YAAY,YAAY,CAAC;EAE1C,OAAO,IAAI,MAAM,SAAS,EACxB,IAAI,QAAQ,UAAU,UAAU;GAC9B,MAAM,QAAQ,QAAQ,IAAI,QAAQ,UAAU,QAAQ;GAEpD,IAAI,OAAO,UAAU,YAAY,OAAO;GAExC,OAAO,SACJ,MAA0C,KAAK,MAAM,GACtD,QACF;EACF,EACF,CAAC;CACH;CAEA,OAAO;EACL;EACA,gBAAgB;EAChB;EACA;EACA;EACA;EACA;CACF;AACF"}
1
+ {"version":3,"file":"createAuthManager.mjs","names":[],"sources":["../../../src/cms/createAuthManager.ts"],"sourcesContent":["import { editor } from '@intlayer/config/built';\nimport { getEditorClientSecret } from '@intlayer/config/secrets';\nimport type { IntlayerConfig } from '@intlayer/types/config';\nimport { defu } from 'defu';\nimport type { FetcherOptions } from '../fetcher';\nimport { getOAuthAPI } from '../getIntlayerAPI/oAuth';\n\n/**\n * Shape of the various OAuth2 token payloads the backend may return. Only the\n * fields used to derive the expiration timestamp are described here.\n */\ntype OAuthTokenLike = {\n accessToken?: string;\n accessTokenExpiresAt?: string | Date;\n expires_in?: number;\n expiresIn?: number;\n expiresAt?: string | Date;\n};\n\n/**\n * Any asynchronous API method exposed by a domain section\n * (e.g. `getDictionaries`, `pushDictionaries`).\n */\ntype AsyncMethod<Arguments extends unknown[], Result> = (\n ...args: Arguments\n) => Promise<Result>;\n\n/**\n * A domain section factory: builds a plain object of async methods from shared\n * fetcher options and the Intlayer configuration.\n */\ntype SectionFactory<Section extends Record<string, unknown>> = (\n fetcherOptions: FetcherOptions,\n intlayerConfig: IntlayerConfig\n) => Section;\n\nconst ONE_MINUTE_MS = 60_000;\n\n/** Whether the current runtime is a browser rather than a server. */\nconst isBrowser = (): boolean => typeof window !== 'undefined';\n\n/**\n * Resolves the expiration timestamp (in ms since epoch) from an OAuth2\n * token-like payload, supporting both absolute dates and relative durations.\n */\nconst getExpiryTimestamp = (\n token: OAuthTokenLike | undefined\n): number | undefined => {\n if (!token) return undefined;\n\n const dateLike = (token.accessTokenExpiresAt ?? token.expiresAt) as\n | string\n | Date\n | undefined;\n\n if (dateLike) {\n const timestamp =\n typeof dateLike === 'string'\n ? Date.parse(dateLike)\n : dateLike.getTime?.();\n if (typeof timestamp === 'number' && Number.isFinite(timestamp)) {\n return timestamp;\n }\n }\n\n const seconds = token.expires_in ?? token.expiresIn;\n if (typeof seconds === 'number' && Number.isFinite(seconds)) {\n return Date.now() + seconds * 1000;\n }\n\n return undefined;\n};\n\n/**\n * Options accepted when creating an {@link AuthManager}.\n */\nexport type AuthManagerOptions = {\n /**\n * Base fetcher options merged into every request (e.g. custom headers).\n */\n baseFetcherOptions?: FetcherOptions;\n /**\n * A pre-issued CLI session token (prefixed with `clisession_`). When provided,\n * it is used directly as the Bearer token and the OAuth2 `client_credentials`\n * exchange is skipped entirely.\n */\n sessionToken?: string;\n};\n\n/**\n * Options accepted when binding a domain section to an {@link AuthManager}.\n */\nexport type BindSectionOptions = {\n /**\n * Forces auth injection on (`false`) or off (`true`). Defaults to enabled\n * whenever credentials are available.\n */\n skipAuth?: boolean;\n};\n\n/**\n * Manages CMS authentication for a set of API calls.\n *\n * The manager owns a single mutable {@link FetcherOptions} object\n * (`fetcherOptions`). Domain factories bound through it share the same\n * reference, so refreshing the token in place transparently authenticates every\n * call — without rebuilding the API clients.\n */\nexport type AuthManager = {\n /**\n * Mutable fetcher options carrying the `Authorization` header. Bound sections\n * share this reference so the injected token is reused everywhere.\n */\n fetcherOptions: FetcherOptions;\n /**\n * The resolved Intlayer configuration (merged with the build-time defaults).\n */\n intlayerConfig: IntlayerConfig;\n /**\n * `true` when credentials are available — either a session token or a\n * `clientId`/`clientSecret` pair in the configuration.\n */\n hasCredentials: boolean;\n /**\n * Ensures a valid access token is available and applies it to\n * `fetcherOptions`. Called automatically by {@link withAuth}.\n */\n ensureAuth: () => Promise<void>;\n /**\n * Forces a token refresh on the next call by clearing the cached token.\n */\n reset: () => void;\n /**\n * Wraps a single API method so it injects (and, on failure, refreshes) the\n * access token on every invocation while preserving the method's signature.\n */\n withAuth: <Arguments extends unknown[], Result>(\n method: AsyncMethod<Arguments, Result>,\n skipAuth?: boolean\n ) => AsyncMethod<Arguments, Result>;\n /**\n * Instantiates a domain factory with the managed fetcher options and config,\n * returning a section whose every method authenticates automatically.\n *\n * @example\n * ```ts\n * const dictionary = authManager.bindSection(getDictionaryAPI);\n * await dictionary.pushDictionaries(dictionaries);\n * ```\n */\n bindSection: <Section extends Record<string, unknown>>(\n factory: SectionFactory<Section>,\n options?: BindSectionOptions\n ) => Section;\n};\n\n/**\n * Creates an instance-scoped {@link AuthManager} handling the OAuth2\n * `client_credentials` flow (or a pre-issued CLI session token).\n *\n * Unlike a global token cache, each manager keeps its own token state, so\n * independent SDK instances never interfere with one another.\n *\n * @param intlayerConfig - Intlayer configuration carrying the `editor`\n * credentials. Defaults to the build-time configuration.\n * @param options - Optional session token and base fetcher options.\n */\nexport const createAuthManager = (\n intlayerConfig: Pick<IntlayerConfig, 'editor'> = { editor },\n options: AuthManagerOptions = {}\n): AuthManager => {\n const { baseFetcherOptions = {}, sessionToken } = options;\n\n // `editor` comes from the generated build-time configuration, which never\n // carries `clientSecret` — it would be inlined into client bundles. The\n // secret is supplied server-side by `@intlayer/config/secrets`, which\n // resolves to an inert stub in the browser; `hasCredentials` then stays false\n // there and the OAuth2 exchange never runs.\n //\n // Only consulted when the configuration declares a `clientId`, so a project\n // that comments its credentials out stays uncredentialed even with the\n // environment variables still defined.\n const resolvedConfig = defu(intlayerConfig ?? {}, {\n editor: {\n ...editor,\n clientSecret: editor?.clientId ? getEditorClientSecret() : undefined,\n },\n }) as IntlayerConfig;\n\n // `credentials: 'omit'` prevents the browser from attaching session cookies;\n // authentication is handled exclusively through the injected Bearer token.\n // This is required because the backend only allows credentialed requests for\n // whitelisted first-party origins.\n const fetcherOptions: FetcherOptions = {\n ...baseFetcherOptions,\n credentials: 'omit',\n };\n\n const hasCredentials =\n Boolean(sessionToken) ||\n Boolean(\n resolvedConfig?.editor?.clientId && resolvedConfig?.editor?.clientSecret\n );\n\n // A session token is used as-is and never expires within the SDK's concern,\n // so inject it once up-front and skip the OAuth2 exchange.\n if (sessionToken) {\n fetcherOptions.headers = {\n ...(fetcherOptions.headers ?? {}),\n Authorization: `Bearer ${sessionToken}`,\n } as HeadersInit;\n }\n\n let accessToken: string | undefined;\n let expiryTimestamp: number | undefined;\n let pendingRefresh: Promise<void> | undefined;\n\n const needsRefresh = (): boolean => {\n if (!accessToken) return true;\n if (!expiryTimestamp) return false; // Unknown expiry: assume usable until failure.\n\n // Refresh one minute before the actual expiry to avoid edge-of-expiry races.\n return Date.now() + ONE_MINUTE_MS >= expiryTimestamp;\n };\n\n const refreshToken = async (): Promise<void> => {\n const doRefresh = async (): Promise<void> => {\n const oAuthAPI = getOAuthAPI({}, resolvedConfig);\n const response = await oAuthAPI.getOAuth2AccessToken();\n const tokenData = response?.data as OAuthTokenLike | undefined;\n\n accessToken = tokenData?.accessToken;\n expiryTimestamp = getExpiryTimestamp(tokenData);\n };\n\n // De-duplicate concurrent refreshes so a burst of calls triggers one fetch.\n pendingRefresh ??= doRefresh().finally(() => {\n pendingRefresh = undefined;\n });\n\n await pendingRefresh;\n };\n\n const applyAuthHeader = (): void => {\n if (!accessToken) return;\n fetcherOptions.headers = {\n ...(fetcherOptions.headers ?? {}),\n Authorization: `Bearer ${accessToken}`,\n } as HeadersInit;\n };\n\n const ensureAuth = async (): Promise<void> => {\n if (sessionToken) return; // Already injected; nothing to refresh.\n if (needsRefresh()) {\n await refreshToken();\n }\n applyAuthHeader();\n };\n\n const reset = (): void => {\n accessToken = undefined;\n expiryTimestamp = undefined;\n };\n\n const withAuth = <Arguments extends unknown[], Result>(\n method: AsyncMethod<Arguments, Result>,\n skipAuth = !hasCredentials\n ): AsyncMethod<Arguments, Result> => {\n // In a browser the project credentials are structurally unavailable, so an\n // authenticated call would silently go out unauthenticated and come back\n // 401. Fail with an actionable message instead: a browser must authenticate\n // as the signed-in *user* (a session token, or the session cookie on a\n // first-party origin), never as the project.\n if (skipAuth && !sessionToken && isBrowser()) {\n return async () => {\n throw new Error(\n 'Intlayer CMS credentials are not available in the browser. ' +\n '`clientSecret` is a server-side credential and is never bundled. ' +\n 'Call this from a server route, or pass a user session token: ' +\n 'createIntlayerCMS(config, { sessionToken }).'\n );\n };\n }\n\n if (skipAuth) return method;\n\n return async (...args: Arguments): Promise<Result> => {\n await ensureAuth();\n\n try {\n return await method(...args);\n } catch (error) {\n // The token may have been revoked or expired early: refresh once and\n // retry before surfacing the error. A session token cannot be\n // refreshed, so re-throw immediately in that case.\n if (sessionToken) throw error;\n\n await refreshToken();\n applyAuthHeader();\n return await method(...args);\n }\n };\n };\n\n const bindSection = <Section extends Record<string, unknown>>(\n factory: SectionFactory<Section>,\n bindOptions: BindSectionOptions = {}\n ): Section => {\n const section = factory(fetcherOptions, resolvedConfig);\n const skipAuth = bindOptions.skipAuth ?? !hasCredentials;\n\n return new Proxy(section, {\n get(target, property, receiver) {\n const value = Reflect.get(target, property, receiver);\n\n if (typeof value !== 'function') return value;\n\n return withAuth(\n (value as AsyncMethod<unknown[], unknown>).bind(target),\n skipAuth\n );\n },\n });\n };\n\n return {\n fetcherOptions,\n intlayerConfig: resolvedConfig,\n hasCredentials,\n ensureAuth,\n reset,\n withAuth,\n bindSection,\n };\n};\n"],"mappings":";;;;;;AAoCA,MAAM,gBAAgB;;AAGtB,MAAM,kBAA2B,OAAO,WAAW;;;;;AAMnD,MAAM,sBACJ,UACuB;CACvB,IAAI,CAAC,OAAO,OAAO;CAEnB,MAAM,WAAY,MAAM,wBAAwB,MAAM;CAKtD,IAAI,UAAU;EACZ,MAAM,YACJ,OAAO,aAAa,WAChB,KAAK,MAAM,QAAQ,IACnB,SAAS,UAAU;EACzB,IAAI,OAAO,cAAc,YAAY,OAAO,SAAS,SAAS,GAC5D,OAAO;CAEX;CAEA,MAAM,UAAU,MAAM,cAAc,MAAM;CAC1C,IAAI,OAAO,YAAY,YAAY,OAAO,SAAS,OAAO,GACxD,OAAO,KAAK,IAAI,IAAI,UAAU;AAIlC;;;;;;;;;;;;AAgGA,MAAa,qBACX,iBAAiD,EAAE,OAAO,GAC1D,UAA8B,CAAC,MACf;CAChB,MAAM,EAAE,qBAAqB,CAAC,GAAG,iBAAiB;CAWlD,MAAM,iBAAiB,KAAK,kBAAkB,CAAC,GAAG,EAChD,QAAQ;EACN,GAAG;EACH,cAAc,QAAQ,WAAW,sBAAsB,IAAI;CAC7D,EACF,CAAC;CAMD,MAAM,iBAAiC;EACrC,GAAG;EACH,aAAa;CACf;CAEA,MAAM,iBACJ,QAAQ,YAAY,KACpB,QACE,gBAAgB,QAAQ,YAAY,gBAAgB,QAAQ,YAC9D;CAIF,IAAI,cACF,eAAe,UAAU;EACvB,GAAI,eAAe,WAAW,CAAC;EAC/B,eAAe,UAAU;CAC3B;CAGF,IAAI;CACJ,IAAI;CACJ,IAAI;CAEJ,MAAM,qBAA8B;EAClC,IAAI,CAAC,aAAa,OAAO;EACzB,IAAI,CAAC,iBAAiB,OAAO;EAG7B,OAAO,KAAK,IAAI,IAAI,iBAAiB;CACvC;CAEA,MAAM,eAAe,YAA2B;EAC9C,MAAM,YAAY,YAA2B;GAG3C,MAAM,aAAY,MAFD,YAAY,CAAC,GAAG,cACH,CAAC,CAAC,qBAAqB,EAC3B,EAAE;GAE5B,cAAc,WAAW;GACzB,kBAAkB,mBAAmB,SAAS;EAChD;EAGA,mBAAmB,UAAU,CAAC,CAAC,cAAc;GAC3C,iBAAiB;EACnB,CAAC;EAED,MAAM;CACR;CAEA,MAAM,wBAA8B;EAClC,IAAI,CAAC,aAAa;EAClB,eAAe,UAAU;GACvB,GAAI,eAAe,WAAW,CAAC;GAC/B,eAAe,UAAU;EAC3B;CACF;CAEA,MAAM,aAAa,YAA2B;EAC5C,IAAI,cAAc;EAClB,IAAI,aAAa,GACf,MAAM,aAAa;EAErB,gBAAgB;CAClB;CAEA,MAAM,cAAoB;EACxB,cAAc;EACd,kBAAkB;CACpB;CAEA,MAAM,YACJ,QACA,WAAW,CAAC,mBACuB;EAMnC,IAAI,YAAY,CAAC,gBAAgB,UAAU,GACzC,OAAO,YAAY;GACjB,MAAM,IAAI,MACR,uOAIF;EACF;EAGF,IAAI,UAAU,OAAO;EAErB,OAAO,OAAO,GAAG,SAAqC;GACpD,MAAM,WAAW;GAEjB,IAAI;IACF,OAAO,MAAM,OAAO,GAAG,IAAI;GAC7B,SAAS,OAAO;IAId,IAAI,cAAc,MAAM;IAExB,MAAM,aAAa;IACnB,gBAAgB;IAChB,OAAO,MAAM,OAAO,GAAG,IAAI;GAC7B;EACF;CACF;CAEA,MAAM,eACJ,SACA,cAAkC,CAAC,MACvB;EACZ,MAAM,UAAU,QAAQ,gBAAgB,cAAc;EACtD,MAAM,WAAW,YAAY,YAAY,CAAC;EAE1C,OAAO,IAAI,MAAM,SAAS,EACxB,IAAI,QAAQ,UAAU,UAAU;GAC9B,MAAM,QAAQ,QAAQ,IAAI,QAAQ,UAAU,QAAQ;GAEpD,IAAI,OAAO,UAAU,YAAY,OAAO;GAExC,OAAO,SACJ,MAA0C,KAAK,MAAM,GACtD,QACF;EACF,EACF,CAAC;CACH;CAEA,OAAO;EACL;EACA,gBAAgB;EAChB;EACA;EACA;EACA;EACA;CACF;AACF"}
@@ -22,13 +22,14 @@ const getAnalyticsAPI = (authAPIOptions = {}, intlayerConfig) => {
22
22
  const getOverview = async (otherOptions = {}) => await fetcher(`${ANALYTICS_API_ROUTE}/overview`, authAPIOptions, otherOptions, { method: "GET" });
23
23
  /**
24
24
  * Audience report: distinct visitors (today / 7d / window), page views, the
25
- * daily evolution series, and locale + country breakdowns.
26
- * @param days - Rolling window size in days (default 30).
25
+ * evolution series, and locale + country breakdowns.
26
+ * @param range - Rolling window, either a named range (`1h`, `24h`, `7d`,
27
+ * `30d`, `90d`, `6mo`, `1y`, `3y`) or a number of days (default 30).
27
28
  * @returns The audience statistics.
28
29
  */
29
- const getAudience = async (days = 30, otherOptions = {}) => await fetcher(`${ANALYTICS_API_ROUTE}/audience`, authAPIOptions, otherOptions, {
30
+ const getAudience = async (range = 30, otherOptions = {}) => await fetcher(`${ANALYTICS_API_ROUTE}/audience`, authAPIOptions, otherOptions, {
30
31
  method: "GET",
31
- params: { days: String(days) }
32
+ params: typeof range === "number" ? { days: String(range) } : { range }
32
33
  });
33
34
  /**
34
35
  * Per-content exposure totals — "which content is actually shown".
@@ -1 +1 @@
1
- {"version":3,"file":"analytics.mjs","names":[],"sources":["../../../src/getIntlayerAPI/analytics.ts"],"sourcesContent":["import type {\n GetAnalyticsOverviewResult,\n GetAudienceResult,\n GetContentStatsResult,\n GetExperimentResultsResult,\n IngestAnalyticsBody,\n IngestAnalyticsResult,\n} from '@intlayer/backend';\nimport { editor } from '@intlayer/config/built';\nimport type { IntlayerConfig } from '@intlayer/types/config';\nimport { createEndpoint } from '../cms/createIntlayerCMS';\nimport { type FetcherOptions, fetcher } from '../fetcher';\n\nexport const getAnalyticsAPI = (\n authAPIOptions: FetcherOptions = {},\n intlayerConfig?: IntlayerConfig\n) => {\n const backendURL = intlayerConfig?.editor?.backendURL ?? editor.backendURL;\n\n const ANALYTICS_API_ROUTE = `${backendURL}/api/analytics`;\n\n /**\n * Ingest a batch of analytics events. Public — attribution is by the\n * project's `clientId` in the body.\n * @param body - Session id, sdk version, and the collected events.\n * @returns The number of accepted events.\n */\n const sendEvents = async (\n body: IngestAnalyticsBody,\n otherOptions: FetcherOptions = {}\n ) =>\n await fetcher<IngestAnalyticsResult>(\n `${ANALYTICS_API_ROUTE}/events`,\n authAPIOptions,\n otherOptions,\n {\n method: 'POST',\n body,\n }\n );\n\n /**\n * Page/locale totals for the authenticated project.\n * @returns Views grouped by url and locale.\n */\n const getOverview = async (otherOptions: FetcherOptions = {}) =>\n await fetcher<GetAnalyticsOverviewResult>(\n `${ANALYTICS_API_ROUTE}/overview`,\n authAPIOptions,\n otherOptions,\n { method: 'GET' }\n );\n\n /**\n * Audience report: distinct visitors (today / 7d / window), page views, the\n * daily evolution series, and locale + country breakdowns.\n * @param days - Rolling window size in days (default 30).\n * @returns The audience statistics.\n */\n const getAudience = async (days = 30, otherOptions: FetcherOptions = {}) =>\n await fetcher<GetAudienceResult>(\n `${ANALYTICS_API_ROUTE}/audience`,\n authAPIOptions,\n otherOptions,\n {\n method: 'GET',\n params: { days: String(days) },\n }\n );\n\n /**\n * Per-content exposure totals — \"which content is actually shown\".\n * @returns Exposures grouped by dictionary key, key path, and locale.\n */\n const getContentStats = async (otherOptions: FetcherOptions = {}) =>\n await fetcher<GetContentStatsResult>(\n `${ANALYTICS_API_ROUTE}/content-stats`,\n authAPIOptions,\n otherOptions,\n { method: 'GET' }\n );\n\n /**\n * A/B experiment results with per-variant conversion rates and significance.\n * @param experimentKey - The experiment to evaluate.\n * @returns The experiment result set.\n */\n const getExperimentResults = async (\n experimentKey: string,\n otherOptions: FetcherOptions = {}\n ) =>\n await fetcher<GetExperimentResultsResult>(\n `${ANALYTICS_API_ROUTE}/experiments/${experimentKey}`,\n authAPIOptions,\n otherOptions,\n { method: 'GET' }\n );\n\n return {\n sendEvents,\n getOverview,\n getAudience,\n getContentStats,\n getExperimentResults,\n };\n};\n\n/**\n * Authenticated `analytics` endpoint bound to an Intlayer CMS authenticator.\n *\n * Pass an authenticator created with `createIntlayerCMS`, or omit it to use\n * the build-time configuration (`@intlayer/config/built`).\n */\nexport const analyticsEndpoint = createEndpoint(getAnalyticsAPI);\n"],"mappings":";;;;;AAaA,MAAa,mBACX,iBAAiC,CAAC,GAClC,mBACG;CAGH,MAAM,sBAAsB,GAFT,gBAAgB,QAAQ,cAAc,OAAO,WAEtB;;;;;;;CAQ1C,MAAM,aAAa,OACjB,MACA,eAA+B,CAAC,MAEhC,MAAM,QACJ,GAAG,oBAAoB,UACvB,gBACA,cACA;EACE,QAAQ;EACR;CACF,CACF;;;;;CAMF,MAAM,cAAc,OAAO,eAA+B,CAAC,MACzD,MAAM,QACJ,GAAG,oBAAoB,YACvB,gBACA,cACA,EAAE,QAAQ,MAAM,CAClB;;;;;;;CAQF,MAAM,cAAc,OAAO,OAAO,IAAI,eAA+B,CAAC,MACpE,MAAM,QACJ,GAAG,oBAAoB,YACvB,gBACA,cACA;EACE,QAAQ;EACR,QAAQ,EAAE,MAAM,OAAO,IAAI,EAAE;CAC/B,CACF;;;;;CAMF,MAAM,kBAAkB,OAAO,eAA+B,CAAC,MAC7D,MAAM,QACJ,GAAG,oBAAoB,iBACvB,gBACA,cACA,EAAE,QAAQ,MAAM,CAClB;;;;;;CAOF,MAAM,uBAAuB,OAC3B,eACA,eAA+B,CAAC,MAEhC,MAAM,QACJ,GAAG,oBAAoB,eAAe,iBACtC,gBACA,cACA,EAAE,QAAQ,MAAM,CAClB;CAEF,OAAO;EACL;EACA;EACA;EACA;EACA;CACF;AACF;;;;;;;AAQA,MAAa,oBAAoB,eAAe,eAAe"}
1
+ {"version":3,"file":"analytics.mjs","names":[],"sources":["../../../src/getIntlayerAPI/analytics.ts"],"sourcesContent":["import type {\n AudienceRange,\n GetAnalyticsOverviewResult,\n GetAudienceResult,\n GetContentStatsResult,\n GetExperimentResultsResult,\n IngestAnalyticsBody,\n IngestAnalyticsResult,\n} from '@intlayer/backend';\nimport { editor } from '@intlayer/config/built';\nimport type { IntlayerConfig } from '@intlayer/types/config';\nimport { createEndpoint } from '../cms/createIntlayerCMS';\nimport { type FetcherOptions, fetcher } from '../fetcher';\n\nexport const getAnalyticsAPI = (\n authAPIOptions: FetcherOptions = {},\n intlayerConfig?: IntlayerConfig\n) => {\n const backendURL = intlayerConfig?.editor?.backendURL ?? editor.backendURL;\n\n const ANALYTICS_API_ROUTE = `${backendURL}/api/analytics`;\n\n /**\n * Ingest a batch of analytics events. Public — attribution is by the\n * project's `clientId` in the body.\n * @param body - Session id, sdk version, and the collected events.\n * @returns The number of accepted events.\n */\n const sendEvents = async (\n body: IngestAnalyticsBody,\n otherOptions: FetcherOptions = {}\n ) =>\n await fetcher<IngestAnalyticsResult>(\n `${ANALYTICS_API_ROUTE}/events`,\n authAPIOptions,\n otherOptions,\n {\n method: 'POST',\n body,\n }\n );\n\n /**\n * Page/locale totals for the authenticated project.\n * @returns Views grouped by url and locale.\n */\n const getOverview = async (otherOptions: FetcherOptions = {}) =>\n await fetcher<GetAnalyticsOverviewResult>(\n `${ANALYTICS_API_ROUTE}/overview`,\n authAPIOptions,\n otherOptions,\n { method: 'GET' }\n );\n\n /**\n * Audience report: distinct visitors (today / 7d / window), page views, the\n * evolution series, and locale + country breakdowns.\n * @param range - Rolling window, either a named range (`1h`, `24h`, `7d`,\n * `30d`, `90d`, `6mo`, `1y`, `3y`) or a number of days (default 30).\n * @returns The audience statistics.\n */\n const getAudience = async (\n range: AudienceRange | number = 30,\n otherOptions: FetcherOptions = {}\n ) =>\n await fetcher<GetAudienceResult>(\n `${ANALYTICS_API_ROUTE}/audience`,\n authAPIOptions,\n otherOptions,\n {\n method: 'GET',\n params: typeof range === 'number' ? { days: String(range) } : { range },\n }\n );\n\n /**\n * Per-content exposure totals — \"which content is actually shown\".\n * @returns Exposures grouped by dictionary key, key path, and locale.\n */\n const getContentStats = async (otherOptions: FetcherOptions = {}) =>\n await fetcher<GetContentStatsResult>(\n `${ANALYTICS_API_ROUTE}/content-stats`,\n authAPIOptions,\n otherOptions,\n { method: 'GET' }\n );\n\n /**\n * A/B experiment results with per-variant conversion rates and significance.\n * @param experimentKey - The experiment to evaluate.\n * @returns The experiment result set.\n */\n const getExperimentResults = async (\n experimentKey: string,\n otherOptions: FetcherOptions = {}\n ) =>\n await fetcher<GetExperimentResultsResult>(\n `${ANALYTICS_API_ROUTE}/experiments/${experimentKey}`,\n authAPIOptions,\n otherOptions,\n { method: 'GET' }\n );\n\n return {\n sendEvents,\n getOverview,\n getAudience,\n getContentStats,\n getExperimentResults,\n };\n};\n\n/**\n * Authenticated `analytics` endpoint bound to an Intlayer CMS authenticator.\n *\n * Pass an authenticator created with `createIntlayerCMS`, or omit it to use\n * the build-time configuration (`@intlayer/config/built`).\n */\nexport const analyticsEndpoint = createEndpoint(getAnalyticsAPI);\n"],"mappings":";;;;;AAcA,MAAa,mBACX,iBAAiC,CAAC,GAClC,mBACG;CAGH,MAAM,sBAAsB,GAFT,gBAAgB,QAAQ,cAAc,OAAO,WAEtB;;;;;;;CAQ1C,MAAM,aAAa,OACjB,MACA,eAA+B,CAAC,MAEhC,MAAM,QACJ,GAAG,oBAAoB,UACvB,gBACA,cACA;EACE,QAAQ;EACR;CACF,CACF;;;;;CAMF,MAAM,cAAc,OAAO,eAA+B,CAAC,MACzD,MAAM,QACJ,GAAG,oBAAoB,YACvB,gBACA,cACA,EAAE,QAAQ,MAAM,CAClB;;;;;;;;CASF,MAAM,cAAc,OAClB,QAAgC,IAChC,eAA+B,CAAC,MAEhC,MAAM,QACJ,GAAG,oBAAoB,YACvB,gBACA,cACA;EACE,QAAQ;EACR,QAAQ,OAAO,UAAU,WAAW,EAAE,MAAM,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM;CACxE,CACF;;;;;CAMF,MAAM,kBAAkB,OAAO,eAA+B,CAAC,MAC7D,MAAM,QACJ,GAAG,oBAAoB,iBACvB,gBACA,cACA,EAAE,QAAQ,MAAM,CAClB;;;;;;CAOF,MAAM,uBAAuB,OAC3B,eACA,eAA+B,CAAC,MAEhC,MAAM,QACJ,GAAG,oBAAoB,eAAe,iBACtC,gBACA,cACA,EAAE,QAAQ,MAAM,CAClB;CAEF,OAAO;EACL;EACA;EACA;EACA;EACA;CACF;AACF;;;;;;;AAQA,MAAa,oBAAoB,eAAe,eAAe"}
@@ -0,0 +1,122 @@
1
+ import { editor } from "@intlayer/config/built";
2
+
3
+ //#region src/public/createPublicClient.ts
4
+ /**
5
+ * Browser-safe Intlayer client, authenticated without any confidential
6
+ * credential.
7
+ *
8
+ * This is the Google-Analytics model applied to the whole SDK: the page ships
9
+ * only the **public** `editor.clientId`, exchanges it for a short-lived scoped
10
+ * token, and calls the API directly. No server route, no server action, and no
11
+ * `clientSecret` in the bundle.
12
+ *
13
+ * What it can do is deliberately narrow, and that is what makes shipping the
14
+ * key safe: read the project's published dictionary content — the content the
15
+ * page already renders — and submit analytics events. Writing content, reading
16
+ * a project, an organization or a user still require a real credential
17
+ * (server-side) or a signed-in user.
18
+ *
19
+ * @example
20
+ * ```ts
21
+ * import { createPublicClient } from '@intlayer/api/public';
22
+ *
23
+ * const client = createPublicClient();
24
+ *
25
+ * const keys = await client.getDictionaryKeys();
26
+ * const [dictionary] = await client.getDictionaries(['navbar']);
27
+ * ```
28
+ */
29
+ /** `sessionStorage` key holding the cached token for this tab. */
30
+ const TOKEN_STORAGE_KEY = "__intlayer_public_token__";
31
+ /**
32
+ * Refresh margin: a token within this window of expiry is treated as expired,
33
+ * so a request is never sent with a token that dies in flight.
34
+ */
35
+ const EXPIRY_MARGIN_MS = 6e4;
36
+ /** Reads the cached token for this tab, ignoring an unusable cache. */
37
+ const readStoredToken = () => {
38
+ try {
39
+ const raw = globalThis.sessionStorage?.getItem(TOKEN_STORAGE_KEY);
40
+ if (!raw) return null;
41
+ const parsed = JSON.parse(raw);
42
+ if (typeof parsed?.token !== "string") return null;
43
+ if (typeof parsed?.expiresAt !== "number") return null;
44
+ return parsed;
45
+ } catch {
46
+ return null;
47
+ }
48
+ };
49
+ /** Persists the token for this tab. Best-effort: storage may be unavailable. */
50
+ const writeStoredToken = (cached) => {
51
+ try {
52
+ globalThis.sessionStorage?.setItem(TOKEN_STORAGE_KEY, JSON.stringify(cached));
53
+ } catch {}
54
+ };
55
+ /**
56
+ * Creates a {@link PublicClient} for the configured project.
57
+ *
58
+ * @param options - Optional configuration override.
59
+ * @returns A client that authenticates itself with the public project key.
60
+ */
61
+ const createPublicClient = ({ intlayerConfig } = {}) => {
62
+ const resolvedEditor = intlayerConfig?.editor ?? editor;
63
+ const backendURL = (resolvedEditor?.backendURL ?? "").replace(/\/$/, "");
64
+ const clientId = resolvedEditor?.clientId;
65
+ let cached = null;
66
+ let pendingExchange = null;
67
+ const isUsable = (candidate) => candidate !== null && candidate.expiresAt - EXPIRY_MARGIN_MS > Date.now();
68
+ const exchange = async () => {
69
+ const response = await fetch(`${backendURL}/api/public/token`, {
70
+ method: "POST",
71
+ headers: { "Content-Type": "application/json" },
72
+ body: JSON.stringify({ clientId }),
73
+ credentials: "omit",
74
+ mode: "cors"
75
+ });
76
+ if (!response.ok) return null;
77
+ const payload = await response.json();
78
+ const token = payload?.data?.token;
79
+ const expiresIn = payload?.data?.expiresIn ?? 0;
80
+ if (!token || expiresIn <= 0) return null;
81
+ cached = {
82
+ token,
83
+ expiresAt: Date.now() + expiresIn * 1e3
84
+ };
85
+ writeStoredToken(cached);
86
+ return token;
87
+ };
88
+ const getToken = async () => {
89
+ if (isUsable(cached)) return cached.token;
90
+ const stored = readStoredToken();
91
+ if (isUsable(stored)) {
92
+ cached = stored;
93
+ return stored.token;
94
+ }
95
+ if (!clientId) return null;
96
+ pendingExchange ??= exchange().finally(() => {
97
+ pendingExchange = null;
98
+ });
99
+ return await pendingExchange;
100
+ };
101
+ /** Issues an authorised GET against the public surface. */
102
+ const request = async (path, fallback) => {
103
+ const token = await getToken();
104
+ if (!token) return fallback;
105
+ const response = await fetch(`${backendURL}/api/public${path}`, {
106
+ headers: { Authorization: `Bearer ${token}` },
107
+ credentials: "omit",
108
+ mode: "cors"
109
+ });
110
+ if (!response.ok) return fallback;
111
+ return (await response.json())?.data ?? fallback;
112
+ };
113
+ return {
114
+ getToken,
115
+ getDictionaryKeys: () => request("/dictionaries/keys", []),
116
+ getDictionaries: (keys) => request(`/dictionaries?keys=${encodeURIComponent(keys.join(","))}`, [])
117
+ };
118
+ };
119
+
120
+ //#endregion
121
+ export { createPublicClient };
122
+ //# sourceMappingURL=createPublicClient.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"createPublicClient.mjs","names":[],"sources":["../../../src/public/createPublicClient.ts"],"sourcesContent":["import type { DictionaryAPI } from '@intlayer/backend';\nimport { editor } from '@intlayer/config/built';\nimport type { IntlayerConfig } from '@intlayer/types/config';\n\n/**\n * Browser-safe Intlayer client, authenticated without any confidential\n * credential.\n *\n * This is the Google-Analytics model applied to the whole SDK: the page ships\n * only the **public** `editor.clientId`, exchanges it for a short-lived scoped\n * token, and calls the API directly. No server route, no server action, and no\n * `clientSecret` in the bundle.\n *\n * What it can do is deliberately narrow, and that is what makes shipping the\n * key safe: read the project's published dictionary content — the content the\n * page already renders — and submit analytics events. Writing content, reading\n * a project, an organization or a user still require a real credential\n * (server-side) or a signed-in user.\n *\n * @example\n * ```ts\n * import { createPublicClient } from '@intlayer/api/public';\n *\n * const client = createPublicClient();\n *\n * const keys = await client.getDictionaryKeys();\n * const [dictionary] = await client.getDictionaries(['navbar']);\n * ```\n */\n\n/** `sessionStorage` key holding the cached token for this tab. */\nconst TOKEN_STORAGE_KEY = '__intlayer_public_token__';\n\n/**\n * Refresh margin: a token within this window of expiry is treated as expired,\n * so a request is never sent with a token that dies in flight.\n */\nconst EXPIRY_MARGIN_MS = 60 * 1000;\n\ntype CachedToken = {\n token: string;\n /** Epoch milliseconds. */\n expiresAt: number;\n};\n\n/** Reads the cached token for this tab, ignoring an unusable cache. */\nconst readStoredToken = (): CachedToken | null => {\n try {\n const raw = globalThis.sessionStorage?.getItem(TOKEN_STORAGE_KEY);\n if (!raw) return null;\n\n const parsed = JSON.parse(raw) as CachedToken;\n\n if (typeof parsed?.token !== 'string') return null;\n if (typeof parsed?.expiresAt !== 'number') return null;\n\n return parsed;\n } catch {\n // Private mode, disabled storage, or a corrupted entry — start fresh.\n return null;\n }\n};\n\n/** Persists the token for this tab. Best-effort: storage may be unavailable. */\nconst writeStoredToken = (cached: CachedToken): void => {\n try {\n globalThis.sessionStorage?.setItem(\n TOKEN_STORAGE_KEY,\n JSON.stringify(cached)\n );\n } catch {\n /* ignore — the in-memory cache still applies for this page */\n }\n};\n\n/** Options accepted by {@link createPublicClient}. */\nexport type PublicClientOptions = {\n /** Overrides the build-time `editor` configuration. */\n intlayerConfig?: Pick<IntlayerConfig, 'editor'>;\n};\n\n/** The credential-free client returned by {@link createPublicClient}. */\nexport type PublicClient = {\n /**\n * Returns the current token, exchanging the public `clientId` for one when\n * needed. `null` when the project is unknown or the origin is not allowed.\n */\n getToken: () => Promise<string | null>;\n /** Every dictionary key published for the project. */\n getDictionaryKeys: () => Promise<string[]>;\n /**\n * Published dictionary content for the given keys.\n *\n * @param keys - Dictionary keys to fetch (at most 100 per call).\n */\n getDictionaries: (keys: string[]) => Promise<DictionaryAPI[]>;\n};\n\n/**\n * Creates a {@link PublicClient} for the configured project.\n *\n * @param options - Optional configuration override.\n * @returns A client that authenticates itself with the public project key.\n */\nexport const createPublicClient = ({\n intlayerConfig,\n}: PublicClientOptions = {}): PublicClient => {\n const resolvedEditor = intlayerConfig?.editor ?? editor;\n const backendURL = (resolvedEditor?.backendURL ?? '').replace(/\\/$/, '');\n const clientId = resolvedEditor?.clientId;\n\n let cached: CachedToken | null = null;\n let pendingExchange: Promise<string | null> | null = null;\n\n const isUsable = (candidate: CachedToken | null): candidate is CachedToken =>\n candidate !== null && candidate.expiresAt - EXPIRY_MARGIN_MS > Date.now();\n\n const exchange = async (): Promise<string | null> => {\n const response = await fetch(`${backendURL}/api/public/token`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ clientId }),\n // Authorised by `Origin`, never by cookies.\n credentials: 'omit',\n mode: 'cors',\n });\n\n if (!response.ok) return null;\n\n const payload = (await response.json()) as {\n data?: { token?: string | null; expiresIn?: number };\n };\n\n const token = payload?.data?.token;\n const expiresIn = payload?.data?.expiresIn ?? 0;\n\n if (!token || expiresIn <= 0) return null;\n\n cached = { token, expiresAt: Date.now() + expiresIn * 1000 };\n writeStoredToken(cached);\n\n return token;\n };\n\n const getToken = async (): Promise<string | null> => {\n if (isUsable(cached)) return cached.token;\n\n const stored = readStoredToken();\n if (isUsable(stored)) {\n cached = stored;\n return stored.token;\n }\n\n if (!clientId) return null;\n\n // De-duplicate concurrent exchanges so a burst of calls triggers one fetch.\n pendingExchange ??= exchange().finally(() => {\n pendingExchange = null;\n });\n\n return await pendingExchange;\n };\n\n /** Issues an authorised GET against the public surface. */\n const request = async <Result>(\n path: string,\n fallback: Result\n ): Promise<Result> => {\n const token = await getToken();\n\n if (!token) return fallback;\n\n const response = await fetch(`${backendURL}/api/public${path}`, {\n headers: { Authorization: `Bearer ${token}` },\n credentials: 'omit',\n mode: 'cors',\n });\n\n if (!response.ok) return fallback;\n\n const payload = (await response.json()) as { data?: Result };\n\n return payload?.data ?? fallback;\n };\n\n return {\n getToken,\n getDictionaryKeys: () => request<string[]>('/dictionaries/keys', []),\n getDictionaries: (keys) =>\n request<DictionaryAPI[]>(\n `/dictionaries?keys=${encodeURIComponent(keys.join(','))}`,\n []\n ),\n };\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA+BA,MAAM,oBAAoB;;;;;AAM1B,MAAM,mBAAmB;;AASzB,MAAM,wBAA4C;CAChD,IAAI;EACF,MAAM,MAAM,WAAW,gBAAgB,QAAQ,iBAAiB;EAChE,IAAI,CAAC,KAAK,OAAO;EAEjB,MAAM,SAAS,KAAK,MAAM,GAAG;EAE7B,IAAI,OAAO,QAAQ,UAAU,UAAU,OAAO;EAC9C,IAAI,OAAO,QAAQ,cAAc,UAAU,OAAO;EAElD,OAAO;CACT,QAAQ;EAEN,OAAO;CACT;AACF;;AAGA,MAAM,oBAAoB,WAA8B;CACtD,IAAI;EACF,WAAW,gBAAgB,QACzB,mBACA,KAAK,UAAU,MAAM,CACvB;CACF,QAAQ,CAER;AACF;;;;;;;AA+BA,MAAa,sBAAsB,EACjC,mBACuB,CAAC,MAAoB;CAC5C,MAAM,iBAAiB,gBAAgB,UAAU;CACjD,MAAM,cAAc,gBAAgB,cAAc,GAAE,CAAE,QAAQ,OAAO,EAAE;CACvE,MAAM,WAAW,gBAAgB;CAEjC,IAAI,SAA6B;CACjC,IAAI,kBAAiD;CAErD,MAAM,YAAY,cAChB,cAAc,QAAQ,UAAU,YAAY,mBAAmB,KAAK,IAAI;CAE1E,MAAM,WAAW,YAAoC;EACnD,MAAM,WAAW,MAAM,MAAM,GAAG,WAAW,oBAAoB;GAC7D,QAAQ;GACR,SAAS,EAAE,gBAAgB,mBAAmB;GAC9C,MAAM,KAAK,UAAU,EAAE,SAAS,CAAC;GAEjC,aAAa;GACb,MAAM;EACR,CAAC;EAED,IAAI,CAAC,SAAS,IAAI,OAAO;EAEzB,MAAM,UAAW,MAAM,SAAS,KAAK;EAIrC,MAAM,QAAQ,SAAS,MAAM;EAC7B,MAAM,YAAY,SAAS,MAAM,aAAa;EAE9C,IAAI,CAAC,SAAS,aAAa,GAAG,OAAO;EAErC,SAAS;GAAE;GAAO,WAAW,KAAK,IAAI,IAAI,YAAY;EAAK;EAC3D,iBAAiB,MAAM;EAEvB,OAAO;CACT;CAEA,MAAM,WAAW,YAAoC;EACnD,IAAI,SAAS,MAAM,GAAG,OAAO,OAAO;EAEpC,MAAM,SAAS,gBAAgB;EAC/B,IAAI,SAAS,MAAM,GAAG;GACpB,SAAS;GACT,OAAO,OAAO;EAChB;EAEA,IAAI,CAAC,UAAU,OAAO;EAGtB,oBAAoB,SAAS,CAAC,CAAC,cAAc;GAC3C,kBAAkB;EACpB,CAAC;EAED,OAAO,MAAM;CACf;;CAGA,MAAM,UAAU,OACd,MACA,aACoB;EACpB,MAAM,QAAQ,MAAM,SAAS;EAE7B,IAAI,CAAC,OAAO,OAAO;EAEnB,MAAM,WAAW,MAAM,MAAM,GAAG,WAAW,aAAa,QAAQ;GAC9D,SAAS,EAAE,eAAe,UAAU,QAAQ;GAC5C,aAAa;GACb,MAAM;EACR,CAAC;EAED,IAAI,CAAC,SAAS,IAAI,OAAO;EAIzB,QAAO,MAFgB,SAAS,KAAK,EAEvB,EAAE,QAAQ;CAC1B;CAEA,OAAO;EACL;EACA,yBAAyB,QAAkB,sBAAsB,CAAC,CAAC;EACnE,kBAAkB,SAChB,QACE,sBAAsB,mBAAmB,KAAK,KAAK,GAAG,CAAC,KACvD,CAAC,CACH;CACJ;AACF"}
@@ -0,0 +1,3 @@
1
+ import { createPublicClient } from "./createPublicClient.mjs";
2
+
3
+ export { createPublicClient };
@@ -1 +1 @@
1
- {"version":3,"file":"createAuthManager.d.ts","names":[],"sources":["../../../src/cms/createAuthManager.ts"],"mappings":";;;;;;;KAsBK,YAAY,6BAA6B,cACzC,MAAM,cACN,QAAQ;;;;;KAMR,eAAe,gBAAgB,4BAClC,gBAAgB,gBAChB,gBAAgB,mBACb;;;;KAuCO;;;;EAIV,qBAAqB;;;;;;EAMrB;;;;;KAMU;;;;;EAKV;;;;;;;;;;KAWU;;;;;EAKV,gBAAgB;;;;EAIhB,gBAAgB;;;;;EAKhB;;;;;EAKA,kBAAkB;;;;EAIlB;;;;;EAKA,WAAW,6BAA6B,QACtC,QAAQ,YAAY,WAAW,SAC/B,uBACG,YAAY,WAAW;;;;;;;;;;;EAW5B,cAAc,gBAAgB,yBAC5B,SAAS,eAAe,UACxB,UAAU,uBACP;;;;;;;;;;;;;cAcM,oBAAiB,iBACZ,KAAK,2BAAyB,UACrC,uBACR"}
1
+ {"version":3,"file":"createAuthManager.d.ts","names":[],"sources":["../../../src/cms/createAuthManager.ts"],"mappings":";;;;;;;KAuBK,YAAY,6BAA6B,cACzC,MAAM,cACN,QAAQ;;;;;KAMR,eAAe,gBAAgB,4BAClC,gBAAgB,gBAChB,gBAAgB,mBACb;;;;KA0CO;;;;EAIV,qBAAqB;;;;;;EAMrB;;;;;KAMU;;;;;EAKV;;;;;;;;;;KAWU;;;;;EAKV,gBAAgB;;;;EAIhB,gBAAgB;;;;;EAKhB;;;;;EAKA,kBAAkB;;;;EAIlB;;;;;EAKA,WAAW,6BAA6B,QACtC,QAAQ,YAAY,WAAW,SAC/B,uBACG,YAAY,WAAW;;;;;;;;;;;EAW5B,cAAc,gBAAgB,yBAC5B,SAAS,eAAe,UACxB,UAAU,uBACP;;;;;;;;;;;;;cAcM,oBAAiB,iBACZ,KAAK,2BAAyB,UACrC,uBACR"}