diff --git a/frontend/apps/site/account-page.tsx b/frontend/apps/site/account-page.tsx index a038fc689c..c0cf6cae6e 100644 --- a/frontend/apps/site/account-page.tsx +++ b/frontend/apps/site/account-page.tsx @@ -1,4 +1,4 @@ -import {HYPERMEDIA_ACCOUNT_PREFIX} from '@mintter/shared' +import {createHmId} from '@mintter/shared' import { Avatar, Heading, @@ -52,10 +52,7 @@ export default function AccountPage({accountId}: {accountId: string}) { return ( - + diff --git a/frontend/apps/site/pages/g/[groupId]/index.tsx b/frontend/apps/site/pages/g/[groupId]/index.tsx index 1537f02dfa..ab1a72b4df 100644 --- a/frontend/apps/site/pages/g/[groupId]/index.tsx +++ b/frontend/apps/site/pages/g/[groupId]/index.tsx @@ -21,15 +21,11 @@ import { View, SimpleTooltip, } from '@mintter/ui' -import {HMGroup, HMPublication} from '@mintter/ui' +import {HMGroup, HMPublication} from '../../../server/json-hm' import {ReactElement} from 'react' import {GestureResponderEvent} from 'react-native' import {Timestamp} from '@bufbuild/protobuf' -import { - HYPERMEDIA_GROUP_PREFIX, - entityIdToSitePath, - formattedDate, -} from '@mintter/shared' +import {formattedDate, unpackHmId, createPublicWebHmUrl} from '@mintter/shared' import {AccountAvatarLink, AccountRow} from 'components/account-row' import {format} from 'date-fns' import {Paragraph} from 'tamagui' @@ -43,9 +39,9 @@ function GroupOwnerSection({owner}: {owner: string}) { ) } -function GroupEditorsSection({groupEid}: {groupEid: string}) { +function GroupEditorsSection({groupId}: {groupId: string}) { const groupMembers = trpc.group.listMembers.useQuery({ - groupEid, + groupId, }) if (!groupMembers.data) return null @@ -143,6 +139,9 @@ function GroupContentItem({ item: {pathName: string; publication: null | HMPublication} group?: null | HMGroup }) { + const groupId = group?.id ? unpackHmId(group?.id) : null + const groupEid = groupId?.eid + if (!groupEid) return null return ( } - href={`${entityIdToSitePath(group?.id)}/${item.pathName}`} + href={`${createPublicWebHmUrl('g', groupEid, {hostname: null})}/${ + item.pathName + }`} /> ) } @@ -181,10 +182,7 @@ export default function GroupPage({ {loadedGroup ? ( <> - + }) { - const groupLink = entityIdToSitePath(group?.id) + const groupId = group?.id ? unpackHmId(group?.id) : null + const groupLink = + groupId?.eid && createPublicWebHmUrl('g', groupId.eid, {hostname: null}) return ( <> {groupLink && ( @@ -209,10 +203,7 @@ export default function PublicationPage({ return ( - + {/* legacy mintter metadata */} @@ -308,19 +299,20 @@ function InlineContentView({ ) } if (content.type === 'link') { - let matchesPattern = matchesHypermediaPattern(content.href) - const href = matchesPattern - ? hmLinkToSitePath(content.href) - : content.href + const href = idToUrl(content.href, null) return ( - - - + href && ( + + + + ) ) } return null @@ -379,21 +371,21 @@ function stripHMLinkPrefix(link: string) { function StaticEmbedBlock({block}: {block: EmbedBlock}) { const reference = block.ref - const [documentId, versionId, blockId] = getIdsfromUrl(reference) + const docId = unpackDocId(reference) const router = useRouter() let embed = trpc.publication.get.useQuery( { - documentId, - versionId, + documentId: docId?.docId, + versionId: docId?.version, }, - {enabled: !!documentId}, + {enabled: !!docId}, ) let content = if (embed.data?.publication?.document?.children) { - if (blockId) { + if (docId?.blockRef) { const blockNode = getBlockNodeById( embed.data?.publication?.document?.children, - blockId, + docId.blockRef, ) content = blockNode ? ( diff --git a/frontend/apps/site/server/routers/_app.ts b/frontend/apps/site/server/routers/_app.ts index 21e76bcf38..de0e931840 100644 --- a/frontend/apps/site/server/routers/_app.ts +++ b/frontend/apps/site/server/routers/_app.ts @@ -4,11 +4,11 @@ import { Changes, ContentGraph, Groups, - HYPERMEDIA_GROUP_PREFIX, Publications, Role, WebPublishing, - getIdsfromUrl, + unpackDocId, + unpackHmId, } from '@mintter/shared' import {localWebsiteClient, transport} from 'client' import {getSiteInfo} from 'get-site-info' @@ -244,15 +244,14 @@ const groupRouter = router({ getGroupPath: procedure .input( z.object({ - groupEid: z.string(), + groupId: z.string(), pathName: z.string(), version: z.string().optional(), }), ) - .query(async ({input: {pathName, groupEid, version}}) => { + .query(async ({input: {pathName, groupId, version}}) => { // todo. get current group content and find the pathName, return the corresponding doc console.log('getting site info') - const groupId = `${HYPERMEDIA_GROUP_PREFIX}${groupEid}` const siteInfo = await groupsClient.listContent({ id: groupId, version, @@ -263,32 +262,31 @@ const groupRouter = router({ }) const item = siteInfo.content[pathName] if (!item) return null - const [documentId, documentVersion] = getIdsfromUrl(item) - if (!documentId || !documentVersion) return null // version is required for group content + const itemId = unpackDocId(item) + if (!itemId?.version) return null // version is required for group content const pub = await publicationsClient.getPublication({ - documentId, - version: documentVersion, + documentId: itemId.docId, + version: itemId.version, }) return { publication: hmPublication(pub), pathName, - documentId, - documentVersion, + documentId: itemId.docId, + documentVersion: itemId.version, groupVersion: version, - groupEid, group: hmGroup(group), } }), get: procedure .input( z.object({ - groupEid: z.string(), + groupId: z.string(), }), ) .query(async ({input}) => { console.log('will getGroup with id', input) const group = await groupsClient.getGroup({ - id: `${HYPERMEDIA_GROUP_PREFIX}${input.groupEid}`, + id: input.groupId, }) console.log('did get group', hmGroup(group)) return { @@ -298,26 +296,26 @@ const groupRouter = router({ listContent: procedure .input( z.object({ - groupEid: z.string(), + groupId: z.string(), }), ) .query(async ({input}) => { const list = await groupsClient.listContent({ - id: `${HYPERMEDIA_GROUP_PREFIX}${input.groupEid}`, + id: input.groupId, }) const listedDocs = await Promise.all( Object.entries(list.content).map(async ([pathName, pubUrl]) => { - const [docId, version] = getIdsfromUrl(pubUrl) - if (!docId || !version) return null // version is required for group content + const docId = unpackDocId(pubUrl) + if (!docId?.version) return null // version is required for group content const pub = await publicationsClient.getPublication({ - documentId: docId, - version, + documentId: docId.docId, + version: docId.version, }) return { pathName, docId, - version, + version: docId.version, publication: hmPublication(pub), } }), @@ -333,12 +331,12 @@ const groupRouter = router({ listMembers: procedure .input( z.object({ - groupEid: z.string(), + groupId: z.string(), }), ) .query(async ({input}) => { const list = await groupsClient.listMembers({ - id: `${HYPERMEDIA_GROUP_PREFIX}${input.groupEid}`, + id: input.groupId, }) return Object.entries(list.members || {}).map(([account, role]) => ({ account, diff --git a/frontend/packages/app/src/components/quick-switcher.tsx b/frontend/packages/app/src/components/quick-switcher.tsx index 119e936583..f22cfffbbb 100644 --- a/frontend/packages/app/src/components/quick-switcher.tsx +++ b/frontend/packages/app/src/components/quick-switcher.tsx @@ -4,11 +4,7 @@ import { } from '@mintter/app/src/models/documents' import {fetchWebLink} from '@mintter/app/src/models/web-links' import {useNavigate} from '@mintter/app/src/utils/navigation' -import { - getIdsfromUrl, - isHypermediaScheme, - matchesHypermediaPattern, -} from '@mintter/shared' +import {isHypermediaScheme} from '@mintter/shared' import {Spinner, YStack} from '@mintter/ui' import {useListen} from '@mintter/app/src/app-context' import {Command} from 'cmdk' @@ -16,6 +12,7 @@ import {useState} from 'react' import {toast} from 'react-hot-toast' import './quick-switcher.css' import {useAppContext} from '@mintter/app/src/app-context' +import {hmIdToAppRoute} from '../open-url' export default function QuickSwitcher() { const [open, setOpen] = useState(false) @@ -57,28 +54,19 @@ export default function QuickSwitcher() { ) : ( No results found. - {(matchesHypermediaPattern(search) || - isHypermediaScheme(search) || + {(isHypermediaScheme(search) || search.startsWith('http://') || search.startsWith('https://')) && ( { - if ( - isHypermediaScheme(search) || - matchesHypermediaPattern(search) - ) { - let [docId, version, block] = getIdsfromUrl(search) + if (isHypermediaScheme(search)) { + const navRoute = hmIdToAppRoute(search) - if (docId) { + if (navRoute) { setOpen(false) - navigate({ - key: 'publication', - documentId: docId, - versionId: version, - blockId: block, - }) + navigate(navRoute) } else { console.log('== ~ QuickSwitcher ~ Querying Web URL', search) setActionPromise( diff --git a/frontend/packages/app/src/components/rightside-block-widget.tsx b/frontend/packages/app/src/components/rightside-block-widget.tsx index efb424d4a5..91cc3d4e41 100644 --- a/frontend/packages/app/src/components/rightside-block-widget.tsx +++ b/frontend/packages/app/src/components/rightside-block-widget.tsx @@ -12,7 +12,7 @@ import {useDocCitations} from '../models/content-graph' import {usePublication} from '../models/documents' import {toast} from '@mintter/app/src/toast' import {copyTextToClipboard} from '@mintter/app/src/copy-to-clipboard' -import {getDocUrl} from '@mintter/shared' +import {createPublicWebHmUrl, unpackHmId} from '@mintter/shared' import {useNavigate, useNavRoute} from '@mintter/app/src/utils/navigation' export function createRightsideBlockWidgetExtension({ @@ -105,9 +105,17 @@ export function RightsideWidget() { }) function onCopy() { - let docUrl = getDocUrl(pub.data) - if (docUrl && spec && spec.id) { - copyTextToClipboard(`${docUrl}#${spec.id}`) + const docId = pub.data?.document?.id + ? unpackHmId(pub.data?.document?.id) + : null + const docVersion = pub.data?.version + if (docId && docId.type === 'd' && docVersion && spec && spec.id) { + copyTextToClipboard( + createPublicWebHmUrl('d', docId.eid, { + version: docVersion, + blockRef: spec.id, + }), + ) toast.success('Block reference copied!') } else { appError('Block reference copy failed', {docUrl, spec}) diff --git a/frontend/packages/app/src/components/titlebar/common.tsx b/frontend/packages/app/src/components/titlebar/common.tsx index e2c2889d1f..fd85a74c86 100644 --- a/frontend/packages/app/src/components/titlebar/common.tsx +++ b/frontend/packages/app/src/components/titlebar/common.tsx @@ -26,8 +26,9 @@ import { Account, DocumentChange, SiteConfig, - getPublicDocUrl, - getPublicEntityUrl, + createPublicWebHmUrl, + unpackHmId, + createHmId, } from '@mintter/shared' import { Back, @@ -149,22 +150,32 @@ export function GroupOptionsButton() { function getReferenceUrlOfRoute(route: NavRoute) { if (route.key === 'group') { - const url = getPublicEntityUrl(route.groupId) // we use this because group IDs are full URLs with hm://g/ prefix, so this more generic conversion is available. - if (!url) return null + const groupId = unpackHmId(route.groupId) + if (!groupId || groupId.type !== 'g') return null + const url = createPublicWebHmUrl('g', groupId.eid) return { label: 'Group URL', url, } } if (route.key === 'publication') { - // docIds currently do not include this hm:// prefix so we use the specific doc url function - const url = getPublicDocUrl(route.documentId, route.versionId) + const docId = unpackHmId(route.documentId) + if (!docId || docId.type !== 'd') return null + const url = createPublicWebHmUrl('d', docId.eid, {version: route.versionId}) if (!url) return null return { label: 'Doc URL', url, } } + if (route.key === 'account') { + const url = createHmId('a', route.accountId) + if (!url) return null + return { + label: 'Account URL', + url, + } + } return null } @@ -210,6 +221,8 @@ export function PageActionButtons(props: TitleBarProps) { , , ] + } else if (route.key === 'account') { + buttonGroup = [] } return {buttonGroup} } diff --git a/frontend/packages/app/src/components/titlebar/publish-share.tsx b/frontend/packages/app/src/components/titlebar/publish-share.tsx index 264f7db277..7659e63abd 100644 --- a/frontend/packages/app/src/components/titlebar/publish-share.tsx +++ b/frontend/packages/app/src/components/titlebar/publish-share.tsx @@ -583,7 +583,7 @@ function PublicationContextButton({route}: {route: PublicationRoute}) { return ( { @@ -731,7 +731,6 @@ export function DraftPublicationButtons() { size="$2" disabled={!isDaemonReady} onPress={() => { - console.log('did start publish', draftId, isDaemonReady) publish.mutate({draftId}) }} theme="green" diff --git a/frontend/packages/app/src/editor/embed-block.tsx b/frontend/packages/app/src/editor/embed-block.tsx index ae0c036858..5c5c4bec5b 100644 --- a/frontend/packages/app/src/editor/embed-block.tsx +++ b/frontend/packages/app/src/editor/embed-block.tsx @@ -16,10 +16,11 @@ import type { import { Block, EmbedBlock as EmbedBlockType, + createHmId, getCIDFromIPFSUrl, - getIdsfromUrl, isHypermediaScheme, serverBlockToEditorInline, + unpackDocId, } from '@mintter/shared' import {SizableText, Spinner, Text, XStack, YStack} from '@mintter/ui' import {AlertCircle} from '@tamagui/lucide-icons' @@ -30,7 +31,7 @@ import {createReactBlockSpec} from '../blocknote-react' import {HMBlockSchema, hmBlockSchema} from '../client/schema' import {BACKEND_FILE_URL} from '../constants' import {usePublication} from '../models/documents' -import {useOpenUrl} from '../open-url' +import {hmIdToAppRoute, useOpenUrl} from '../open-url' function InlineContentView({inline}: {inline: InlineContent[]}) { const openUrl = useOpenUrl() @@ -173,14 +174,9 @@ function EmbedPresentation({ if (editor?.isEditable) { return } - let [documentId, version, blockId] = getIdsfromUrl(block.props.ref) - if (documentId) { - spawn({ - key: 'publication', - documentId, - versionId: version, - blockId, - }) + const route = hmIdToAppRoute(block.props.ref) + if (route) { + spawn(route) } }} > @@ -308,25 +304,24 @@ export const EmbedBlock = createReactBlockSpec({ function useEmbed(ref: string): ReturnType & { content?: BlockNode[] & PartialMessage[] } { - let [documentId, versionId, blockId] = getIdsfromUrl(ref) - + const pubId = unpackDocId(ref) let pubQuery = usePublication({ - documentId, - versionId, - enabled: !!documentId, + documentId: pubId?.docId, + versionId: pubId?.version, + enabled: !!pubId?.docId, }) return useMemo(() => { const data = pubQuery.data if (!data || !data.document) return pubQuery - const selectedBlock = blockId - ? getBlockNodeById(data.document.children, blockId) + const selectedBlock = pubId?.blockRef + ? getBlockNodeById(data.document.children, pubId?.blockRef) : null const embedBlocks = selectedBlock ? [selectedBlock] : data.document.children return {...pubQuery, content: embedBlocks} - }, [pubQuery.data, blockId]) + }, [pubQuery.data, pubId?.blockRef]) } function getBlockNodeById( diff --git a/frontend/packages/app/src/editor/hyperdocs-link-plugin.tsx b/frontend/packages/app/src/editor/hyperdocs-link-plugin.tsx index 11d577d27f..ce321eaa24 100644 --- a/frontend/packages/app/src/editor/hyperdocs-link-plugin.tsx +++ b/frontend/packages/app/src/editor/hyperdocs-link-plugin.tsx @@ -1,4 +1,4 @@ -import {createHyperdocsDocLink} from '@mintter/shared' +import {createHmDocLink} from '@mintter/shared' import {EditorView} from '@tiptap/pm/view' import {Plugin, PluginKey} from 'prosemirror-state' import {fetchWebLink} from '../models/web-links' @@ -86,7 +86,7 @@ async function checkHyperLink( pos, pos + node.textContent.length, view.state.schema.mark('link', { - href: createHyperdocsDocLink( + href: createHmDocLink( res!.documentId!, res?.documentVersion, res?.blockId, diff --git a/frontend/packages/app/src/models/documents.ts b/frontend/packages/app/src/models/documents.ts index 52cee8f73f..fc2fad28d3 100644 --- a/frontend/packages/app/src/models/documents.ts +++ b/frontend/packages/app/src/models/documents.ts @@ -29,12 +29,11 @@ import { Document, DocumentChange, GRPCClient, - HYPERMEDIA_DOCUMENT_PREFIX, ListPublicationsResponse, Publication, isHypermediaScheme, - isMintterGatewayLink, - normalizeHypermediaLink, + isPublicGatewayLink, + normlizeHmId, } from '@mintter/shared' import {useWidgetViewFactory} from '@prosemirror-adapter/react' import { @@ -297,7 +296,7 @@ export function usePublishDraft( await grpcClient.groups.updateGroup({ id: draftGroupContext.groupId, updatedContent: { - [publishPathName]: `${HYPERMEDIA_DOCUMENT_PREFIX}${publishedId}?v=${pub.version}`, + [publishPathName]: `${publishedId}?v=${pub.version}`, }, }) } @@ -1017,8 +1016,8 @@ function extractEmbedRefOfLink(block: any): false | string { if (block.content.length == 1) { let leaf = block.content[0] if (leaf.type == 'link') { - if (isMintterGatewayLink(leaf.href) || isHypermediaScheme(leaf.href)) { - const hmLink = normalizeHypermediaLink(leaf.href) + if (isPublicGatewayLink(leaf.href) || isHypermediaScheme(leaf.href)) { + const hmLink = normlizeHmId(leaf.href) console.log(`== ~ extractEmbedRefOfLink ~ hmLink:`, hmLink) if (hmLink) return hmLink diff --git a/frontend/packages/app/src/models/groups.ts b/frontend/packages/app/src/models/groups.ts index 9fe639f292..451472a3e3 100644 --- a/frontend/packages/app/src/models/groups.ts +++ b/frontend/packages/app/src/models/groups.ts @@ -1,4 +1,4 @@ -import {HYPERMEDIA_DOCUMENT_PREFIX, Role, getIdsfromUrl} from '@mintter/shared' +import {Role, unpackDocId} from '@mintter/shared' import {UseMutationOptions, useMutation, useQuery} from '@tanstack/react-query' import {useGRPCClient, useQueryInvalidator} from '../app-context' import {queryKeys} from './query-keys' @@ -126,7 +126,7 @@ export function usePublishDocToGroup( await grpcClient.groups.updateGroup({ id: groupId, updatedContent: { - [pathName]: `${HYPERMEDIA_DOCUMENT_PREFIX}${docId}?v=${version}`, + [pathName]: `${docId}?v=${version}`, }, }) }, @@ -184,13 +184,6 @@ export function useRenameGroupDoc( const listed = await grpcClient.groups.listContent({ id: groupId, }) - console.log( - 'huh RENAME?!', - groupId, - pathName, - newPathName, - listed.content, - ) const prevPathValue = listed.content[pathName] if (!prevPathValue) throw new Error('Could not find previous path at ' + pathName) @@ -201,10 +194,10 @@ export function useRenameGroupDoc( return prevPathValue }, onSuccess: (result, input, context) => { - const [docId] = getIdsfromUrl(result) + const docId = unpackDocId(result) opts?.onSuccess?.(result, input, context) invalidate([queryKeys.GET_GROUP_CONTENT, input.groupId]) - invalidate([queryKeys.GET_GROUPS_FOR_DOCUMENT, docId]) + invalidate([queryKeys.GET_GROUPS_FOR_DOCUMENT, docId?.docId]) }, }) } @@ -240,7 +233,7 @@ export function useDocumentGroups(documentId?: string) { queryKey: [queryKeys.GET_GROUPS_FOR_DOCUMENT, documentId], queryFn: async () => { const result = await grpcClient.groups.listDocumentGroups({ - documentId: `${HYPERMEDIA_DOCUMENT_PREFIX}${documentId}`, + documentId, }) const resultMap = new Map< string, diff --git a/frontend/packages/app/src/models/publication.ts b/frontend/packages/app/src/models/publication.ts index bc1c225de7..16b2e7d37b 100644 --- a/frontend/packages/app/src/models/publication.ts +++ b/frontend/packages/app/src/models/publication.ts @@ -1,4 +1,4 @@ -import {Publication, getIdsfromUrl} from '@mintter/shared' +import {Publication, unpackDocId} from '@mintter/shared' import {UseQueryOptions} from '@tanstack/react-query' import {usePublication} from './documents' import {PublicationRouteContext} from '../utils/navigation' @@ -36,12 +36,12 @@ export function usePublicationInContext({ // ) queryDocumentId = undefined } - const [groupContentDocId, groupContentVersion] = getIdsfromUrl(contentURL) - if (groupContentDocId !== documentId) + const groupItem = unpackDocId(contentURL) + if (groupItem?.docId !== documentId) throw new Error( - `Group ${groupContextId} content for "${groupContext.pathName}" not match route document id "${documentId}", instead has "${groupContentDocId}"`, + `Group ${groupContextId} content for "${groupContext.pathName}" not match route document id "${documentId}", instead has "${groupItem?.docId}"`, ) - queryVersionId = groupContentVersion + queryVersionId = groupItem?.version } // this avoids querying usePublication if we are in a group context and the group content is not yet loaded, or if it has an error. if the route specifies the version directly we are also ready to query const pubQueryReady = !!queryVersionId || pubContext?.key !== 'group' diff --git a/frontend/packages/app/src/models/sites.ts b/frontend/packages/app/src/models/sites.ts index e450aa0b82..d7e47b7ab1 100644 --- a/frontend/packages/app/src/models/sites.ts +++ b/frontend/packages/app/src/models/sites.ts @@ -1,13 +1,13 @@ import { Block, Document, - getIdsfromUrl, GRPCClient, Member, Member_Role, ReferencedDocument, SiteConfig, SiteInfo, + unpackDocId, } from '@mintter/shared' import {useMutation, UseMutationOptions, useQuery} from '@tanstack/react-query' import {queryKeys} from './query-keys' @@ -22,14 +22,10 @@ function blockExtractReferencedDocs( const docIds: Array = [] block.annotations.forEach((annotation) => { if (annotation.type === 'embed' || annotation.type === 'link') { - let ids - try { - ids = getIdsfromUrl(annotation.attributes.url) - } catch (e) { - // not the best fix for now, but regular URLS are coming through here and we can just skip over them - } - if (ids?.[0]) { - docIds.push({documentId: ids[0], version: ids[1]}) + const ids = unpackDocId(annotation.attributes.url) + + if (ids?.scheme === 'hm') { + docIds.push({documentId: ids?.docId, version: ids?.version}) } } }) diff --git a/frontend/packages/app/src/open-url.ts b/frontend/packages/app/src/open-url.ts index c827e50e4c..70d6d5d5df 100644 --- a/frontend/packages/app/src/open-url.ts +++ b/frontend/packages/app/src/open-url.ts @@ -1,8 +1,35 @@ import {useAppContext} from '@mintter/app/src/app-context' import {NavRoute, useNavigate} from '@mintter/app/src/utils/navigation' -import {getIdsfromUrl, HYPERMEDIA_DOCUMENT_PREFIX} from '@mintter/shared' +import {createHmId, isHypermediaScheme, unpackHmId} from '@mintter/shared' import {useMemo} from 'react' +export function hmIdToAppRoute(hmId: string): NavRoute | undefined { + const hmIds = unpackHmId(hmId) + + let pubRoute: NavRoute | undefined = undefined + if (hmIds?.scheme === 'hm') { + if (hmIds?.type === 'd') { + pubRoute = { + key: 'publication', + documentId: createHmId('d', hmIds.eid), + versionId: hmIds.version, + blockId: hmIds.blockRef, + } + } else if (hmIds?.type === 'g') { + pubRoute = { + key: 'group', + groupId: createHmId('g', hmIds.eid), + } + } else if (hmIds?.type === 'a') { + pubRoute = { + key: 'account', + accountId: hmIds.eid, + } + } + } + return pubRoute +} + export function useOpenUrl() { const {externalOpen} = useAppContext() const spawn = useNavigate('spawn') @@ -11,17 +38,8 @@ export function useOpenUrl() { return (url?: string, newWindow?: boolean) => { if (!url) return - if (url.startsWith(HYPERMEDIA_DOCUMENT_PREFIX)) { - const hmIds = getIdsfromUrl(url) - if (!hmIds[0]) { - throw new Error('Cannot parse Hyperdocs URL without document ID') - } - const pubRoute: NavRoute = { - key: 'publication', - documentId: hmIds[0], - versionId: hmIds[1], - blockId: hmIds[2], - } + const pubRoute = hmIdToAppRoute(url) + if (pubRoute) { if (newWindow) { spawn(pubRoute) } else { diff --git a/frontend/packages/app/src/pages/group.tsx b/frontend/packages/app/src/pages/group.tsx index 8be22b25b3..60125c2da2 100644 --- a/frontend/packages/app/src/pages/group.tsx +++ b/frontend/packages/app/src/pages/group.tsx @@ -1,5 +1,5 @@ import Footer from '@mintter/app/src/components/footer' -import {Document, getIdsfromUrl} from '@mintter/shared' +import {Document, unpackDocId} from '@mintter/shared' import { Button, Container, @@ -263,16 +263,18 @@ export default function GroupPage() { {Object.entries(groupContent.data?.content || {}).map( ([pathName, hmUrl]) => { - const [docId, version] = getIdsfromUrl(hmUrl) + const docId = unpackDocId(hmUrl) if (!docId) return null return ( d.id == docId)} + version={docId?.version} + hasDraft={drafts.data?.documents.find( + (d) => d.id == docId.docId, + )} pathName={pathName} /> ) diff --git a/frontend/packages/app/src/pages/site-page.tsx b/frontend/packages/app/src/pages/site-page.tsx index f72fa154f5..1cc7531e4a 100644 --- a/frontend/packages/app/src/pages/site-page.tsx +++ b/frontend/packages/app/src/pages/site-page.tsx @@ -2,7 +2,6 @@ import {useAccount} from '@mintter/app/src/models/accounts' import {usePublication} from '@mintter/app/src/models/documents' import {useSitePublications} from '@mintter/app/src/models/sites' import {usePopoverState} from '@mintter/app/src/use-popover-state' -import {getDocUrl} from '@mintter/shared' import {useNavigate, useNavRoute} from '@mintter/app/src/utils/navigation' import {useOpenDraft} from '@mintter/app/src/utils/open-draft' import {hostnameStripProtocol} from '@mintter/app/src/utils/site-hostname' @@ -161,22 +160,6 @@ function WebPublicationListItem({ align="start" data-testid="library-item-dropdown-root" > - { - const docUrl = getDocUrl(publication, webPub) - if (!docUrl) return - copyTextToClipboard(docUrl) - toast.success( - `Copied ${hostnameStripProtocol(publishedWebHost)} URL`, - ) - }} - asChild - icon={Copy} - title={`Copy Document URL on ${hostnameStripProtocol( - publishedWebHost, - )}`} - /> { + test('unpacks hm://d/abc', () => { + expect(unpackHmId('hm://d/abc')).toEqual({ + scheme: 'hm', + hostname: undefined, + type: 'd', + eid: 'abc', + version: undefined, + blockRef: undefined, + }) + }) + test('unpacks hm://g/abc?v=123#foo', () => { + expect(unpackHmId('hm://g/abc?v=123#foo')).toEqual({ + scheme: 'hm', + hostname: undefined, + type: 'g', + eid: 'abc', + version: '123', + blockRef: 'foo', + }) + }) + test('unpacks hm://d/foo#bar', () => { + expect(unpackHmId('hm://d/foo#bar')).toEqual({ + scheme: 'hm', + hostname: undefined, + type: 'd', + eid: 'foo', + version: undefined, + blockRef: 'bar', + }) + }) + test('unpacks hm://a/foo?v=bar', () => { + expect(unpackHmId('hm://a/foo?v=bar')).toEqual({ + scheme: 'hm', + hostname: undefined, + type: 'a', + eid: 'foo', + version: 'bar', + blockRef: undefined, + }) + }) + test('unpacks https://foobar.com/d/1?v=2', () => { + expect(unpackHmId('https://foobar.com/d/1?v=2')).toEqual({ + scheme: 'https', + hostname: 'foobar.com', + type: 'd', + eid: '1', + version: '2', + blockRef: undefined, + }) + }) + test('unpacks http://foobar.com/a/1#block', () => { + expect(unpackHmId('http://foobar.com/a/1#block')).toEqual({ + scheme: 'http', + hostname: 'foobar.com', + type: 'a', + eid: '1', + version: undefined, + blockRef: 'block', + }) + }) +}) +describe('parseCustomURL', () => { + test('parseCustomURL hm://a/b?foo=1=&bar=2#block', () => { + expect(parseCustomURL('hm://a/b?foo=1=&bar=2#block')).toEqual({ + scheme: 'hm', + path: ['a', 'b'], + query: {foo: '1', bar: '2'}, + fragment: 'block', + }) + }) +}) +describe('createHmId', () => { + test('creates hm://d/abc', () => { + expect(createHmId('d', 'abc')).toEqual('hm://d/abc') + }) + test('creates hm://g/123?v=foo', () => { + expect(createHmId('g', '123', {version: 'foo'})).toEqual('hm://g/123?v=foo') + }) + test('creates hm://d/123#block', () => { + expect(createHmId('d', '123', {blockRef: 'block'})).toEqual( + 'hm://d/123#block', + ) + }) + test('creates hm://a/123?v=foo#bar', () => { + expect(createHmId('a', '123', {version: 'foo', blockRef: 'bar'})).toEqual( + 'hm://a/123?v=foo#bar', + ) + }) +}) + +describe('unpackDocId', () => { + it('should return values from matching URLs', () => { + const result = unpackDocId('https://hyper.media/d/foo?v=bar#block') + expect(result).toEqual({ + docId: 'hm://d/foo', + eid: 'foo', + hostname: 'hyper.media', + scheme: 'https', + type: 'd', + version: 'bar', + blockRef: 'block', + }) + }) + + it('should handle URLs without version and blockId', () => { + const result = unpackDocId('http://gabo.es/d/anotherpath') + expect(result).toEqual({ + docId: 'hm://d/anotherpath', + eid: 'anotherpath', + hostname: 'gabo.es', + scheme: 'http', + type: 'd', + version: undefined, + blockRef: undefined, + }) + }) + + it('should handle Fully Qualified IDs', () => { + const result = unpackDocId('hm://d/abc123') + expect(result).toEqual({ + docId: 'hm://d/abc123', + eid: 'abc123', + hostname: undefined, + scheme: 'hm', + type: 'd', + version: undefined, + blockRef: undefined, + }) + }) +}) diff --git a/frontend/packages/shared/src/utils/__tests__/get-ids-from-url.test.ts b/frontend/packages/shared/src/utils/__tests__/get-ids-from-url.test.ts deleted file mode 100644 index b643d4dac3..0000000000 --- a/frontend/packages/shared/src/utils/__tests__/get-ids-from-url.test.ts +++ /dev/null @@ -1,75 +0,0 @@ -import { - extractHypermediaWebsiteValues, - getIdsfromUrl, - matchesHypermediaPattern, -} from '../get-ids-from-url' -import {describe, expect, it, test} from 'vitest' - -const testCases: Array<[string, boolean]> = [ - ['https://mintter.com/d/foo', true], - ['https://www.mintter.com/d/foo', true], - ['https://mintter.com/d/foo?v=bar', true], - ['https://mintter.com/d/foo?v=bar#block', true], - ['https://www.mintter.com/d/foo?v=bar#block', true], - ['https://gabo.es/d/anotherpath', true], - ['https://gabo.es/d/anotherpath?v=versionhere', true], - ['https://gabo.es/d/anotherpath?v=bar#block', true], - ['https://www.hhg.link/g/somegroupid', true], - ['https://juligasa.es/a/accountid', true], - ['https://example.com/invalid', false], - ['http://example.com/a/shouldbeinvalid', false], -] - -describe('matchesHypermediaPattern', () => { - test.each(testCases)('should match valid URLs', (url, expected) => { - expect(matchesHypermediaPattern(url)).toBe(expected) - }) -}) - -describe('extractHypermediaWebsiteValues', () => { - it('should extract values from valid URLs', () => { - const values = extractHypermediaWebsiteValues( - 'https://mintter.com/d/foo?v=bar#block', - ) - expect(values).toEqual({ - hostname: 'mintter.com', - pathType: 'd', - docId: 'foo', - version: 'bar', - blockId: 'block', - }) - // Add more test cases here - }) - - it('should return null for invalid URLs', () => { - const values = extractHypermediaWebsiteValues('https://example.com/invalid') - expect(values).toBeNull() - // Add more test cases here - }) -}) - -describe('getIdsfromUrl', () => { - it('should return values from matching URLs', () => { - const result = getIdsfromUrl('https://mintter.com/d/foo?v=bar#block') - expect(result).toEqual(['foo', 'bar', 'block']) - // Add more test cases here - }) - - it('should handle URLs without version and blockId', () => { - const result = getIdsfromUrl('https://gabo.es/d/anotherpath') - expect(result).toEqual(['anotherpath', undefined, undefined]) - // Add more test cases here - }) - - it('should handle Fully Qualified IDs', () => { - const result = getIdsfromUrl('hm://d/abc123') - expect(result).toEqual(['abc123', undefined, undefined]) - // Add more test cases here - }) - - it('should handle invalid entries', () => { - const result = getIdsfromUrl('https://example.com/invalid') - expect(result).toEqual([undefined, undefined, undefined]) - // Add more test cases here - }) -}) diff --git a/frontend/packages/shared/src/utils/doc-url.ts b/frontend/packages/shared/src/utils/doc-url.ts deleted file mode 100644 index 04d9f4ee42..0000000000 --- a/frontend/packages/shared/src/utils/doc-url.ts +++ /dev/null @@ -1,60 +0,0 @@ -import {Publication, WebPublicationRecord} from '../client' - -const publicWebHost = 'https://mintter.com' - -export function getDocUrl( - pub: Publication | undefined, - webPub?: WebPublicationRecord, -): string | null { - const id = pub?.document?.id - if (!id) { - return null - } - const publishedWebHost = pub?.document - ? pub.document.webUrl || publicWebHost - : null - let path = `/d/${id}` - if (webPub?.path === '/') { - path = '/' - } else if (webPub?.path) { - path = `/${webPub.path}` - } - let docUrl = `${publishedWebHost}${path}?v=${pub.version}` - - return docUrl -} - -export function getPublicDocUrl(docId: string, version?: string | undefined) { - let webUrl = `${publicWebHost}/d/${docId}` - if (version) return `${webUrl}?v=${version}` - return webUrl -} - -export function extractEntityId(id: string): [string, string] | null { - // input is like hm://x/abcd. output is ['x', 'abcd'] - const m = id.match(/^hm:\/\/([^/]+)\/(.+)$/) - if (!m) return null - const entityType = m[1] - const entityEId = m[2] - return [entityType, entityEId] -} - -export function entityIdToSitePath(entityId?: string): string | null { - const [entityType, entityEId] = extractEntityId(entityId || '') || [] - if (!entityType || !entityEId) return null - if (entityType === 'g') return `/g/${entityEId}` - if (entityType === 'a') return `/a/${entityEId}` - if (entityType === 'd') return `/d/${entityEId}` - return null -} - -export function getPublicEntityUrl( - groupId: string, - version?: string | undefined, -) { - const extractedId = extractEntityId(groupId) - if (!extractedId) return null - let webUrl = `${publicWebHost}/${extractedId?.[0]}/${extractedId?.[1]}` - if (version) return `${webUrl}?v=${version}` - return webUrl -} diff --git a/frontend/packages/shared/src/utils/entity-id-url.ts b/frontend/packages/shared/src/utils/entity-id-url.ts new file mode 100644 index 0000000000..bf09c89ad9 --- /dev/null +++ b/frontend/packages/shared/src/utils/entity-id-url.ts @@ -0,0 +1,195 @@ +export const HYPERMEDIA_PUBLIC_WEB_GATEWAY = 'https://hyper.media' + +export const HYPERMEDIA_SCHEME = 'hm' +export const HYPERMEDIA_ENTITY_TYPES = { + a: 'Account', + d: 'Document', + g: 'Group', +} as const + +export function getPublicDocUrl(docId: string, version?: string | undefined) { + let webUrl = `${HYPERMEDIA_PUBLIC_WEB_GATEWAY}/d/${docId}` + if (version) return `${webUrl}?v=${version}` + return webUrl +} + +export function extractEntityId(id: string): [string, string] | null { + // input is like hm://x/abcd. output is ['x', 'abcd'] + const m = id.match(/^hm:\/\/([^/]+)\/(.+)$/) + if (!m) return null + const entityType = m[1] + const entityEId = m[2] + return [entityType, entityEId] +} + +export function isValidSiteEntity(entityType: string) { + if (entityType === 'a') return true + if (entityType === 'd') return true + if (entityType === 'g') return true + return false +} + +export function createPublicWebHmUrl( + type: keyof typeof HYPERMEDIA_ENTITY_TYPES, + eid: string, + { + version, + blockRef, + hostname, + }: { + version?: string + blockRef?: string + hostname?: string | null | undefined + } = {}, +) { + const webPath = `/${type}/${eid}` + let urlHost = + hostname === null + ? '' + : hostname === undefined + ? HYPERMEDIA_PUBLIC_WEB_GATEWAY + : hostname + let webUrl = `${urlHost}${webPath}` + if (version) webUrl += `?v=${version}` + if (blockRef) webUrl += `#${blockRef}` + return webUrl +} + +export function createHmId( + type: keyof typeof HYPERMEDIA_ENTITY_TYPES, + id: string, + opts?: {version?: string; blockRef?: string}, +) { + let outputUrl = `${HYPERMEDIA_SCHEME}://${type}/${id}` + if (opts?.version) outputUrl += `?v=${opts.version}` + if (opts?.blockRef) outputUrl += `#${opts.blockRef}` + return outputUrl +} + +type ParsedURL = { + scheme: string | null + path: string[] + query: Record + fragment: string | null +} + +export function parseCustomURL(url: string): ParsedURL | null { + const [scheme, rest] = url.split('://') + if (!rest) return null + const [pathAndQuery, fragment] = rest.split('#') + const [path, queryString] = pathAndQuery.split('?') + + const query: Record = {} + queryString?.split('&').forEach((param) => { + const [key, value] = param.split('=') + query[key] = decodeURIComponent(value) + }) + + return { + scheme, + path: path.split('/'), + query, + fragment: fragment || null, + } +} + +function inKeys( + key: string, + values: Record, +): V | null { + // @ts-expect-error + if (values[key]) return key as V + return null +} + +export function unpackHmId(hypermediaId: string) { + const parsed = parseCustomURL(hypermediaId) + if (parsed?.scheme === HYPERMEDIA_SCHEME) { + const type = inKeys(parsed?.path[0], HYPERMEDIA_ENTITY_TYPES) + const eid = parsed?.path[1] + const version = parsed?.query.v + return { + type, + eid, + version, + blockRef: parsed?.fragment || undefined, + hostname: undefined, + scheme: parsed?.scheme, + } + } + if (parsed?.scheme === 'https' || parsed?.scheme === 'http') { + const type = inKeys(parsed?.path[1], HYPERMEDIA_ENTITY_TYPES) + const eid = parsed?.path[2] + const version = parsed?.query.v + let hostname = parsed?.path[0] + return { + type, + eid, + version, + blockRef: parsed?.fragment || undefined, + hostname, + scheme: parsed?.scheme, + } + } + return null +} + +export function unpackDocId(inputUrl: string) { + const unpackedHm = unpackHmId(inputUrl) + if (!unpackedHm?.eid) return null + if (unpackedHm.type !== 'd') { + throw new Error('URL is expected to be a document ID: ' + inputUrl) + } + return { + eid: unpackedHm.eid, + type: 'd', + docId: createHmId('d', unpackedHm.eid), + version: unpackedHm.version, + blockRef: unpackedHm.blockRef, + hostname: unpackedHm.hostname, + scheme: unpackedHm.scheme, + } +} + +export function isHypermediaScheme(url?: string) { + return !!url?.startsWith(`${HYPERMEDIA_SCHEME}://`) +} + +export function isPublicGatewayLink(text: string) { + const matchesGateway = text.indexOf(HYPERMEDIA_PUBLIC_WEB_GATEWAY) === 0 + console.log('PATH', text.split(HYPERMEDIA_PUBLIC_WEB_GATEWAY)[1]) + return !!matchesGateway +} + +export function idToUrl(hmId: string, hostname?: string | null) { + const unpacked = unpackHmId(hmId) + if (!unpacked?.type) return null + return createPublicWebHmUrl(unpacked.type, unpacked.eid, { + version: unpacked.version, + blockRef: unpacked.blockRef, + hostname, + }) +} + +export function normlizeHmId(urlMaybe: string): string | undefined { + if (isHypermediaScheme(urlMaybe)) return urlMaybe + if (isPublicGatewayLink(urlMaybe)) { + const unpacked = unpackHmId(urlMaybe) + + if (unpacked?.eid && unpacked.type) { + return createHmId(unpacked.type, unpacked.eid, unpacked) + } + return undefined + } +} + +export function createHmDocLink( + documentId: string, + version?: string | null, + blockRef?: string | null, +): string { + let res = documentId + if (version) res += `?v=${version}` + if (blockRef) res += `${!blockRef.startsWith('#') ? '#' : ''}${blockRef}` + return res +} diff --git a/frontend/packages/shared/src/utils/entity-ids.ts b/frontend/packages/shared/src/utils/entity-ids.ts deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/frontend/packages/shared/src/utils/get-ids-from-url.ts b/frontend/packages/shared/src/utils/get-ids-from-url.ts deleted file mode 100644 index 2dae3a5633..0000000000 --- a/frontend/packages/shared/src/utils/get-ids-from-url.ts +++ /dev/null @@ -1,88 +0,0 @@ -export const HYPERMEDIA_DOCUMENT_PREFIX = 'hm://d/' -export const HYPERMEDIA_ACCOUNT_PREFIX = 'hm://a/' -export const HYPERMEDIA_GROUP_PREFIX = 'hm://g/' -export const HYPERMEDIA_SITES_PATTERN = - /^https:\/\/(www\.)?([^/]+)\/(d|a|g)\/([^/?#]+)(\?.+)?(#.+)?$/ - -export function matchesHypermediaPattern(url: string): boolean { - return HYPERMEDIA_SITES_PATTERN.test(url) - - // Test cases - // const testCases = [ - // "https://mintter.com/d/foo", - // "https://mintter.com/d/foo?v=bar", - // "https://mintter.com/d/foo?v=bar#block", - // "https://www.mintter.com/d/foo?v=bar#block", - // "https://gabo.es/d/anotherpath", - // "https://gabo.es/d/anotherpath?v=versionhere", - // "https://gabo.es/d/anotherpath?v=bar#block", - // "https://www.hhg.link/g/somegroupid", - // "https://juligasa.es/a/accountid" - // ]; -} - -export function extractHypermediaWebsiteValues(url: string) { - const match = url.match(HYPERMEDIA_SITES_PATTERN) - - if (!match) { - return null - } - - const [, , hostname, pathType, docId, queryAndFragment] = match - const [query, fragment] = queryAndFragment ? queryAndFragment.split('#') : [] - - const versionMatch = query && query.match(/[?&]v=([^&]+)/) - const version = versionMatch ? versionMatch[1] : undefined - - return { - hostname, - pathType, - docId, - version, - blockId: fragment || undefined, - } -} - -export function getIdsfromUrl( - entry: string, -): [docId: string | undefined, version?: string, blockId?: string] { - console.log(`[getIdsfromUrl]: START -> ${entry}`) - if (matchesHypermediaPattern(entry)) { - const values = extractHypermediaWebsiteValues(entry) - if (values) { - let {docId, version, blockId} = values - - console.log( - `[getIdsfromUrl]: entry match web sites pattern: ${entry} -> ${JSON.stringify( - values, - )}`, - ) - return [docId, version, blockId] - } - } - - if (entry.startsWith(HYPERMEDIA_DOCUMENT_PREFIX)) { - const [, restUrl] = entry.split(HYPERMEDIA_DOCUMENT_PREFIX) - - if (restUrl.length > 3) { - let firstSplit = restUrl.split('?v=') - const [docId] = firstSplit - const [version, blockId] = - firstSplit.length > 1 - ? restUrl.split('?v=')[1].split('#') - : [undefined, undefined] - console.log( - `[getIdsfromUrl]: entry match Fully Qualified ID: ${entry}, docId = ${docId}, version = ${version}, blockId = ${blockId}`, - ) - return [docId, version, blockId] - } else { - console.warn( - `[getIdsfromUrl]: entry match Fully Qualified ID, but does not satisfy the correct length: ${entry}`, - ) - } - } - - console.warn(`[getIdsfromUrl]: entry does not match any pattern: ${entry}`) - - return [undefined, undefined, undefined] -} diff --git a/frontend/packages/shared/src/utils/hyperdocs-link.ts b/frontend/packages/shared/src/utils/hyperdocs-link.ts deleted file mode 100644 index 0d379c9fef..0000000000 --- a/frontend/packages/shared/src/utils/hyperdocs-link.ts +++ /dev/null @@ -1,61 +0,0 @@ -import { - extractHypermediaWebsiteValues, - getIdsfromUrl, - HYPERMEDIA_DOCUMENT_PREFIX, -} from './get-ids-from-url' - -export function isHypermediaScheme(url?: string) { - return !!url?.startsWith(HYPERMEDIA_DOCUMENT_PREFIX) -} - -export function isMintterGatewayLink(text: string) { - return extractHypermediaWebsiteValues(text) -} - -export function normalizeHypermediaLink(urlMaybe: string): string | undefined { - if (isHypermediaScheme(urlMaybe)) return urlMaybe - if (isMintterGatewayLink(urlMaybe)) { - const [docId, version, blockRef] = getIdsfromUrl(urlMaybe) - - if (docId && docId != 'undefined') { - return createHyperdocsDocLink(docId, version, blockRef) - } - return undefined - } -} - -export function createLinkParams( - documentId: string, - version?: string | null, - blockRef?: string | null, -): string { - let res = documentId - if (version) res += `?v=${version}` - if (blockRef) res += `${!blockRef.startsWith('#') ? '#' : ''}${blockRef}` - - return res -} - -export function createHyperdocsDocLink( - documentId: string, - version?: string | null, - blockRef?: string | null, -): string { - return `${HYPERMEDIA_DOCUMENT_PREFIX}${createLinkParams( - documentId, - version, - blockRef, - )}` -} - -export function createMintterLink( - documentId: string, - version?: string | null, - blockRef?: string | null, -): string { - return `https://mintter.com/d/${createLinkParams( - documentId, - version, - blockRef, - )}` -} diff --git a/frontend/packages/shared/src/utils/index.ts b/frontend/packages/shared/src/utils/index.ts index 3d81b82e8e..375a83354f 100644 --- a/frontend/packages/shared/src/utils/index.ts +++ b/frontend/packages/shared/src/utils/index.ts @@ -1,7 +1,5 @@ export * from './abbreviate' export * from './date' -export * from './doc-url' -export * from './get-ids-from-url' +export * from './entity-id-url' export * from './language' export * from './ipfs-cid' -export * from './hyperdocs-link' diff --git a/yarn.lock b/yarn.lock index 7f3e57c0e7..afc96a6a66 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6905,15 +6905,15 @@ __metadata: languageName: node linkType: hard -"@sentry-internal/tracing@npm:7.68.0, @sentry-internal/tracing@npm:^7.66.0": - version: 7.68.0 - resolution: "@sentry-internal/tracing@npm:7.68.0" +"@sentry-internal/tracing@npm:7.69.0, @sentry-internal/tracing@npm:^7.66.0": + version: 7.69.0 + resolution: "@sentry-internal/tracing@npm:7.69.0" dependencies: - "@sentry/core": 7.68.0 - "@sentry/types": 7.68.0 - "@sentry/utils": 7.68.0 + "@sentry/core": 7.69.0 + "@sentry/types": 7.69.0 + "@sentry/utils": 7.69.0 tslib: ^2.4.1 || ^1.9.3 - checksum: 93523660ede29988054451e6af6e4f38102753a0bd851c0623ccd05207e3fc63eaf01559c1ffc76e4e234624df9ee26834e693426995907ce646cb750e80a3f2 + checksum: 3ccb7e7d008dd39ed2bb9a02fcd7ae6161a8355451891db25020d8068357254a430e697c4f72c4d1d747754585ca0f610cea6798d51b6a791ae2c73ee399b58e languageName: node linkType: hard @@ -6931,17 +6931,17 @@ __metadata: languageName: node linkType: hard -"@sentry/browser@npm:7.68.0": - version: 7.68.0 - resolution: "@sentry/browser@npm:7.68.0" +"@sentry/browser@npm:7.69.0": + version: 7.69.0 + resolution: "@sentry/browser@npm:7.69.0" dependencies: - "@sentry-internal/tracing": 7.68.0 - "@sentry/core": 7.68.0 - "@sentry/replay": 7.68.0 - "@sentry/types": 7.68.0 - "@sentry/utils": 7.68.0 + "@sentry-internal/tracing": 7.69.0 + "@sentry/core": 7.69.0 + "@sentry/replay": 7.69.0 + "@sentry/types": 7.69.0 + "@sentry/utils": 7.69.0 tslib: ^2.4.1 || ^1.9.3 - checksum: 85d688902057d85113cd6fb46e4ca17167bfda6ce77c8264f1f29bf7d451dd73315ebcf7134ac91d539fc4032449ac25b0dd6e9209695bd934a4716acda55aac + checksum: 8b5460c1733628ecb1dee16228f0e055fcf7787f97ce85554a5fddd7dbdf787a6be0f61c8180756144139ec3bb6a0647e95f82772e601353f1fc64e32d5fda91 languageName: node linkType: hard @@ -7014,14 +7014,14 @@ __metadata: languageName: node linkType: hard -"@sentry/core@npm:7.68.0": - version: 7.68.0 - resolution: "@sentry/core@npm:7.68.0" +"@sentry/core@npm:7.69.0": + version: 7.69.0 + resolution: "@sentry/core@npm:7.69.0" dependencies: - "@sentry/types": 7.68.0 - "@sentry/utils": 7.68.0 + "@sentry/types": 7.69.0 + "@sentry/utils": 7.69.0 tslib: ^2.4.1 || ^1.9.3 - checksum: 1601fba3f7c8b2f35c3212d8f51690cb25efb17a7c21698ae67fdcbe3ae17f56c381edb51870739649752aee291efe742fb3e169b3118dc2690eb820de043766 + checksum: b24ec3121dd899dc53edaf1ca984f6df4fab3cd9dc1756b2037729c61e33df47ad4b94abb0dc24fc2dfb6099396a3e9df7f13d0e4673184c93e5932dcfb9a8e1 languageName: node linkType: hard @@ -7040,29 +7040,29 @@ __metadata: languageName: node linkType: hard -"@sentry/integrations@npm:7.68.0": - version: 7.68.0 - resolution: "@sentry/integrations@npm:7.68.0" +"@sentry/integrations@npm:7.69.0": + version: 7.69.0 + resolution: "@sentry/integrations@npm:7.69.0" dependencies: - "@sentry/types": 7.68.0 - "@sentry/utils": 7.68.0 + "@sentry/types": 7.69.0 + "@sentry/utils": 7.69.0 localforage: ^1.8.1 tslib: ^2.4.1 || ^1.9.3 - checksum: 7bc15750c93015afc661bf3306628a3d34b16e34a5f56758ac8ec25ee33a80d0191f28ee40e3fe821408f4c725c67da32fa1f51ca69f6456c52a1a5e6f54163a + checksum: 500d9e6a0a65f7c5df5f043f476ccda91d15b223fe4119196dfa6f9d004fd53f62951187308d3e50c185e1f908f4024547e1889f71c886869aac075e7e8128cb languageName: node linkType: hard "@sentry/nextjs@npm:latest": - version: 7.68.0 - resolution: "@sentry/nextjs@npm:7.68.0" + version: 7.69.0 + resolution: "@sentry/nextjs@npm:7.69.0" dependencies: "@rollup/plugin-commonjs": 24.0.0 - "@sentry/core": 7.68.0 - "@sentry/integrations": 7.68.0 - "@sentry/node": 7.68.0 - "@sentry/react": 7.68.0 - "@sentry/types": 7.68.0 - "@sentry/utils": 7.68.0 + "@sentry/core": 7.69.0 + "@sentry/integrations": 7.69.0 + "@sentry/node": 7.69.0 + "@sentry/react": 7.69.0 + "@sentry/types": 7.69.0 + "@sentry/utils": 7.69.0 "@sentry/webpack-plugin": 1.20.0 chalk: 3.0.0 rollup: 2.78.0 @@ -7075,7 +7075,7 @@ __metadata: peerDependenciesMeta: webpack: optional: true - checksum: afe7b745484fdd937f3969c2635f1aa5df20d63b5d35f2a9e365870871f4b9ac89e2a9520bbb76da8538be8ae059a46ecb8f61a21292a47451ff8048dac384f6 + checksum: c2b8580eaa6c3bdb48c56555c6afdbf8cae755953dcf4a41dd3a6f9c5f6b5769cb2f648e9eccc0e97373db6fc0fa31b28639dd1e63954a54647088e76fb9fc11 languageName: node linkType: hard @@ -7095,34 +7095,34 @@ __metadata: languageName: node linkType: hard -"@sentry/node@npm:7.68.0, @sentry/node@npm:^7.60.0": - version: 7.68.0 - resolution: "@sentry/node@npm:7.68.0" +"@sentry/node@npm:7.69.0, @sentry/node@npm:^7.60.0": + version: 7.69.0 + resolution: "@sentry/node@npm:7.69.0" dependencies: - "@sentry-internal/tracing": 7.68.0 - "@sentry/core": 7.68.0 - "@sentry/types": 7.68.0 - "@sentry/utils": 7.68.0 + "@sentry-internal/tracing": 7.69.0 + "@sentry/core": 7.69.0 + "@sentry/types": 7.69.0 + "@sentry/utils": 7.69.0 cookie: ^0.4.1 https-proxy-agent: ^5.0.0 lru_map: ^0.3.3 tslib: ^2.4.1 || ^1.9.3 - checksum: bcbcce099830e89a8b0cd48b4505fa4f88d14d818cdb3a6861dba8cd0e77b492733fb1e1f33f6aa5606b05b14146a23dffdb4b829a85bb3c31b2bc7b7b5290fb + checksum: 97210ced968a3d968fd9d93e67e1f3c9613b99b223f87fad944e6e94db40ebc10a7c339c848e0529c5ded69f94f1f689b4a6df1da4df1aad6663a752ac591d03 languageName: node linkType: hard -"@sentry/react@npm:7.68.0": - version: 7.68.0 - resolution: "@sentry/react@npm:7.68.0" +"@sentry/react@npm:7.69.0": + version: 7.69.0 + resolution: "@sentry/react@npm:7.69.0" dependencies: - "@sentry/browser": 7.68.0 - "@sentry/types": 7.68.0 - "@sentry/utils": 7.68.0 + "@sentry/browser": 7.69.0 + "@sentry/types": 7.69.0 + "@sentry/utils": 7.69.0 hoist-non-react-statics: ^3.3.2 tslib: ^2.4.1 || ^1.9.3 peerDependencies: react: 15.x || 16.x || 17.x || 18.x - checksum: 73b9c3e2e3805ec068d95ceef0702783604c972b1ea873523ec63af9b3ce73667eb9fecbfe0838a5b0f33fe87da97cd2f6c1a74859e342c637adf15999f1bae2 + checksum: 5aa863918be7e7122967c40e0f83946081eda2fc05ff563d0a65ba0329e51954992576e6e5b27d714b134a603c06067cc9ff78fb8f8c8c43824a3ebe02c127ae languageName: node linkType: hard @@ -7137,14 +7137,14 @@ __metadata: languageName: node linkType: hard -"@sentry/replay@npm:7.68.0": - version: 7.68.0 - resolution: "@sentry/replay@npm:7.68.0" +"@sentry/replay@npm:7.69.0": + version: 7.69.0 + resolution: "@sentry/replay@npm:7.69.0" dependencies: - "@sentry/core": 7.68.0 - "@sentry/types": 7.68.0 - "@sentry/utils": 7.68.0 - checksum: a54871ec011bdd7df329fe684f3790932d1bd3c08ea7a849f5e1d9bde6cdae3b96993687522a92e0cfaf529f4e7439e28db1da5216e13d633af2dcf121b6f505 + "@sentry/core": 7.69.0 + "@sentry/types": 7.69.0 + "@sentry/utils": 7.69.0 + checksum: 47d9ccfd6a619ef1640a2649fccce7a0824e52e12c84759d40c569416ad65886e50bd30c3b789c6fe9d121060e3e55f4344925470dfd9db12ac678703b005f72 languageName: node linkType: hard @@ -7171,10 +7171,10 @@ __metadata: languageName: node linkType: hard -"@sentry/types@npm:7.68.0": - version: 7.68.0 - resolution: "@sentry/types@npm:7.68.0" - checksum: 9854e14b6864dc2b649655dc10a6f05d1e960d8fb2e2b0cb178073d884118314f790aca633d208ab0ab76b173523a66d066fb915294c0d5412e00456fa89343d +"@sentry/types@npm:7.69.0": + version: 7.69.0 + resolution: "@sentry/types@npm:7.69.0" + checksum: aaa40a43cab358e10c2566d62966eff61925fb2605c146967bf9eb8acb4a883d4ca7c8a5eee1915271da08f27ddf1ed7dc520a8617f229ce70c7d00557173cc4 languageName: node linkType: hard @@ -7198,13 +7198,13 @@ __metadata: languageName: node linkType: hard -"@sentry/utils@npm:7.68.0, @sentry/utils@npm:^7.60.0": - version: 7.68.0 - resolution: "@sentry/utils@npm:7.68.0" +"@sentry/utils@npm:7.69.0, @sentry/utils@npm:^7.60.0": + version: 7.69.0 + resolution: "@sentry/utils@npm:7.69.0" dependencies: - "@sentry/types": 7.68.0 + "@sentry/types": 7.69.0 tslib: ^2.4.1 || ^1.9.3 - checksum: fd80eb9379ffb208864906b693e599d01a9229f523cf6516c261abe37316afd9dedd83a5690355e4823290164099e129957bf7dc3891ab724ac6e00b56908cc9 + checksum: 8c18b6a1c5a869d608ff9cdb4534b29c5c85a06660647d8d8dfd67460be985d44a88f1ec4335174d40f147fecaa2e952b78747e83b6ed2f654e93248744fa291 languageName: node linkType: hard @@ -10892,13 +10892,13 @@ __metadata: linkType: hard "algoliasearch-helper@npm:^3.10.0": - version: 3.14.0 - resolution: "algoliasearch-helper@npm:3.14.0" + version: 3.14.1 + resolution: "algoliasearch-helper@npm:3.14.1" dependencies: "@algolia/events": ^4.0.1 peerDependencies: algoliasearch: ">= 3.1 < 6" - checksum: bfda2125432eb6807241bb1ebfeb9b43ece456a759e160c28f34ef50c53eb763428fe7276a9e6b32ae70eba7346f89a1251524b5c8cfebe3bce45abe44af669d + checksum: 30b0ebba46450cb44ec1625b1425d9c4f7bf0660d6d5dd0448f98d690418bd709c19ee6c1631293333922cdcc22cd5621d0bf48407dbd04273810c61affdc2f2 languageName: node linkType: hard @@ -12368,9 +12368,9 @@ __metadata: linkType: hard "caniuse-lite@npm:^1.0.0, caniuse-lite@npm:^1.0.30001406, caniuse-lite@npm:^1.0.30001517, caniuse-lite@npm:^1.0.30001520": - version: 1.0.30001533 - resolution: "caniuse-lite@npm:1.0.30001533" - checksum: 1ffb2d69f817ee5f13351cb01c26a98ac61515809a6ce355305df3fbc6de6391a58466c1dcad1f322231b1ddc59bdda9bc52b9480cac100c3ff2f5782e859eb1 + version: 1.0.30001534 + resolution: "caniuse-lite@npm:1.0.30001534" + checksum: 8e8b63c1ce0d5b944ee2d8223955b33f3d4f60c33fed394ff6353b5c7106b2dc55219fd07d80c79e66ed1f82ed9367cee17bda96789cbd2ab57c8d30b1b5c510 languageName: node linkType: hard @@ -12754,9 +12754,9 @@ __metadata: linkType: hard "cli-spinners@npm:^2.5.0": - version: 2.9.0 - resolution: "cli-spinners@npm:2.9.0" - checksum: a9c56e1f44457d4a9f4f535364e729cb8726198efa9e98990cfd9eda9e220dfa4ba12f92808d1be5e29029cdfead781db82dc8549b97b31c907d55f96aa9b0e2 + version: 2.9.1 + resolution: "cli-spinners@npm:2.9.1" + checksum: 1780618be58309c469205bc315db697934bac68bce78cd5dfd46248e507a533172d623c7348ecfd904734f597ce0a4e5538684843d2cfb7af485d4466699940c languageName: node linkType: hard @@ -14186,6 +14186,17 @@ __metadata: languageName: node linkType: hard +"define-data-property@npm:^1.0.1": + version: 1.1.0 + resolution: "define-data-property@npm:1.1.0" + dependencies: + get-intrinsic: ^1.2.1 + gopd: ^1.0.1 + has-property-descriptors: ^1.0.0 + checksum: 7ad4ee84cca8ad427a4831f5693526804b62ce9dfd4efac77214e95a4382aed930072251d4075dc8dc9fc949a353ed51f19f5285a84a788ba9216cc51472a093 + languageName: node + linkType: hard + "define-lazy-prop@npm:^2.0.0": version: 2.0.0 resolution: "define-lazy-prop@npm:2.0.0" @@ -14201,12 +14212,13 @@ __metadata: linkType: hard "define-properties@npm:^1.1.3, define-properties@npm:^1.1.4, define-properties@npm:^1.2.0": - version: 1.2.0 - resolution: "define-properties@npm:1.2.0" + version: 1.2.1 + resolution: "define-properties@npm:1.2.1" dependencies: + define-data-property: ^1.0.1 has-property-descriptors: ^1.0.0 object-keys: ^1.1.1 - checksum: e60aee6a19b102df4e2b1f301816804e81ab48bb91f00d0d935f269bf4b3f79c88b39e4f89eaa132890d23267335fd1140dfcd8d5ccd61031a0a2c41a54e33a6 + checksum: b4ccd00597dd46cb2d4a379398f5b19fca84a16f3374e2249201992f36b30f6835949a9429669ee6b41b6e837205a163eadd745e472069e70dfc10f03e5fcc12 languageName: node linkType: hard @@ -14839,9 +14851,9 @@ __metadata: linkType: hard "electron-to-chromium@npm:^1.4.477": - version: 1.4.517 - resolution: "electron-to-chromium@npm:1.4.517" - checksum: e6a6643bf9cd4fe60d0ab902167e5b04bf183a6abbc246d7e4125ef4f76e3b3ff9847dcd34caaf545d9c6ee13ca9ef87ebe29fa11f64f6a0b369b40ed435dec8 + version: 1.4.519 + resolution: "electron-to-chromium@npm:1.4.519" + checksum: 2599e5b62a045698015f5feafa0ca19d659d887a9b289cde19c24981d06ede843feb928c7ef1bb2aa63a1d25694d1b1e385c9c0959d56b4bfe98b05cb63eecf0 languageName: node linkType: hard @@ -20616,14 +20628,14 @@ __metadata: linkType: hard "lib0@npm:^0.2.42, lib0@npm:^0.2.74": - version: 0.2.84 - resolution: "lib0@npm:0.2.84" + version: 0.2.85 + resolution: "lib0@npm:0.2.85" dependencies: isomorphic.js: ^0.2.4 bin: 0gentesthtml: bin/gentesthtml.js 0serve: bin/0serve.js - checksum: 4403435191dec226e3c61aac994cd1f4dbb0c4988e2f600fb5a398d2f99ea85dc060db9756237d9084a936ffdbec141fc13e8e8e1c53db795fb9f7e7602b9c38 + checksum: 6a3c5c5c3f37f0940eff9309b2595f9a77822f521773db773e0d809309ccf5e6ecab8f39cc47b55b6b167f60b3824c44bb7d92b5c9ffb81a3f331b21277906d2 languageName: node linkType: hard @@ -26453,13 +26465,13 @@ __metadata: linkType: hard "regexp.prototype.flags@npm:^1.5.0": - version: 1.5.0 - resolution: "regexp.prototype.flags@npm:1.5.0" + version: 1.5.1 + resolution: "regexp.prototype.flags@npm:1.5.1" dependencies: call-bind: ^1.0.2 define-properties: ^1.2.0 - functions-have-names: ^1.2.3 - checksum: c541687cdbdfff1b9a07f6e44879f82c66bbf07665f9a7544c5fd16acdb3ec8d1436caab01662d2fbcad403f3499d49ab0b77fbc7ef29ef961d98cc4bc9755b4 + set-function-name: ^2.0.0 + checksum: 869edff00288442f8d7fa4c9327f91d85f3b3acf8cbbef9ea7a220345cf23e9241b6def9263d2c1ebcf3a316b0aa52ad26a43a84aa02baca3381717b3e307f47 languageName: node linkType: hard @@ -27610,6 +27622,17 @@ __metadata: languageName: node linkType: hard +"set-function-name@npm:^2.0.0": + version: 2.0.1 + resolution: "set-function-name@npm:2.0.1" + dependencies: + define-data-property: ^1.0.1 + functions-have-names: ^1.2.3 + has-property-descriptors: ^1.0.0 + checksum: 4975d17d90c40168eee2c7c9c59d023429f0a1690a89d75656306481ece0c3c1fb1ebcc0150ea546d1913e35fbd037bace91372c69e543e51fc5d1f31a9fa126 + languageName: node + linkType: hard + "set-value@npm:^2.0.0, set-value@npm:^2.0.1": version: 2.0.1 resolution: "set-value@npm:2.0.1" @@ -28396,8 +28419,8 @@ __metadata: linkType: hard "string.prototype.matchall@npm:^4.0.8": - version: 4.0.9 - resolution: "string.prototype.matchall@npm:4.0.9" + version: 4.0.10 + resolution: "string.prototype.matchall@npm:4.0.10" dependencies: call-bind: ^1.0.2 define-properties: ^1.2.0 @@ -28406,8 +28429,9 @@ __metadata: has-symbols: ^1.0.3 internal-slot: ^1.0.5 regexp.prototype.flags: ^1.5.0 + set-function-name: ^2.0.0 side-channel: ^1.0.4 - checksum: a68a9914755ec8c9b9129d6fb70603d78b839a1ca4a907e601fcb860109cecfbd1884e8b38989885f9c825c1c2015ff5b1ed9ddac7c8d040e4e4b3c9bc4ed5ed + checksum: 3c78bdeff39360c8e435d7c4c6ea19f454aa7a63eda95fa6fadc3a5b984446a2f9f2c02d5c94171ce22268a573524263fbd0c8edbe3ce2e9890d7cc036cdc3ed languageName: node linkType: hard @@ -29722,21 +29746,21 @@ __metadata: "typescript@patch:typescript@5.1.6#~builtin": version: 5.1.6 - resolution: "typescript@patch:typescript@npm%3A5.1.6#~builtin::version=5.1.6&hash=5da071" + resolution: "typescript@patch:typescript@npm%3A5.1.6#~builtin::version=5.1.6&hash=77c9e2" bin: tsc: bin/tsc tsserver: bin/tsserver - checksum: f53bfe97f7c8b2b6d23cf572750d4e7d1e0c5fff1c36d859d0ec84556a827b8785077bc27676bf7e71fae538e517c3ecc0f37e7f593be913d884805d931bc8be + checksum: 21e88b0a0c0226f9cb9fd25b9626fb05b4c0f3fddac521844a13e1f30beb8f14e90bd409a9ac43c812c5946d714d6e0dee12d5d02dfc1c562c5aacfa1f49b606 languageName: node linkType: hard "typescript@patch:typescript@^5.2.2#~builtin": version: 5.2.2 - resolution: "typescript@patch:typescript@npm%3A5.2.2#~builtin::version=5.2.2&hash=f3b441" + resolution: "typescript@patch:typescript@npm%3A5.2.2#~builtin::version=5.2.2&hash=77c9e2" bin: tsc: bin/tsc tsserver: bin/tsserver - checksum: 0f4da2f15e6f1245e49db15801dbee52f2bbfb267e1c39225afdab5afee1a72839cd86000e65ee9d7e4dfaff12239d28beaf5ee431357fcced15fb08583d72ca + checksum: 07106822b4305de3f22835cbba949a2b35451cad50888759b6818421290ff95d522b38ef7919e70fb381c5fe9c1c643d7dea22c8b31652a717ddbd57b7f4d554 languageName: node linkType: hard