@rynt/sdk 0.9.53

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 (332) hide show
  1. package/README.md +122 -0
  2. package/REGISTRIES.md +189 -0
  3. package/env.d.ts +11 -0
  4. package/host-shims.d.ts +30 -0
  5. package/package.json +88 -0
  6. package/src/extension-marketplace/api-types.ts +141 -0
  7. package/src/extension-marketplace/client.ts +296 -0
  8. package/src/extension-marketplace/index.ts +22 -0
  9. package/src/extension-marketplace/schemas.ts +178 -0
  10. package/src/extensions/ExtensionRoutePage.vue +17 -0
  11. package/src/extensions/context.ts +37 -0
  12. package/src/extensions/disabled-folder.ts +21 -0
  13. package/src/extensions/extension-expose-map.ts +5 -0
  14. package/src/extensions/extension-expose.ts +48 -0
  15. package/src/extensions/graph.ts +67 -0
  16. package/src/extensions/index.ts +251 -0
  17. package/src/extensions/invite-handler/types.ts +20 -0
  18. package/src/extensions/launcher-entities/create-launcher-entity.ts +25 -0
  19. package/src/extensions/launcher-entities/keys.ts +46 -0
  20. package/src/extensions/launcher-entities/launcher-entity-components.ts +177 -0
  21. package/src/extensions/launcher-entities/props-map.ts +69 -0
  22. package/src/extensions/launcher-entities/registry.ts +32 -0
  23. package/src/extensions/launcher-models/apis/accounts-contracts.ts +102 -0
  24. package/src/extensions/launcher-models/apis/launcher-model-apis.ts +553 -0
  25. package/src/extensions/launcher-models/keys.ts +23 -0
  26. package/src/extensions/launcher-models/public.ts +9 -0
  27. package/src/extensions/launcher-models/registry-core.ts +34 -0
  28. package/src/extensions/manifest-types.ts +22 -0
  29. package/src/extensions/manifest.ts +46 -0
  30. package/src/extensions/marketplace-open-key.ts +26 -0
  31. package/src/extensions/plugin-types.ts +44 -0
  32. package/src/extensions/plugin.ts +62 -0
  33. package/src/extensions/registries/bootstrap.ts +11 -0
  34. package/src/extensions/registries/builtins/account-provider.ts +6 -0
  35. package/src/extensions/registries/builtins/app-topbar-left-widgets.ts +6 -0
  36. package/src/extensions/registries/builtins/app-topbar-right-widgets.ts +6 -0
  37. package/src/extensions/registries/builtins/app-topbar-status-widgets.ts +6 -0
  38. package/src/extensions/registries/builtins/build-card-actions.ts +6 -0
  39. package/src/extensions/registries/builtins/build-card-after-meta.ts +6 -0
  40. package/src/extensions/registries/builtins/build-card-before-media.ts +6 -0
  41. package/src/extensions/registries/builtins/build-card-before-meta.ts +6 -0
  42. package/src/extensions/registries/builtins/build-card-footer-actions.ts +6 -0
  43. package/src/extensions/registries/builtins/build-detail-after-content.ts +6 -0
  44. package/src/extensions/registries/builtins/build-detail-before-content.ts +6 -0
  45. package/src/extensions/registries/builtins/build-detail-before-hero.ts +6 -0
  46. package/src/extensions/registries/builtins/build-detail-header-actions.ts +6 -0
  47. package/src/extensions/registries/builtins/build-detail-mod-row-actions.ts +6 -0
  48. package/src/extensions/registries/builtins/build-detail-resourcepack-row-actions.ts +6 -0
  49. package/src/extensions/registries/builtins/build-detail-right-column-bottom.ts +6 -0
  50. package/src/extensions/registries/builtins/build-detail-right-column-top.ts +6 -0
  51. package/src/extensions/registries/builtins/dialog-footer-actions.ts +6 -0
  52. package/src/extensions/registries/builtins/feed-after-content.ts +6 -0
  53. package/src/extensions/registries/builtins/feed-before-content.ts +6 -0
  54. package/src/extensions/registries/builtins/file-editor.ts +19 -0
  55. package/src/extensions/registries/builtins/friends-after-list.ts +6 -0
  56. package/src/extensions/registries/builtins/friends-before-list.ts +6 -0
  57. package/src/extensions/registries/builtins/index.ts +141 -0
  58. package/src/extensions/registries/builtins/invite-handler.ts +7 -0
  59. package/src/extensions/registries/builtins/library-after-content.ts +6 -0
  60. package/src/extensions/registries/builtins/library-before-content.ts +6 -0
  61. package/src/extensions/registries/builtins/loader.ts +8 -0
  62. package/src/extensions/registries/builtins/map-card-actions.ts +6 -0
  63. package/src/extensions/registries/builtins/map-card-after-meta.ts +6 -0
  64. package/src/extensions/registries/builtins/map-card-before-meta.ts +6 -0
  65. package/src/extensions/registries/builtins/map-card-footer-actions.ts +6 -0
  66. package/src/extensions/registries/builtins/map-detail-after-content.ts +6 -0
  67. package/src/extensions/registries/builtins/map-detail-before-content.ts +6 -0
  68. package/src/extensions/registries/builtins/map-detail-header-actions.ts +6 -0
  69. package/src/extensions/registries/builtins/markdown-editor-tiptap-extensions.ts +7 -0
  70. package/src/extensions/registries/builtins/markdown-editor-toolbar-actions.ts +6 -0
  71. package/src/extensions/registries/builtins/markdown-renderer-after-content.ts +6 -0
  72. package/src/extensions/registries/builtins/markdown-renderer-before-content.ts +6 -0
  73. package/src/extensions/registries/builtins/mod-details-footer-actions.ts +6 -0
  74. package/src/extensions/registries/builtins/mod-manage-actions.ts +6 -0
  75. package/src/extensions/registries/builtins/mod-provider.ts +5 -0
  76. package/src/extensions/registries/builtins/nav.ts +7 -0
  77. package/src/extensions/registries/builtins/page.ts +13 -0
  78. package/src/extensions/registries/builtins/projects-after-content.ts +6 -0
  79. package/src/extensions/registries/builtins/projects-before-content.ts +6 -0
  80. package/src/extensions/registries/builtins/resourcepack-manage-actions.ts +7 -0
  81. package/src/extensions/registries/builtins/server-card-actions.ts +6 -0
  82. package/src/extensions/registries/builtins/server-card-after-meta.ts +6 -0
  83. package/src/extensions/registries/builtins/server-card-before-meta.ts +6 -0
  84. package/src/extensions/registries/builtins/server-card-footer-actions.ts +6 -0
  85. package/src/extensions/registries/builtins/server-detail-after-content.ts +6 -0
  86. package/src/extensions/registries/builtins/server-detail-before-content.ts +6 -0
  87. package/src/extensions/registries/builtins/server-detail-header-actions.ts +6 -0
  88. package/src/extensions/registries/builtins/settings-after-sections.ts +6 -0
  89. package/src/extensions/registries/builtins/settings-before-sections.ts +6 -0
  90. package/src/extensions/registries/builtins/settings-section-widgets.ts +6 -0
  91. package/src/extensions/registries/builtins/shaderpack-manage-actions.ts +7 -0
  92. package/src/extensions/registries/builtins/shell.ts +5 -0
  93. package/src/extensions/registries/builtins/sidebar-after-content.ts +6 -0
  94. package/src/extensions/registries/builtins/sidebar-before-content.ts +6 -0
  95. package/src/extensions/registries/builtins/sidebar-footer-widgets.ts +6 -0
  96. package/src/extensions/registries/builtins/sidebar-header-widgets.ts +6 -0
  97. package/src/extensions/registries/builtins/sidebar.ts +11 -0
  98. package/src/extensions/registries/builtins/theme.ts +5 -0
  99. package/src/extensions/registries/builtins/user-card-after-meta.ts +6 -0
  100. package/src/extensions/registries/builtins/user-card-before-meta.ts +6 -0
  101. package/src/extensions/registries/builtins/user-menu-actions.ts +6 -0
  102. package/src/extensions/registries/builtins/user-menu-after-actions.ts +6 -0
  103. package/src/extensions/registries/builtins/user-menu-before-actions.ts +6 -0
  104. package/src/extensions/registries/builtins/user-strip.ts +5 -0
  105. package/src/extensions/registries/clear-extension-ui-registries.ts +15 -0
  106. package/src/extensions/registries/define-extension-registry.ts +58 -0
  107. package/src/extensions/registries/extension-host-api.ts +41 -0
  108. package/src/extensions/registries/extension-registry-api.ts +103 -0
  109. package/src/extensions/registries/extension-registry-payload-map.ts +9 -0
  110. package/src/extensions/registries/extension-scope.ts +41 -0
  111. package/src/extensions/registries/get-registry.ts +23 -0
  112. package/src/extensions/registries/index.ts +58 -0
  113. package/src/extensions/registries/manifest-rynt.ts +193 -0
  114. package/src/extensions/registries/registry-slot.ts +40 -0
  115. package/src/extensions/registries/registry-value-map.ts +89 -0
  116. package/src/extensions/registries/store.ts +206 -0
  117. package/src/extensions/resolve-extensions.ts +245 -0
  118. package/src/extensions/router-bridge.ts +103 -0
  119. package/src/extensions/session.ts +6 -0
  120. package/src/extensions/slug.ts +23 -0
  121. package/src/extensions/version.ts +147 -0
  122. package/src/host/extensions-composables.ts +33 -0
  123. package/src/host/extensions-init.ts +194 -0
  124. package/src/host/index.ts +11 -0
  125. package/src/host/launcher-models/index.ts +4 -0
  126. package/src/index.ts +229 -0
  127. package/src/minecraft-loader/base-loader.ts +102 -0
  128. package/src/minecraft-loader/index.ts +11 -0
  129. package/src/minecraft-loader/loader-registry.ts +72 -0
  130. package/src/shared/api/assets.ts +112 -0
  131. package/src/shared/api/auth.ts +283 -0
  132. package/src/shared/api/builds.ts +647 -0
  133. package/src/shared/api/config.ts +19 -0
  134. package/src/shared/api/download-stats.ts +103 -0
  135. package/src/shared/api/downloads.ts +36 -0
  136. package/src/shared/api/entity-authorship.ts +60 -0
  137. package/src/shared/api/events.ts +393 -0
  138. package/src/shared/api/friends.ts +140 -0
  139. package/src/shared/api/graphql.ts +87 -0
  140. package/src/shared/api/index.ts +23 -0
  141. package/src/shared/api/invites.ts +262 -0
  142. package/src/shared/api/library.ts +44 -0
  143. package/src/shared/api/maps.ts +385 -0
  144. package/src/shared/api/notify-websocket.ts +140 -0
  145. package/src/shared/api/posts.ts +357 -0
  146. package/src/shared/api/projectServers.ts +379 -0
  147. package/src/shared/api/serverMembers.ts +173 -0
  148. package/src/shared/api/users.ts +294 -0
  149. package/src/shared/composables/buildEditor/useBuildEditor.ts +66 -0
  150. package/src/shared/composables/buildManifest/buildManifest.ts +447 -0
  151. package/src/shared/composables/filesEditor/filesEditor.ts +346 -0
  152. package/src/shared/composables/index.ts +10 -0
  153. package/src/shared/composables/modsEditor/modsEditor.ts +1678 -0
  154. package/src/shared/composables/registrySlot/registry-slot-utils.ts +25 -0
  155. package/src/shared/composables/registrySlot/useRegistrySlotMissing.ts +35 -0
  156. package/src/shared/composables/resourcePacksEditor/resourcePacksEditor.ts +448 -0
  157. package/src/shared/composables/shaderPacksEditor/shaderPacksEditor.ts +395 -0
  158. package/src/shared/composables/useSkinRender.ts +70 -0
  159. package/src/shared/composables/useZlDeepLink.ts +178 -0
  160. package/src/shared/definitions/defineGraphCache.ts +216 -0
  161. package/src/shared/definitions/defineStore.ts +32 -0
  162. package/src/shared/definitions/index.ts +2 -0
  163. package/src/shared/minecraft-types/build-manifest.ts +611 -0
  164. package/src/shared/minecraft-types/index.ts +3 -0
  165. package/src/shared/minecraft-types/launcher-versions.ts +32 -0
  166. package/src/shared/minecraft-types/minecraft-launcher-types.ts +276 -0
  167. package/src/shared/mocks/index.ts +1 -0
  168. package/src/shared/mocks/navigation.ts +17 -0
  169. package/src/shared/mods/http.ts +45 -0
  170. package/src/shared/mods/index.ts +5 -0
  171. package/src/shared/mods/marketplace-editor-search.ts +266 -0
  172. package/src/shared/mods/marketplace-search-utils.ts +42 -0
  173. package/src/shared/mods/mod-marketplace-registry.ts +66 -0
  174. package/src/shared/mods/mod-marketplace-types.ts +28 -0
  175. package/src/shared/mods/providers/curseforge.ts +464 -0
  176. package/src/shared/mods/providers/index.ts +8 -0
  177. package/src/shared/mods/providers/modrinth.ts +402 -0
  178. package/src/shared/mods/resolve-mods-provider-loader-ids.ts +77 -0
  179. package/src/shared/mods/types.ts +76 -0
  180. package/src/shared/styles/index.css +713 -0
  181. package/src/shared/themes/index.ts +23 -0
  182. package/src/shared/themes/theme-tokens-black.json +126 -0
  183. package/src/shared/themes/theme-tokens-classic.json +126 -0
  184. package/src/shared/themes/theme-tokens-pink.json +126 -0
  185. package/src/shared/themes/theme-tokens.json +126 -0
  186. package/src/shared/themes/types.ts +85 -0
  187. package/src/shared/types/API_DOCUMENTATION.md +422 -0
  188. package/src/shared/types/account.ts +40 -0
  189. package/src/shared/types/build.ts +8 -0
  190. package/src/shared/types/entities.ts +181 -0
  191. package/src/shared/types/index.ts +6 -0
  192. package/src/shared/types/invite-payloads.ts +60 -0
  193. package/src/shared/types/navigation.ts +16 -0
  194. package/src/shared/types/running-build.ts +51 -0
  195. package/src/shared/types/serverMember.ts +17 -0
  196. package/src/shared/types/user.ts +55 -0
  197. package/src/shared/ui/base/Avatar.vue +262 -0
  198. package/src/shared/ui/base/Badge.vue +47 -0
  199. package/src/shared/ui/base/Button.vue +78 -0
  200. package/src/shared/ui/base/Divider.vue +42 -0
  201. package/src/shared/ui/base/Icon.vue +597 -0
  202. package/src/shared/ui/base/StatusIndicator.vue +44 -0
  203. package/src/shared/ui/base/index.ts +7 -0
  204. package/src/shared/ui/cards/InviteCard.vue +47 -0
  205. package/src/shared/ui/cards/index.ts +2 -0
  206. package/src/shared/ui/dialog/Dialog.vue +71 -0
  207. package/src/shared/ui/dialog/DialogContent.vue +31 -0
  208. package/src/shared/ui/dialog/DialogFooter.vue +14 -0
  209. package/src/shared/ui/dialog/DialogHeader.vue +41 -0
  210. package/src/shared/ui/dialog/index.ts +5 -0
  211. package/src/shared/ui/editors/AttachmentImagesEditor.vue +133 -0
  212. package/src/shared/ui/editors/ContentAttachmentsDisplay.vue +76 -0
  213. package/src/shared/ui/editors/MarkdownEditor.vue +956 -0
  214. package/src/shared/ui/editors/MarkdownRenderer.vue +299 -0
  215. package/src/shared/ui/editors/RichContentImageViewer.vue +85 -0
  216. package/src/shared/ui/editors/SocialPostMediaZone.vue +320 -0
  217. package/src/shared/ui/editors/index.ts +6 -0
  218. package/src/shared/ui/editors/markdown-editor-gallery.ts +234 -0
  219. package/src/shared/ui/editors/markdown-editor-image.ts +178 -0
  220. package/src/shared/ui/form/Checkbox.vue +38 -0
  221. package/src/shared/ui/form/FormField.vue +30 -0
  222. package/src/shared/ui/form/FormGrid.vue +38 -0
  223. package/src/shared/ui/form/ImageEditor.vue +598 -0
  224. package/src/shared/ui/form/Input.vue +72 -0
  225. package/src/shared/ui/form/Range.vue +65 -0
  226. package/src/shared/ui/form/Select.vue +76 -0
  227. package/src/shared/ui/form/Switch.vue +38 -0
  228. package/src/shared/ui/form/Textarea.vue +144 -0
  229. package/src/shared/ui/form/index.ts +9 -0
  230. package/src/shared/ui/index.ts +9 -0
  231. package/src/shared/ui/layout/BusyOverlay.vue +31 -0
  232. package/src/shared/ui/layout/Callout.vue +44 -0
  233. package/src/shared/ui/layout/Card.vue +38 -0
  234. package/src/shared/ui/layout/Container.vue +36 -0
  235. package/src/shared/ui/layout/EmptyState.vue +99 -0
  236. package/src/shared/ui/layout/EntityMediaRow.vue +54 -0
  237. package/src/shared/ui/layout/FilterResultsLayout.vue +22 -0
  238. package/src/shared/ui/layout/FloatingPanel.vue +37 -0
  239. package/src/shared/ui/layout/FullscreenDimmer.vue +11 -0
  240. package/src/shared/ui/layout/Grid.vue +40 -0
  241. package/src/shared/ui/layout/Inline.vue +59 -0
  242. package/src/shared/ui/layout/LoadingState.vue +39 -0
  243. package/src/shared/ui/layout/MediaBox.vue +47 -0
  244. package/src/shared/ui/layout/OverlayPanel.vue +28 -0
  245. package/src/shared/ui/layout/OverlayWaitPanel.vue +22 -0
  246. package/src/shared/ui/layout/PageSection.vue +43 -0
  247. package/src/shared/ui/layout/PageToolbar.vue +29 -0
  248. package/src/shared/ui/layout/Panel.vue +39 -0
  249. package/src/shared/ui/layout/ProgressBar.vue +49 -0
  250. package/src/shared/ui/layout/Section.vue +30 -0
  251. package/src/shared/ui/layout/SegmentedControl.vue +43 -0
  252. package/src/shared/ui/layout/SelectableCard.vue +46 -0
  253. package/src/shared/ui/layout/SelectableRow.vue +41 -0
  254. package/src/shared/ui/layout/Skeleton.vue +25 -0
  255. package/src/shared/ui/layout/SkeletonAvatar.vue +30 -0
  256. package/src/shared/ui/layout/SkeletonEntityCard.vue +20 -0
  257. package/src/shared/ui/layout/SkeletonFeedPost.vue +22 -0
  258. package/src/shared/ui/layout/SkeletonGrid.vue +18 -0
  259. package/src/shared/ui/layout/SkeletonListRow.vue +31 -0
  260. package/src/shared/ui/layout/SkeletonText.vue +25 -0
  261. package/src/shared/ui/layout/Stack.vue +42 -0
  262. package/src/shared/ui/layout/StateBlock.vue +44 -0
  263. package/src/shared/ui/layout/TwoPaneLayout.vue +35 -0
  264. package/src/shared/ui/layout/VirtualList.vue +160 -0
  265. package/src/shared/ui/layout/index.ts +35 -0
  266. package/src/shared/ui/layout/skeletonSurfaceStyles.ts +24 -0
  267. package/src/shared/ui/navigation/NavItem.vue +139 -0
  268. package/src/shared/ui/navigation/Tab.vue +61 -0
  269. package/src/shared/ui/navigation/Tabs.vue +37 -0
  270. package/src/shared/ui/navigation/index.ts +4 -0
  271. package/src/shared/ui/primitives/Action.vue +19 -0
  272. package/src/shared/ui/primitives/Block.vue +28 -0
  273. package/src/shared/ui/primitives/CanvasView.vue +19 -0
  274. package/src/shared/ui/primitives/Control.vue +24 -0
  275. package/src/shared/ui/primitives/ControlSelect.vue +19 -0
  276. package/src/shared/ui/primitives/ControlTextarea.vue +17 -0
  277. package/src/shared/ui/primitives/FieldLabel.vue +19 -0
  278. package/src/shared/ui/primitives/Form.vue +19 -0
  279. package/src/shared/ui/primitives/Heading.vue +29 -0
  280. package/src/shared/ui/primitives/Image.vue +17 -0
  281. package/src/shared/ui/primitives/LineBreak.vue +3 -0
  282. package/src/shared/ui/primitives/Link.vue +19 -0
  283. package/src/shared/ui/primitives/List.vue +28 -0
  284. package/src/shared/ui/primitives/ListItem.vue +19 -0
  285. package/src/shared/ui/primitives/OptionItem.vue +19 -0
  286. package/src/shared/ui/primitives/Text.vue +28 -0
  287. package/src/shared/ui/primitives/VideoView.vue +19 -0
  288. package/src/shared/ui/primitives/index.ts +19 -0
  289. package/src/shared/ui/primitives/resolveElement.ts +25 -0
  290. package/src/shared/ui/special/AngularAccent.vue +106 -0
  291. package/src/shared/ui/special/ExtensionRegistrySlotButton.vue +143 -0
  292. package/src/shared/ui/special/InfoRow.vue +39 -0
  293. package/src/shared/ui/special/LogViewer.vue +53 -0
  294. package/src/shared/ui/special/PageHeader.vue +23 -0
  295. package/src/shared/ui/special/RegistrySlotMissingCallout.vue +48 -0
  296. package/src/shared/ui/special/WelcomeCard.vue +32 -0
  297. package/src/shared/ui/special/index.ts +9 -0
  298. package/src/shared/utils/app-paths.ts +50 -0
  299. package/src/shared/utils/attachments.ts +16 -0
  300. package/src/shared/utils/autostart.ts +213 -0
  301. package/src/shared/utils/build-files.ts +439 -0
  302. package/src/shared/utils/build-manifest-init.ts +176 -0
  303. package/src/shared/utils/cloudinary.ts +67 -0
  304. package/src/shared/utils/cn.ts +7 -0
  305. package/src/shared/utils/download-stats-week.ts +165 -0
  306. package/src/shared/utils/entity-api-to-cache.ts +84 -0
  307. package/src/shared/utils/entity-build-from-api.ts +1 -0
  308. package/src/shared/utils/entity-display.ts +27 -0
  309. package/src/shared/utils/entity-map-from-api.ts +1 -0
  310. package/src/shared/utils/file-hash.ts +65 -0
  311. package/src/shared/utils/formatSize.ts +5 -0
  312. package/src/shared/utils/formatTime.ts +157 -0
  313. package/src/shared/utils/getAccountSkinRender.ts +32 -0
  314. package/src/shared/utils/index.ts +34 -0
  315. package/src/shared/utils/local-mods.ts +678 -0
  316. package/src/shared/utils/local-settings.ts +217 -0
  317. package/src/shared/utils/member-join-stats.ts +35 -0
  318. package/src/shared/utils/platform.ts +86 -0
  319. package/src/shared/utils/play-host-slug.ts +92 -0
  320. package/src/shared/utils/rich-content.ts +294 -0
  321. package/src/shared/utils/safeRequest.ts +23 -0
  322. package/src/shared/utils/semver.ts +81 -0
  323. package/src/shared/utils/serverPermissions.ts +155 -0
  324. package/src/shared/utils/skin-render-cache.ts +372 -0
  325. package/src/shared/utils/stripMarkdown.ts +45 -0
  326. package/src/shared/utils/transliterate.ts +74 -0
  327. package/src/shared/utils/updateAccountSkinRender.ts +64 -0
  328. package/src/shared/utils/updater.ts +218 -0
  329. package/src/shared/utils/uploadImage.ts +195 -0
  330. package/src/shared/utils/user-status.ts +9 -0
  331. package/src/tiptap/index.ts +7 -0
  332. package/tsconfig.json +13 -0
@@ -0,0 +1,294 @@
1
+ import { marked } from 'marked';
2
+
3
+ marked.setOptions({
4
+ breaks: true,
5
+ gfm: true,
6
+ });
7
+
8
+ const looksLikeHtml = (content: string): boolean => /<[a-z][\s\S]*>/i.test(content);
9
+
10
+ const sanitizeBasicHtml = (html: string): string => {
11
+ return html
12
+ .replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, '')
13
+ .replace(/<iframe\b[^<]*(?:(?!<\/iframe>)<[^<]*)*<\/iframe>/gi, '')
14
+ .replace(/<object\b[^<]*(?:(?!<\/object>)<[^<]*)*<\/object>/gi, '')
15
+ .replace(/<embed\b[^<]*(?:(?!<\/embed>)<[^<]*)*<\/embed>/gi, '')
16
+ .replace(/\son\w+="[^"]*"/gi, '')
17
+ .replace(/\son\w+='[^']*'/gi, '');
18
+ };
19
+
20
+ const linkifyTextInNode = (root: HTMLElement) => {
21
+ const urlRegex = /https?:\/\/[^\s<]+[^\s<.,:;"')\]}]/gi;
22
+ const walker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT);
23
+ const textNodes: Text[] = [];
24
+
25
+ let current = walker.nextNode();
26
+ while (current) {
27
+ const textNode = current as Text;
28
+ const parentTag = textNode.parentElement?.tagName;
29
+ if (!parentTag || ['A', 'CODE', 'PRE', 'SCRIPT', 'STYLE'].includes(parentTag)) {
30
+ current = walker.nextNode();
31
+ continue;
32
+ }
33
+ if (urlRegex.test(textNode.data)) {
34
+ textNodes.push(textNode);
35
+ }
36
+ urlRegex.lastIndex = 0;
37
+ current = walker.nextNode();
38
+ }
39
+
40
+ textNodes.forEach((textNode) => {
41
+ const text = textNode.data;
42
+ const fragment = document.createDocumentFragment();
43
+ let lastIndex = 0;
44
+
45
+ text.replace(urlRegex, (url, offset: number) => {
46
+ if (offset > lastIndex) {
47
+ fragment.appendChild(document.createTextNode(text.slice(lastIndex, offset)));
48
+ }
49
+
50
+ const link = document.createElement('a');
51
+ link.href = url;
52
+ link.textContent = url;
53
+ link.target = '_blank';
54
+ link.rel = 'noopener noreferrer';
55
+ fragment.appendChild(link);
56
+
57
+ lastIndex = offset + url.length;
58
+ return url;
59
+ });
60
+
61
+ if (lastIndex < text.length) {
62
+ fragment.appendChild(document.createTextNode(text.slice(lastIndex)));
63
+ }
64
+
65
+ textNode.parentNode?.replaceChild(fragment, textNode);
66
+ });
67
+ };
68
+
69
+ const setExternalLinksBlank = (html: string): string => {
70
+ if (typeof window === 'undefined' || typeof DOMParser === 'undefined') {
71
+ return html.replace(
72
+ /<a\b([^>]*?)href=(["'])(https?:\/\/[^"']+)\2([^>]*)>/gi,
73
+ '<a$1href=$2$3$2$4 target="_blank" rel="noopener noreferrer">',
74
+ );
75
+ }
76
+
77
+ const doc = new DOMParser().parseFromString(`<div>${html}</div>`, 'text/html');
78
+ const root = doc.body.firstElementChild as HTMLElement | null;
79
+ if (!root) return html;
80
+
81
+ linkifyTextInNode(root);
82
+
83
+ const links = root.querySelectorAll('a[href]');
84
+ links.forEach((link) => {
85
+ const href = link.getAttribute('href') || '';
86
+ if (/^https?:\/\//i.test(href)) {
87
+ link.setAttribute('target', '_blank');
88
+ link.setAttribute('rel', 'noopener noreferrer');
89
+ }
90
+ });
91
+
92
+ return root.innerHTML;
93
+ };
94
+
95
+ const escapeHtmlAttr = (value: string): string =>
96
+ value
97
+ .replace(/&/g, '&amp;')
98
+ .replace(/"/g, '&quot;')
99
+ .replace(/</g, '&lt;');
100
+
101
+ const parseContentToHtml = (content: string): string => {
102
+ if (!content) return '';
103
+ if (looksLikeHtml(content)) return content;
104
+ return marked.parse(content) as string;
105
+ };
106
+
107
+ /**
108
+ * Переносит все изображения в конец HTML для редактора.
109
+ */
110
+ export const normalizeEditorHtmlContent = (content: string): string => {
111
+ const { html, images } = splitRichContentImages(content);
112
+ if (!images.length) return html;
113
+
114
+ const imageHtml = images
115
+ .map((image) => {
116
+ const alt = image.alt ? ` alt="${escapeHtmlAttr(image.alt)}"` : '';
117
+ return `<img class="md-editor-image" src="${escapeHtmlAttr(image.src)}"${alt} />`;
118
+ })
119
+ .join('');
120
+
121
+ if (!html) return imageHtml;
122
+ return `${html}${imageHtml}`;
123
+ };
124
+
125
+ /**
126
+ * Конвертирует сохраненный контент в HTML для редактора без переноса картинок.
127
+ */
128
+ export const toEditorHtmlContent = (content: string): string => {
129
+ if (!content) return '';
130
+ return sanitizeBasicHtml(parseContentToHtml(content));
131
+ };
132
+
133
+ /**
134
+ * Конвертирует сохраненный контент в HTML для редактора (legacy: картинки в конец).
135
+ */
136
+ export const toHtmlContent = (content: string): string => {
137
+ return normalizeEditorHtmlContent(content);
138
+ };
139
+
140
+ export interface RichContentImage {
141
+ src: string;
142
+ alt: string;
143
+ }
144
+
145
+ const parseHtmlRoot = (html: string): HTMLElement | null => {
146
+ if (typeof window === 'undefined' || typeof DOMParser === 'undefined') {
147
+ return null;
148
+ }
149
+
150
+ const doc = new DOMParser().parseFromString(`<div>${html}</div>`, 'text/html');
151
+ return doc.body.firstElementChild as HTMLElement | null;
152
+ };
153
+
154
+ /**
155
+ * Извлекает изображения из HTML и возвращает текст без них.
156
+ * Изображения всегда собираются в конец списка, даже если в исходнике были inline.
157
+ */
158
+ export const splitRichContentImages = (
159
+ content: string,
160
+ ): { html: string; images: RichContentImage[] } => {
161
+ const html = sanitizeBasicHtml(parseContentToHtml(content));
162
+ const root = parseHtmlRoot(html);
163
+
164
+ if (!root) {
165
+ const images: RichContentImage[] = [];
166
+ const withoutImages = html.replace(/<img\b[^>]*>/gi, (tag) => {
167
+ const src = tag.match(/\bsrc=(["'])(.*?)\1/i)?.[2] ?? '';
168
+ const alt = tag.match(/\balt=(["'])(.*?)\1/i)?.[2] ?? '';
169
+ if (src) images.push({ src, alt });
170
+ return '';
171
+ });
172
+ return { html: withoutImages.trim(), images };
173
+ }
174
+
175
+ const images: RichContentImage[] = [];
176
+ root.querySelectorAll('img').forEach((img) => {
177
+ const src = img.getAttribute('src') || '';
178
+ if (!src) return;
179
+ images.push({
180
+ src,
181
+ alt: img.getAttribute('alt') || '',
182
+ });
183
+ const host = img.closest('[data-uploading]');
184
+ host?.remove();
185
+ if (!host) img.remove();
186
+ });
187
+
188
+ root.querySelectorAll('[data-uploading]').forEach((node) => {
189
+ if (!node.querySelector('img')) node.remove();
190
+ });
191
+
192
+ return { html: root.innerHTML.trim(), images };
193
+ };
194
+
195
+ const parseGalleryImagesFromAttr = (raw: string | null): RichContentImage[] => {
196
+ if (!raw) return [];
197
+ try {
198
+ const parsed = JSON.parse(raw) as unknown;
199
+ if (!Array.isArray(parsed)) return [];
200
+ return parsed
201
+ .map((item) => {
202
+ if (typeof item === 'string' && item) return { src: item, alt: '' };
203
+ if (item && typeof item === 'object' && 'src' in item) {
204
+ const src = String((item as { src: unknown }).src || '');
205
+ if (!src || src.startsWith('blob:')) return null;
206
+ if (
207
+ 'uploading' in item &&
208
+ (item as { uploading?: unknown }).uploading === true
209
+ ) {
210
+ return null;
211
+ }
212
+ const alt =
213
+ 'alt' in item && typeof (item as { alt?: unknown }).alt === 'string'
214
+ ? (item as { alt: string }).alt
215
+ : '';
216
+ return { src, alt };
217
+ }
218
+ return null;
219
+ })
220
+ .filter((item): item is RichContentImage => item !== null);
221
+ } catch {
222
+ return [];
223
+ }
224
+ };
225
+
226
+ const collectRichVariantImages = (root: HTMLElement): RichContentImage[] => {
227
+ const images: RichContentImage[] = [];
228
+ const seen = new Set<string>();
229
+
230
+ const pushImage = (src: string, alt = '') => {
231
+ if (!src || seen.has(src)) return;
232
+ seen.add(src);
233
+ images.push({ src, alt });
234
+ };
235
+
236
+ root.querySelectorAll('[data-type="image-gallery"]').forEach((gallery) => {
237
+ const fromAttr = parseGalleryImagesFromAttr(gallery.getAttribute('data-images'));
238
+ if (fromAttr.length) {
239
+ fromAttr.forEach((image) => pushImage(image.src, image.alt));
240
+ return;
241
+ }
242
+ gallery.querySelectorAll('img').forEach((img) => {
243
+ pushImage(img.getAttribute('src') || '', img.getAttribute('alt') || '');
244
+ });
245
+ });
246
+
247
+ root.querySelectorAll('img').forEach((img) => {
248
+ if (img.closest('[data-type="image-gallery"]')) return;
249
+ pushImage(img.getAttribute('src') || '', img.getAttribute('alt') || '');
250
+ });
251
+
252
+ return images;
253
+ };
254
+
255
+ /**
256
+ * Подготавливает rich-контент: inline-картинки и галереи остаются в HTML.
257
+ */
258
+ export const prepareRichVariantContent = (
259
+ content: string,
260
+ ): { html: string; images: RichContentImage[] } => {
261
+ const html = sanitizeBasicHtml(parseContentToHtml(content));
262
+ const root = parseHtmlRoot(html);
263
+
264
+ if (!root) {
265
+ const images: RichContentImage[] = [];
266
+ const imgRegex = /<img\b[^>]*>/gi;
267
+ const withoutInline = html.replace(imgRegex, (tag) => {
268
+ const src = tag.match(/\bsrc=(["'])(.*?)\1/i)?.[2] ?? '';
269
+ const alt = tag.match(/\balt=(["'])(.*?)\1/i)?.[2] ?? '';
270
+ if (src) images.push({ src, alt });
271
+ return tag;
272
+ });
273
+ return { html: setExternalLinksBlank(withoutInline.trim()), images };
274
+ }
275
+
276
+ const images = collectRichVariantImages(root);
277
+ return { html: setExternalLinksBlank(root.innerHTML.trim()), images };
278
+ };
279
+
280
+ /**
281
+ * Подготавливает контент к безопасному рендеру в UI:
282
+ * - markdown -> html (для legacy данных)
283
+ * - базовая очистка
284
+ * - внешние http/https ссылки в новой вкладке
285
+ */
286
+ export const renderRichContent = (content: string): string => {
287
+ const { html } = splitRichContentImages(content);
288
+ return setExternalLinksBlank(html);
289
+ };
290
+
291
+ export const renderRichVariantContent = (content: string): string => {
292
+ return prepareRichVariantContent(content).html;
293
+ };
294
+
@@ -0,0 +1,23 @@
1
+ export type ApiResult<T> =
2
+ | { ok: true; result: T; error: null }
3
+ | { ok: false; result: null; error: string };
4
+
5
+ export async function safeRequest<T>(
6
+ fn: () => Promise<T>,
7
+ ): Promise<ApiResult<T>> {
8
+ try {
9
+ const result = await fn();
10
+
11
+ return {
12
+ ok: true,
13
+ result,
14
+ error: null,
15
+ };
16
+ } catch (err) {
17
+ return {
18
+ ok: false,
19
+ result: null,
20
+ error: err instanceof Error ? err.message : 'Unknown error',
21
+ };
22
+ }
23
+ }
@@ -0,0 +1,81 @@
1
+ /**
2
+ * Утилиты для работы с семантическими версиями
3
+ */
4
+
5
+ /**
6
+ * Проверяет, является ли строка валидной семантической версией
7
+ * Формат: MAJOR.MINOR.PATCH[-PRERELEASE][+BUILD]
8
+ */
9
+ export function isValidSemver(version: string): boolean {
10
+ const semverRegex = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$/;
11
+ return semverRegex.test(version);
12
+ }
13
+
14
+ /**
15
+ * Сравнивает две семантические версии
16
+ * @returns -1 если v1 < v2, 0 если v1 === v2, 1 если v1 > v2
17
+ */
18
+ export function compareVersions(v1: string, v2: string): number {
19
+ if (!isValidSemver(v1) || !isValidSemver(v2)) {
20
+ throw new Error('Invalid semver format');
21
+ }
22
+
23
+ // Парсим версии
24
+ const parseVersion = (v: string) => {
25
+ const [version, prerelease] = v.split('-');
26
+ const [major, minor, patch] = version.split('.').map(Number);
27
+ return { major, minor, patch, prerelease: prerelease || '' };
28
+ };
29
+
30
+ const parsed1 = parseVersion(v1);
31
+ const parsed2 = parseVersion(v2);
32
+
33
+ // Сравниваем major, minor, patch
34
+ if (parsed1.major !== parsed2.major) {
35
+ return parsed1.major > parsed2.major ? 1 : -1;
36
+ }
37
+ if (parsed1.minor !== parsed2.minor) {
38
+ return parsed1.minor > parsed2.minor ? 1 : -1;
39
+ }
40
+ if (parsed1.patch !== parsed2.patch) {
41
+ return parsed1.patch > parsed2.patch ? 1 : -1;
42
+ }
43
+
44
+ // Если версии одинаковые, сравниваем prerelease
45
+ if (parsed1.prerelease && !parsed2.prerelease) {
46
+ return -1; // prerelease версия меньше release
47
+ }
48
+ if (!parsed1.prerelease && parsed2.prerelease) {
49
+ return 1; // release версия больше prerelease
50
+ }
51
+ if (parsed1.prerelease && parsed2.prerelease) {
52
+ // Сравниваем prerelease строки лексикографически
53
+ return parsed1.prerelease > parsed2.prerelease ? 1 : parsed1.prerelease < parsed2.prerelease ? -1 : 0;
54
+ }
55
+
56
+ return 0;
57
+ }
58
+
59
+ /**
60
+ * Проверяет, что новая версия больше текущей
61
+ */
62
+ export function isVersionGreater(newVersion: string, currentVersion: string): boolean {
63
+ try {
64
+ return compareVersions(newVersion, currentVersion) > 0;
65
+ } catch {
66
+ return false;
67
+ }
68
+ }
69
+
70
+ /**
71
+ * Проверяет, что версия не меньше текущей (больше или равна)
72
+ */
73
+ export function isVersionNotLower(newVersion: string, currentVersion: string): boolean {
74
+ try {
75
+ return compareVersions(newVersion, currentVersion) >= 0;
76
+ } catch {
77
+ return false;
78
+ }
79
+ }
80
+
81
+
@@ -0,0 +1,155 @@
1
+ /**
2
+ * Утилиты для работы с правами доступа на серверах на клиенте
3
+ */
4
+
5
+ import { ref, computed, watch, type ComputedRef, type Ref } from 'vue';
6
+ import type { ServerMemberRole } from '../types/serverMember';
7
+ import { useUserModel } from '@/models/user';
8
+ import { useServersModel } from '@/models/projectServers';
9
+ import { getServerMembers } from '../api/serverMembers';
10
+
11
+ /**
12
+ * Получает роль текущего пользователя на сервере
13
+ * @param serverId - ID сервера
14
+ * @returns Роль пользователя или null, если пользователь не является участником
15
+ */
16
+ export async function getCurrentUserServerRole(
17
+ serverId: string,
18
+ ): Promise<ServerMemberRole | null> {
19
+ const { user } = useUserModel();
20
+ const { selectedServer } = useServersModel();
21
+
22
+ if (!user.value) return null;
23
+
24
+ // Сначала проверяем, является ли пользователь автором проекта (если сервер в проекте)
25
+ const server = selectedServer.value;
26
+ if (server?.project?.author?.id === user.value.id) {
27
+ return 'admin';
28
+ }
29
+
30
+ // Затем проверяем участников сервера
31
+ const res = await getServerMembers(serverId);
32
+ if (res.ok) {
33
+ const currentUserMember = res.result.find((m) => m.user.id === user.value?.id);
34
+ if (currentUserMember) {
35
+ return currentUserMember.role;
36
+ }
37
+ }
38
+
39
+ return null;
40
+ }
41
+
42
+ /**
43
+ * Проверяет, является ли текущий пользователь участником сервера
44
+ * @param serverId - ID сервера
45
+ * @returns true, если пользователь является участником или автором проекта
46
+ */
47
+ export async function isCurrentUserServerMember(
48
+ serverId: string,
49
+ ): Promise<boolean> {
50
+ const role = await getCurrentUserServerRole(serverId);
51
+ return role !== null;
52
+ }
53
+
54
+ /**
55
+ * Проверяет, имеет ли текущий пользователь права модератора или выше на сервере
56
+ * @param serverId - ID сервера
57
+ * @returns true, если пользователь является модератором или администратором
58
+ */
59
+ export async function hasModeratorOrAdminRole(
60
+ serverId: string,
61
+ ): Promise<boolean> {
62
+ const role = await getCurrentUserServerRole(serverId);
63
+ return role === 'moderator' || role === 'admin';
64
+ }
65
+
66
+ /**
67
+ * Проверяет, имеет ли текущий пользователь права администратора на сервере
68
+ * @param serverId - ID сервера
69
+ * @returns true, если пользователь является администратором
70
+ */
71
+ export async function hasAdminRole(serverId: string): Promise<boolean> {
72
+ const role = await getCurrentUserServerRole(serverId);
73
+ return role === 'admin';
74
+ }
75
+
76
+ /**
77
+ * Создает computed свойство для проверки роли пользователя на сервере
78
+ * @param serverId - ID сервера (может быть ref, computed или функция)
79
+ * @returns Computed свойство с ролью пользователя
80
+ */
81
+ export function useServerRole(
82
+ serverId:
83
+ | ComputedRef<string | null>
84
+ | Ref<string | null>
85
+ | (() => string | null),
86
+ ) {
87
+ const { user } = useUserModel();
88
+ const { selectedServer } = useServersModel();
89
+
90
+ const role = ref<ServerMemberRole | null>(null);
91
+ const isLoading = ref(false);
92
+
93
+ const loadRole = async () => {
94
+ const id =
95
+ typeof serverId === 'function' ? serverId() : (serverId as any).value;
96
+ if (!id || !user.value) {
97
+ role.value = null;
98
+ return;
99
+ }
100
+
101
+ isLoading.value = true;
102
+ try {
103
+ // Проверяем, является ли пользователь автором проекта
104
+ const server = selectedServer.value;
105
+ if (server?.project?.author?.id === user.value.id) {
106
+ role.value = 'admin';
107
+ isLoading.value = false;
108
+ return;
109
+ }
110
+
111
+ // Проверяем участников сервера
112
+ const res = await getServerMembers(id);
113
+ if (res.ok) {
114
+ const currentUserMember = res.result.find(
115
+ (m) => m.user.id === user.value?.id,
116
+ );
117
+ role.value = currentUserMember?.role || null;
118
+ } else {
119
+ console.error('[useServerRole] Error loading role:', res.error);
120
+ role.value = null;
121
+ }
122
+ } finally {
123
+ isLoading.value = false;
124
+ }
125
+ };
126
+
127
+ watch(
128
+ () => {
129
+ const id =
130
+ typeof serverId === 'function' ? serverId() : (serverId as any).value;
131
+ return id;
132
+ },
133
+ () => {
134
+ loadRole();
135
+ },
136
+ { immediate: true },
137
+ );
138
+
139
+ const isMember = computed(() => role.value !== null);
140
+ const isModerator = computed(
141
+ () => role.value === 'moderator' || role.value === 'admin',
142
+ );
143
+ const isAdmin = computed(() => role.value === 'admin');
144
+ const isPlayer = computed(() => role.value === 'player');
145
+
146
+ return {
147
+ role: computed(() => role.value),
148
+ isLoading: computed(() => isLoading.value),
149
+ isMember,
150
+ isModerator,
151
+ isAdmin,
152
+ isPlayer,
153
+ refresh: loadRole,
154
+ };
155
+ }