@flexkit/studio 0.2.34 → 0.2.35

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 (100) hide show
  1. package/dist/chat-page-IR4RF46H.js +4 -0
  2. package/dist/chat-page-IR4RF46H.js.map +1 -0
  3. package/dist/{chunk-CHNQZU2P.js → chunk-7N3LTSVX.js} +2 -2
  4. package/dist/{chunk-CHNQZU2P.js.map → chunk-7N3LTSVX.js.map} +1 -1
  5. package/dist/chunk-BTH7HU5Y.js +2 -0
  6. package/dist/chunk-BTH7HU5Y.js.map +1 -0
  7. package/dist/chunk-DEG7QP5C.js +30 -0
  8. package/dist/chunk-DEG7QP5C.js.map +1 -0
  9. package/dist/{chunk-DFBG7PUJ.js → chunk-FJRDLP7V.js} +4 -4
  10. package/dist/{chunk-DFBG7PUJ.js.map → chunk-FJRDLP7V.js.map} +1 -1
  11. package/dist/{chunk-RBGXXVF4.js → chunk-J6EGNRRT.js} +3 -3
  12. package/dist/{chunk-RBGXXVF4.js.map → chunk-J6EGNRRT.js.map} +1 -1
  13. package/dist/chunk-KEGJNZLW.js +378 -0
  14. package/dist/chunk-KEGJNZLW.js.map +1 -0
  15. package/dist/chunk-PMLDA2FF.js +2 -0
  16. package/dist/chunk-PMLDA2FF.js.map +1 -0
  17. package/dist/{chunk-HR4QZ5TD.js → chunk-PWGLA33F.js} +3 -3
  18. package/dist/chunk-PWGLA33F.js.map +1 -0
  19. package/dist/chunk-PZWEC6KD.js +2 -0
  20. package/dist/chunk-PZWEC6KD.js.map +1 -0
  21. package/dist/{chunk-YC7PBLTK.js → chunk-Q4CWQHBJ.js} +2 -2
  22. package/dist/{chunk-YC7PBLTK.js.map → chunk-Q4CWQHBJ.js.map} +1 -1
  23. package/dist/{chunk-PSECMZJC.js → chunk-R6DPAMLC.js} +3 -3
  24. package/dist/{chunk-PSECMZJC.js.map → chunk-R6DPAMLC.js.map} +1 -1
  25. package/dist/{chunk-7JA6SI5H.js → chunk-RAUQCO6W.js} +2 -2
  26. package/dist/{chunk-7JA6SI5H.js.map → chunk-RAUQCO6W.js.map} +1 -1
  27. package/dist/{chunk-XYFS6X74.js → chunk-RFKMFD2O.js} +2 -2
  28. package/dist/{chunk-XYFS6X74.js.map → chunk-RFKMFD2O.js.map} +1 -1
  29. package/dist/{chunk-VRDTHLA3.js → chunk-TI3EHLRE.js} +3 -3
  30. package/dist/{chunk-VRDTHLA3.js.map → chunk-TI3EHLRE.js.map} +1 -1
  31. package/dist/chunk-W7JDZHCV.js +2 -0
  32. package/dist/chunk-W7JDZHCV.js.map +1 -0
  33. package/dist/chunk-XYE6ZAUB.js +2 -0
  34. package/dist/{chunk-3OBELKJU.js.map → chunk-XYE6ZAUB.js.map} +1 -1
  35. package/dist/chunk-ZMONKGWJ.js +4 -0
  36. package/dist/{chunk-2FCVATRT.js.map → chunk-ZMONKGWJ.js.map} +1 -1
  37. package/dist/{chunk-6AJRQSFO.js → chunk-ZRTWLY77.js} +3 -3
  38. package/dist/{chunk-6AJRQSFO.js.map → chunk-ZRTWLY77.js.map} +1 -1
  39. package/dist/entry-NQRN7SWV.js +2 -0
  40. package/dist/{entry-APJ4V7PW.js.map → entry-NQRN7SWV.js.map} +1 -1
  41. package/dist/index.d.ts +5 -1
  42. package/dist/index.js +7 -7
  43. package/dist/index.js.map +1 -1
  44. package/dist/{list-7IHDJM54.js → list-7SCWJXUE.js} +3 -3
  45. package/dist/{list-7IHDJM54.js.map → list-7SCWJXUE.js.map} +1 -1
  46. package/dist/list-RPIG75US.js +2 -0
  47. package/dist/{list-7WFALOUT.js.map → list-RPIG75US.js.map} +1 -1
  48. package/dist/{pages-BV3ABSFQ.js → pages-KXERA5Z6.js} +3 -3
  49. package/dist/{pages-BV3ABSFQ.js.map → pages-KXERA5Z6.js.map} +1 -1
  50. package/dist/{plugin-pages-OWGJPPWV.js → plugin-pages-NXA4BT7Y.js} +2 -2
  51. package/dist/{plugin-pages-OWGJPPWV.js.map → plugin-pages-NXA4BT7Y.js.map} +1 -1
  52. package/dist/{project-selector-V5N2724B.js → project-selector-SA6UUVBK.js} +2 -2
  53. package/dist/{project-selector-V5N2724B.js.map → project-selector-SA6UUVBK.js.map} +1 -1
  54. package/dist/record-links-RTMCYEPM.js +2 -0
  55. package/dist/{record-links-3P3XSW66.js.map → record-links-RTMCYEPM.js.map} +1 -1
  56. package/dist/{root-GBBZ3DX5.js → root-6H6K3YQF.js} +3 -3
  57. package/dist/{root-GBBZ3DX5.js.map → root-6H6K3YQF.js.map} +1 -1
  58. package/dist/root-IBZPU2D4.js +23 -0
  59. package/dist/{root-RGZZYETM.js.map → root-IBZPU2D4.js.map} +1 -1
  60. package/dist/{root-F2GTYDJM.js → root-MBFQXX4E.js} +2 -2
  61. package/dist/{root-F2GTYDJM.js.map → root-MBFQXX4E.js.map} +1 -1
  62. package/dist/root-TSZ3CSJU.js +14 -0
  63. package/dist/root-TSZ3CSJU.js.map +1 -0
  64. package/dist/{root-NIL2UHKS.js → root-TZIXWEZO.js} +2 -2
  65. package/dist/{root-NIL2UHKS.js.map → root-TZIXWEZO.js.map} +1 -1
  66. package/dist/{root-J62PBDZ2.js → root-W5ZPSNQ2.js} +2 -2
  67. package/dist/{root-J62PBDZ2.js.map → root-W5ZPSNQ2.js.map} +1 -1
  68. package/dist/{search-IIGNCQEZ.js → search-ZLEYFT3T.js} +2 -2
  69. package/dist/{search-IIGNCQEZ.js.map → search-ZLEYFT3T.js.map} +1 -1
  70. package/dist/{skills-pages-T52MF4DO.js → skills-pages-RPMWIBLS.js} +2 -2
  71. package/dist/{skills-pages-T52MF4DO.js.map → skills-pages-RPMWIBLS.js.map} +1 -1
  72. package/dist/source-VYWE5EB5.js +2 -0
  73. package/dist/{source-IJAAJDGW.js.map → source-VYWE5EB5.js.map} +1 -1
  74. package/package.json +3 -3
  75. package/dist/chat-page-7XT4K75G.js +0 -4
  76. package/dist/chat-page-7XT4K75G.js.map +0 -1
  77. package/dist/chunk-2FCVATRT.js +0 -4
  78. package/dist/chunk-3OBELKJU.js +0 -2
  79. package/dist/chunk-4PUMSUJL.js +0 -30
  80. package/dist/chunk-4PUMSUJL.js.map +0 -1
  81. package/dist/chunk-ERDARDGB.js +0 -2
  82. package/dist/chunk-ERDARDGB.js.map +0 -1
  83. package/dist/chunk-HR4QZ5TD.js.map +0 -1
  84. package/dist/chunk-KO2JLLUJ.js +0 -2
  85. package/dist/chunk-KO2JLLUJ.js.map +0 -1
  86. package/dist/chunk-PC5Z37LT.js +0 -378
  87. package/dist/chunk-PC5Z37LT.js.map +0 -1
  88. package/dist/chunk-U6NFYYTF.js +0 -2
  89. package/dist/chunk-U6NFYYTF.js.map +0 -1
  90. package/dist/chunk-XMOSCJOI.js +0 -2
  91. package/dist/chunk-XMOSCJOI.js.map +0 -1
  92. package/dist/chunk-YDVO6XWD.js +0 -2
  93. package/dist/chunk-YDVO6XWD.js.map +0 -1
  94. package/dist/entry-APJ4V7PW.js +0 -2
  95. package/dist/list-7WFALOUT.js +0 -2
  96. package/dist/record-links-3P3XSW66.js +0 -2
  97. package/dist/root-LYRL7VPN.js +0 -14
  98. package/dist/root-LYRL7VPN.js.map +0 -1
  99. package/dist/root-RGZZYETM.js +0 -23
  100. package/dist/source-IJAAJDGW.js +0 -2
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/apps/knowledge/record-links.tsx"],"names":["PICKER_PAGE_SIZE","DetachButton","disabled","label","onClick","jsxs","Tooltip","jsx","TooltipTrigger","Button","Unlink2Icon","TooltipContent","AttachDocumentDialog","api","attachedIds","onAttached","onOpenChange","open","projectId","target","targetLabel","search","setSearch","useState","deferredSearch","useDeferredValue","attachingId","setAttachingId","getPageKey","pageIndex","previousPage","knowledgePaths","pages","error","isLoading","setSize","size","useSWRInfinite","fetcher","candidates","useMemo","getAttachableSources","page","hasMore","isLoadingMore","attach","source","result","toast","attachError","describeApiError","body","Skeleton","status","summarizeKnowledgeStatus","isAttaching","SourceIcon","getKnowledgeKindLabel","LoaderCircle","StatusBadge","InfiniteScrollSentinel","current","Dialog","DialogContent","DialogHeader","DialogTitle","DialogDescription","SearchIcon","Input","event","SourceRows","renderAction","sources","Link","getKnowledgeSourcePath","EntityKnowledgeButton","entityName","useProjectKnowledge","currentProject","useConfig","isOpen","setIsOpen","entitySchema","findEntityByName","isLinkable","isKnowledgeLinkableEntity","data","useSWR","FetcherError","count","Fragment","getEntityLinkLabel","BookOpenTextIcon","Badge","EntityKnowledgeDialog","entityLabel","canMutate","useCanMutate","isPickerOpen","setIsPickerOpen","detachingId","setDetachingId","mutate","detach","entry","invalidateKnowledgeLinks","detachError","PermissionTooltip","PaperclipIcon","RecordKnowledgePanel","recordId","recordLabel","record","inherited","knowledgeApi","TooltipProvider","Collapsible","CollapsibleTrigger","ChevronRightIcon","CollapsibleContent","useRecordLabels","group","schema","displayAttribute","scope","useAppContext","ids","link","useEntityQuery","labels","item","value","LinkedRecordGroup","props","getEntitySchema","getDisplayAttribute","LinkedRecordRows","ResolvedLinkedRecordGroup","detachingKey","onDetach","dispatch","useDispatch","getKnowledgeLinkLabel","key","LinkedRecordsSection","setDetachingKey","links","entities","groups","groupKnowledgeLinks","detachEntity","attachEntity","entity","AppliesToEntities","busy","onAttach","getLinkableEntities","labelOf","LayersIcon","XIcon","Popover","PopoverTrigger","PlusIcon","PopoverContent","Command","CommandInput","CommandList","CommandEmpty","CommandGroup","CommandItem"],"mappings":"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","file":"chunk-YC7PBLTK.js","sourcesContent":["import type { JSX } from 'react';\nimport { useDeferredValue, useMemo, useState } from 'react';\nimport {\n BookOpenTextIcon,\n ChevronRightIcon,\n LayersIcon,\n LoaderCircle,\n PaperclipIcon,\n PlusIcon,\n SearchIcon,\n Unlink2Icon,\n XIcon,\n} from 'lucide-react';\nimport { Link } from 'react-router-dom';\nimport useSWR from 'swr';\nimport useSWRInfinite from 'swr/infinite';\nimport { useAppContext } from '../../core/app-context';\nimport { useConfig } from '../../core/config/config-context';\nimport { getDisplayAttribute } from '../../core/get-display-attribute';\nimport type { Attribute, Entity } from '../../core/types';\nimport { useCanMutate } from '../../core/permissions';\nimport { useDispatch } from '../../entities/actions-context';\nimport { getEntitySchema } from '../../graphql-client/queries';\nimport type { MappedEntityItem } from '../../graphql-client/types';\nimport { useEntityQuery } from '../../graphql-client/use-entity-query';\nimport {\n Badge,\n Button,\n Collapsible,\n CollapsibleContent,\n CollapsibleTrigger,\n Command,\n CommandEmpty,\n CommandGroup,\n CommandInput,\n CommandItem,\n CommandList,\n Dialog,\n DialogContent,\n DialogDescription,\n DialogHeader,\n DialogTitle,\n Input,\n PermissionTooltip,\n Popover,\n PopoverContent,\n PopoverTrigger,\n Skeleton,\n toast,\n Tooltip,\n TooltipContent,\n TooltipProvider,\n TooltipTrigger,\n} from '../../ui';\nimport { fetcher, FetcherError, knowledgePaths, type KnowledgeApi } from './api';\nimport {\n findEntityByName,\n getAttachableSources,\n getEntityLinkLabel,\n getKnowledgeKindLabel,\n getKnowledgeLinkLabel,\n getKnowledgeSourcePath,\n getLinkableEntities,\n groupKnowledgeLinks,\n isKnowledgeLinkableEntity,\n summarizeKnowledgeStatus,\n type KnowledgeEntityLink,\n type KnowledgeEntitySources,\n type KnowledgeLink,\n type KnowledgeLinkGroup,\n type KnowledgeLinksList,\n type KnowledgeLinkTarget,\n type KnowledgeRecordRef,\n type KnowledgeRecordSources,\n type KnowledgeSource,\n type KnowledgeSourcesList,\n} from './knowledge';\nimport {\n describeApiError,\n InfiniteScrollSentinel,\n invalidateKnowledgeLinks,\n SourceIcon,\n StatusBadge,\n useProjectKnowledge,\n} from './shared';\n\nconst PICKER_PAGE_SIZE = 50;\n\nfunction DetachButton({\n disabled,\n label,\n onClick,\n}: {\n disabled: boolean;\n label: string;\n onClick: () => void;\n}): JSX.Element {\n return (\n <Tooltip>\n <TooltipTrigger asChild>\n <Button\n aria-label={label}\n className=\"fk:size-7 fk:shrink-0\"\n disabled={disabled}\n size=\"icon\"\n variant=\"ghost\"\n onClick={onClick}\n >\n <Unlink2Icon className=\"fk:size-4\" />\n </Button>\n </TooltipTrigger>\n <TooltipContent>{label}</TooltipContent>\n </Tooltip>\n );\n}\n\n/**\n * The picker behind \"Attach document\": the project's documents, searched by\n * title, minus the ones already attached. Picking one links it right away.\n */\nfunction AttachDocumentDialog({\n api,\n attachedIds,\n onAttached,\n onOpenChange,\n open,\n projectId,\n target,\n targetLabel,\n}: {\n api: KnowledgeApi;\n attachedIds: string[];\n onAttached: (_source: KnowledgeSource) => void;\n onOpenChange: (_open: boolean) => void;\n open: boolean;\n projectId: string;\n /** A record, or an entity type when it names no record. */\n target: KnowledgeLinkTarget;\n /** How the target is called in the dialog copy: a record title, or an entity label. */\n targetLabel: string;\n}): JSX.Element {\n const [search, setSearch] = useState('');\n const deferredSearch = useDeferredValue(search.trim());\n const [attachingId, setAttachingId] = useState<string | null>(null);\n const getPageKey = (pageIndex: number, previousPage: KnowledgeSourcesList | null): string | null => {\n if (!open || (previousPage && !previousPage.hasMore)) {\n return null;\n }\n\n return knowledgePaths(projectId).sources({\n limit: PICKER_PAGE_SIZE,\n offset: pageIndex * PICKER_PAGE_SIZE,\n search: deferredSearch || undefined,\n });\n };\n // Paged like the document list: the attached ones are hidden client-side,\n // so a record with many documents attached must be able to reach the next page.\n const {\n data: pages,\n error,\n isLoading,\n setSize,\n size,\n } = useSWRInfinite<KnowledgeSourcesList>(getPageKey, fetcher, { keepPreviousData: true });\n const candidates = useMemo(\n () => getAttachableSources(pages?.flatMap((page) => page.sources) ?? [], attachedIds),\n [attachedIds, pages]\n );\n const hasMore = pages?.[pages.length - 1]?.hasMore ?? false;\n const isLoadingMore = pages !== undefined && size > pages.length;\n\n async function attach(source: KnowledgeSource): Promise<void> {\n setAttachingId(source.id);\n\n try {\n const result = await api.linkSource(source.id, target);\n\n toast.success(result.linked ? `Attached \"${source.title}\".` : `\"${source.title}\" was already attached.`);\n onAttached(source);\n onOpenChange(false);\n } catch (attachError) {\n toast.error(describeApiError(attachError, 'The document could not be attached.'));\n } finally {\n setAttachingId(null);\n }\n }\n\n let body: JSX.Element;\n\n if (error) {\n body = <p className=\"fk:py-6 fk:text-center fk:text-sm fk:text-destructive\">The documents could not be loaded.</p>;\n } else if (isLoading && !pages) {\n body = (\n <div aria-busy=\"true\" className=\"fk:flex fk:flex-col fk:gap-3 fk:py-2\">\n <Skeleton className=\"fk:h-10 fk:w-full\" />\n <Skeleton className=\"fk:h-10 fk:w-full\" />\n <Skeleton className=\"fk:h-10 fk:w-full\" />\n </div>\n );\n } else if (candidates.length === 0 && !hasMore) {\n body = (\n <p className=\"fk:py-6 fk:text-center fk:text-sm fk:text-muted-foreground\">\n {deferredSearch\n ? 'No documents match your search.'\n : 'Every document is attached already, or there are none yet. Add documents in Knowledge first.'}\n </p>\n );\n } else {\n body = (\n <ul className=\"fk:flex fk:max-h-80 fk:flex-col fk:overflow-y-auto fk:-mx-2\">\n {candidates.map((source) => {\n const status = summarizeKnowledgeStatus(source);\n const isAttaching = attachingId === source.id;\n\n return (\n <li key={source.id}>\n <button\n className=\"fk:flex fk:w-full fk:min-w-0 fk:items-center fk:gap-3 fk:rounded-lg fk:px-2 fk:py-2 fk:text-left fk:transition-colors fk:hover:bg-accent fk:focus-visible:bg-accent fk:focus-visible:outline-none fk:disabled:opacity-60 fk:corner-squircle\"\n disabled={attachingId !== null}\n type=\"button\"\n onClick={() => void attach(source)}\n >\n <SourceIcon source={source} />\n <span className=\"fk:min-w-0 fk:flex-1\">\n <span className=\"fk:block fk:truncate fk:text-sm fk:font-medium\">{source.title}</span>\n <span className=\"fk:block fk:truncate fk:text-xs fk:text-muted-foreground\">\n {getKnowledgeKindLabel(source)} · {status.detail}\n </span>\n </span>\n {isAttaching ? (\n <LoaderCircle className=\"fk:size-4 fk:shrink-0 fk:animate-spin fk:text-muted-foreground\" />\n ) : (\n <StatusBadge label={status.label} tone={status.tone} />\n )}\n </button>\n </li>\n );\n })}\n {hasMore && !isLoadingMore ? (\n <li aria-hidden>\n <InfiniteScrollSentinel onVisible={() => void setSize((current) => current + 1)} />\n </li>\n ) : null}\n {isLoadingMore ? (\n <li className=\"fk:flex fk:justify-center fk:py-2\">\n <LoaderCircle className=\"fk:size-4 fk:animate-spin fk:text-muted-foreground\" />\n </li>\n ) : null}\n </ul>\n );\n }\n\n return (\n <Dialog open={open} onOpenChange={onOpenChange}>\n <DialogContent className=\"fk:sm:max-w-lg\">\n <DialogHeader>\n <DialogTitle>Attach a document</DialogTitle>\n <DialogDescription>\n {target.recordId === undefined\n ? `Documents attached to every ${targetLabel} are searched before the rest of Knowledge when a question is about one of them, or about ${targetLabel} in general.`\n : `Documents attached to ${targetLabel} are searched before the rest of Knowledge when a question is about it.`}\n </DialogDescription>\n </DialogHeader>\n <div className=\"fk:relative\">\n <SearchIcon className=\"fk:pointer-events-none fk:absolute fk:left-2.5 fk:top-1/2 fk:size-4 fk:-translate-y-1/2 fk:text-muted-foreground\" />\n <Input\n aria-label=\"Search documents\"\n autoFocus\n className=\"fk:pl-8\"\n placeholder=\"Search documents by title\"\n value={search}\n onChange={(event) => setSearch(event.target.value)}\n />\n </div>\n {body}\n </DialogContent>\n </Dialog>\n );\n}\n\n/** Document rows shared by the record panel and the entity dialog: icon, title, kind and status, plus an optional action. */\nfunction SourceRows({\n projectId,\n renderAction,\n sources,\n}: {\n projectId: string;\n renderAction?: (_source: KnowledgeSource) => JSX.Element | null;\n sources: KnowledgeSource[];\n}): JSX.Element {\n return (\n <ul className=\"fk:flex fk:flex-col fk:gap-1 fk:-mx-2\">\n {sources.map((source) => {\n const status = summarizeKnowledgeStatus(source);\n\n return (\n <li\n className=\"fk:flex fk:min-w-0 fk:items-center fk:gap-2 fk:rounded-lg fk:px-2 fk:py-1.5 fk:corner-squircle\"\n key={source.id}\n >\n <Link\n className=\"fk:flex fk:min-w-0 fk:flex-1 fk:items-center fk:gap-3 fk:rounded-md fk:hover:underline fk:underline-offset-2\"\n to={getKnowledgeSourcePath(projectId, source.id)}\n >\n <SourceIcon source={source} />\n <span className=\"fk:min-w-0 fk:flex-1\">\n <span className=\"fk:block fk:truncate fk:text-sm fk:font-medium\">{source.title}</span>\n <span className=\"fk:block fk:truncate fk:text-xs fk:text-muted-foreground\">\n {getKnowledgeKindLabel(source)} · {status.detail}\n </span>\n </span>\n </Link>\n <StatusBadge label={status.label} tone={status.tone} />\n {renderAction?.(source)}\n </li>\n );\n })}\n </ul>\n );\n}\n\n/**\n * The documents attached to every record of an entity type, behind a button\n * in the Desk list toolbar: open them, attach another, detach one. Only the\n * project's own entities qualify, and the button stays out while the schema\n * is unknown.\n */\nexport function EntityKnowledgeButton({ entityName }: { entityName: string }): JSX.Element | null {\n const { api, projectId } = useProjectKnowledge();\n const { currentProject } = useConfig();\n const [isOpen, setIsOpen] = useState(false);\n const entitySchema = findEntityByName(currentProject?.schema ?? [], entityName);\n const isLinkable = Boolean(entitySchema) && isKnowledgeLinkableEntity(entityName);\n const { data, error } = useSWR<KnowledgeEntitySources>(\n projectId && isLinkable ? knowledgePaths(projectId).entitySources(entityName) : null,\n fetcher\n );\n\n if (!api || !projectId || !entitySchema || !isLinkable || (error instanceof FetcherError && error.status === 404)) {\n return null;\n }\n\n const count = data?.sources.length ?? 0;\n\n return (\n <>\n <Button\n aria-label={`Knowledge for every ${getEntityLinkLabel(entitySchema)}`}\n className=\"fk:mr-2 fk:h-8\"\n data-knowledge-entity-button\n size=\"sm\"\n variant=\"outline\"\n onClick={() => setIsOpen(true)}\n >\n <BookOpenTextIcon />\n Knowledge\n {count > 0 ? (\n <Badge className=\"fk:corner-squircle\" variant=\"secondary\">\n {count}\n </Badge>\n ) : null}\n </Button>\n <EntityKnowledgeDialog\n api={api}\n entityLabel={getEntityLinkLabel(entitySchema)}\n entityName={entityName}\n open={isOpen}\n projectId={projectId}\n onOpenChange={setIsOpen}\n />\n </>\n );\n}\n\nfunction EntityKnowledgeDialog({\n api,\n entityLabel,\n entityName,\n onOpenChange,\n open,\n projectId,\n}: {\n api: KnowledgeApi;\n entityLabel: string;\n entityName: string;\n onOpenChange: (_open: boolean) => void;\n open: boolean;\n projectId: string;\n}): JSX.Element {\n const canMutate = useCanMutate();\n const [isPickerOpen, setIsPickerOpen] = useState(false);\n const [detachingId, setDetachingId] = useState<string | null>(null);\n const { data, error, isLoading, mutate } = useSWR<KnowledgeEntitySources>(\n knowledgePaths(projectId).entitySources(entityName),\n fetcher\n );\n const sources = data?.sources ?? [];\n const target = useMemo<KnowledgeLinkTarget>(() => ({ entity: entityName }), [entityName]);\n\n async function detach(source: KnowledgeSource): Promise<void> {\n setDetachingId(source.id);\n\n try {\n await api.unlinkSource(source.id, target);\n toast.success(`\"${source.title}\" no longer applies to every ${entityLabel}.`);\n await mutate({ sources: sources.filter((entry) => entry.id !== source.id) }, { revalidate: false });\n void invalidateKnowledgeLinks();\n } catch (detachError) {\n toast.error(describeApiError(detachError, 'The document could not be detached.'));\n } finally {\n setDetachingId(null);\n }\n }\n\n let body: JSX.Element;\n\n if (error) {\n body = <p className=\"fk:text-sm fk:text-destructive\">The documents could not be loaded.</p>;\n } else if (isLoading && !data) {\n body = (\n <div aria-busy=\"true\" className=\"fk:flex fk:flex-col fk:gap-2\">\n <Skeleton className=\"fk:h-9 fk:w-full\" />\n <Skeleton className=\"fk:h-9 fk:w-2/3\" />\n </div>\n );\n } else if (sources.length === 0) {\n body = (\n <p className=\"fk:text-sm fk:text-muted-foreground\">\n No documents apply to every {entityLabel} yet. Attach a guide, a policy or a spec that holds for all of them and\n the agent searches it first when asked about any {entityLabel}, or about {entityLabel} in general.\n </p>\n );\n } else {\n body = (\n <SourceRows\n projectId={projectId}\n sources={sources}\n renderAction={(source) =>\n canMutate ? (\n <DetachButton\n disabled={detachingId !== null}\n label={`Detach ${source.title}`}\n onClick={() => void detach(source)}\n />\n ) : null\n }\n />\n );\n }\n\n return (\n <Dialog open={open} onOpenChange={onOpenChange}>\n <DialogContent className=\"fk:sm:max-w-lg\" data-knowledge-entity-dialog>\n <DialogHeader>\n <DialogTitle className=\"fk:flex fk:items-center fk:gap-2\">\n <BookOpenTextIcon className=\"fk:size-4 fk:text-muted-foreground\" />\n Knowledge for every {entityLabel}\n {data ? (\n <Badge className=\"fk:corner-squircle\" variant=\"outline\">\n {sources.length}\n </Badge>\n ) : null}\n </DialogTitle>\n <DialogDescription>\n Documents attached here apply to every {entityLabel}; each record can hold its own on top.\n </DialogDescription>\n </DialogHeader>\n <div className=\"fk:max-h-80 fk:overflow-y-auto\">{body}</div>\n <div className=\"fk:flex fk:justify-end\">\n <PermissionTooltip disabled={!canMutate}>\n <Button disabled={!canMutate} size=\"sm\" variant=\"outline\" onClick={() => setIsPickerOpen(true)}>\n <PaperclipIcon />\n Attach document\n </Button>\n </PermissionTooltip>\n </div>\n <AttachDocumentDialog\n api={api}\n attachedIds={sources.map((source) => source.id)}\n open={isPickerOpen}\n projectId={projectId}\n target={target}\n targetLabel={entityLabel}\n onAttached={(source) => {\n // \"Already attached\" (a stale picker in another tab) must not list the document twice.\n void mutate(\n { sources: sources.some((entry) => entry.id === source.id) ? sources : [...sources, source] },\n { revalidate: false }\n );\n void invalidateKnowledgeLinks();\n }}\n onOpenChange={setIsPickerOpen}\n />\n </DialogContent>\n </Dialog>\n );\n}\n\n/**\n * The documents attached to one record, pinned under the record form in the\n * Desk as a collapsible panel: open them, attach another, detach one. The\n * agent searches these before the rest of Knowledge when a question is about this record.\n */\nexport function RecordKnowledgePanel({\n entityName,\n recordId,\n recordLabel,\n}: {\n entityName: string;\n recordId: string;\n recordLabel: string;\n}): JSX.Element | null {\n const { api, projectId } = useProjectKnowledge();\n const { currentProject } = useConfig();\n const canMutate = useCanMutate();\n const [isOpen, setIsOpen] = useState(false);\n const [isPickerOpen, setIsPickerOpen] = useState(false);\n const [detachingId, setDetachingId] = useState<string | null>(null);\n const record = useMemo<KnowledgeRecordRef>(() => ({ entity: entityName, recordId }), [entityName, recordId]);\n const entitySchema = findEntityByName(currentProject?.schema ?? [], entityName);\n const entityLabel = entitySchema ? getEntityLinkLabel(entitySchema) : entityName;\n // Built-in entities (assets, tags) are not something a document is about, so they are not even asked.\n const isLinkable = isKnowledgeLinkableEntity(entityName);\n const { data, error, isLoading, mutate } = useSWR<KnowledgeRecordSources>(\n projectId && isLinkable ? knowledgePaths(projectId).recordSources(record) : null,\n fetcher\n );\n\n if (!api || !projectId || !isLinkable) {\n return null;\n }\n\n // The platform answers 404 for an entity it does not know, which can\n // happen while a schema change is still deploying; the panel then stays out of the way.\n if (error instanceof FetcherError && error.status === 404) {\n return null;\n }\n\n const sources = data?.sources ?? [];\n const inherited = data?.inherited ?? [];\n const attachedIds = sources.map((source) => source.id);\n const knowledgeApi = api;\n\n async function detach(source: KnowledgeSource): Promise<void> {\n setDetachingId(source.id);\n\n try {\n await knowledgeApi.unlinkSource(source.id, record);\n toast.success(`Detached \"${source.title}\".`);\n // The global invalidation below revalidates this key too; one refetch is enough.\n await mutate({ sources: sources.filter((entry) => entry.id !== source.id), inherited }, { revalidate: false });\n void invalidateKnowledgeLinks();\n } catch (detachError) {\n toast.error(describeApiError(detachError, 'The document could not be detached.'));\n } finally {\n setDetachingId(null);\n }\n }\n\n let body: JSX.Element;\n\n if (error) {\n body = <p className=\"fk:text-sm fk:text-destructive\">The attached documents could not be loaded.</p>;\n } else if (isLoading && !data) {\n body = (\n <div aria-busy=\"true\" className=\"fk:flex fk:flex-col fk:gap-2\">\n <Skeleton className=\"fk:h-9 fk:w-full\" />\n <Skeleton className=\"fk:h-9 fk:w-2/3\" />\n </div>\n );\n } else {\n body = (\n <div className=\"fk:flex fk:flex-col fk:gap-4\">\n {sources.length === 0 ? (\n <p className=\"fk:text-sm fk:text-muted-foreground\">\n {inherited.length > 0\n ? 'No documents attached to this record itself; the ones below apply to every record of its type.'\n : \"No documents attached. Attach a manual, spec or contract and the agent will use it alongside the record's data when asked about this record.\"}\n </p>\n ) : (\n <SourceRows\n projectId={projectId}\n sources={sources}\n renderAction={(source) =>\n canMutate ? (\n <DetachButton\n disabled={detachingId !== null}\n label={`Detach ${source.title}`}\n onClick={() => void detach(source)}\n />\n ) : null\n }\n />\n )}\n {inherited.length > 0 ? (\n <div className=\"fk:flex fk:flex-col fk:gap-1\" data-knowledge-inherited>\n <span className=\"fk:text-xs fk:font-medium fk:uppercase fk:tracking-wide fk:text-muted-foreground\">\n Applies to every {entityLabel}\n </span>\n <SourceRows projectId={projectId} sources={inherited} />\n </div>\n ) : null}\n </div>\n );\n }\n\n // The record drawer is mounted by the actions manager outside any app, so\n // it carries no tooltip provider of its own. The panel is a flex child of\n // the drawer: collapsed it is one row, open it claims half the height and\n // scrolls inside, so the form above stays visible and scrollable.\n return (\n <TooltipProvider>\n <Collapsible\n className={`fk:z-10 fk:flex fk:shrink-0 fk:flex-col fk:border-t fk:bg-background ${isOpen ? 'fk:h-1/2' : ''}`}\n data-knowledge-record-panel\n open={isOpen}\n onOpenChange={setIsOpen}\n >\n <div className=\"fk:flex fk:shrink-0 fk:items-center fk:gap-2 fk:pl-4 fk:pr-10 fk:py-2\">\n <CollapsibleTrigger asChild>\n <button\n className=\"fk:flex fk:min-w-0 fk:flex-1 fk:items-center fk:gap-2 fk:rounded-md fk:py-1 fk:text-left fk:text-sm fk:font-semibold fk:tracking-tight fk:hover:text-foreground/80 fk:focus-visible:outline-none fk:focus-visible:ring-2 fk:focus-visible:ring-ring\"\n type=\"button\"\n >\n <ChevronRightIcon\n className={`fk:size-4 fk:shrink-0 fk:text-muted-foreground fk:transition-transform ${isOpen ? 'fk:rotate-90' : ''}`}\n />\n <BookOpenTextIcon className=\"fk:size-4 fk:shrink-0 fk:text-muted-foreground\" />\n Knowledge\n {data ? (\n <Badge className=\"fk:corner-squircle\" variant=\"outline\">\n {sources.length + inherited.length}\n </Badge>\n ) : null}\n </button>\n </CollapsibleTrigger>\n {isOpen ? (\n <PermissionTooltip disabled={!canMutate}>\n <Button disabled={!canMutate} size=\"sm\" variant=\"outline\" onClick={() => setIsPickerOpen(true)}>\n <PaperclipIcon />\n Attach document\n </Button>\n </PermissionTooltip>\n ) : null}\n </div>\n <CollapsibleContent className=\"fk:min-h-0 fk:flex-1 fk:overflow-y-auto fk:px-10 fk:pb-6\">\n {body}\n </CollapsibleContent>\n <AttachDocumentDialog\n api={api}\n attachedIds={attachedIds}\n open={isPickerOpen}\n projectId={projectId}\n target={record}\n targetLabel={recordLabel || `this ${entityName}`}\n onAttached={(source) => {\n // A document that applied to the whole type now hangs on the record itself; one that\n // was already attached (a stale picker in another tab) is not listed twice.\n void mutate(\n {\n sources: sources.some((entry) => entry.id === source.id) ? sources : [...sources, source],\n inherited: inherited.filter((entry) => entry.id !== source.id),\n },\n { revalidate: false }\n );\n void invalidateKnowledgeLinks();\n }}\n onOpenChange={setIsPickerOpen}\n />\n </Collapsible>\n </TooltipProvider>\n );\n}\n\ntype RecordLabels = { [recordId: string]: string };\n\n/**\n * Resolves the labels of one entity's linked records with a single display\n * query. The platform only names records whose display attribute it can read\n * from the node; a space-scoped one lives on other nodes, so the Studio asks\n * the data itself, the way the Desk grid does. Only called for a deployed entity.\n */\nfunction useRecordLabels(group: KnowledgeLinkGroup, schema: Entity[], displayAttribute: Attribute): RecordLabels {\n const { scope } = useAppContext();\n const ids = group.links.map((link) => link.recordId);\n const { data } = useEntityQuery({\n entityNamePlural: group.entityPlural,\n schema,\n scope,\n selection: 'display',\n includeCount: false,\n variables: { where: { _id: { in: ids } }, limit: ids.length },\n });\n\n return useMemo(() => {\n const labels: RecordLabels = {};\n\n for (const item of (data ?? []) as MappedEntityItem[]) {\n const value = item[displayAttribute.name];\n\n if (typeof value === 'string' && value.trim()) {\n labels[item._id] = value;\n }\n }\n\n return labels;\n }, [data, displayAttribute]);\n}\n\ninterface LinkedRecordGroupProps {\n canMutate: boolean;\n detachingKey: string | null;\n group: KnowledgeLinkGroup;\n onDetach: (_link: KnowledgeLink) => void;\n}\n\nfunction LinkedRecordGroup(props: LinkedRecordGroupProps): JSX.Element {\n const { currentProject } = useConfig();\n const schema = useMemo(() => currentProject?.schema ?? [], [currentProject]);\n const entitySchema = getEntitySchema(schema, props.group.entityPlural);\n const displayAttribute = getDisplayAttribute(entitySchema);\n\n // The query builder throws for an entity it does not know or that has no\n // attributes yet, which can happen between a deploy and the next schema\n // load; such a group shows what the platform knew.\n if (!entitySchema || !displayAttribute) {\n return (\n <LinkedRecordRows\n {...props}\n entityLabel={entitySchema ? getEntityLinkLabel(entitySchema) : props.group.entityPlural}\n labels={{}}\n />\n );\n }\n\n return (\n <ResolvedLinkedRecordGroup\n {...props}\n displayAttribute={displayAttribute}\n schema={schema}\n entityLabel={getEntityLinkLabel(entitySchema)}\n />\n );\n}\n\nfunction ResolvedLinkedRecordGroup({\n displayAttribute,\n entityLabel,\n schema,\n ...props\n}: LinkedRecordGroupProps & { displayAttribute: Attribute; entityLabel: string; schema: Entity[] }): JSX.Element {\n const labels = useRecordLabels(props.group, schema, displayAttribute);\n\n return <LinkedRecordRows {...props} entityLabel={entityLabel} labels={labels} />;\n}\n\nfunction LinkedRecordRows({\n canMutate,\n detachingKey,\n entityLabel,\n group,\n labels,\n onDetach,\n}: LinkedRecordGroupProps & { entityLabel: string; labels: RecordLabels }): JSX.Element {\n const dispatch = useDispatch();\n\n return (\n <div className=\"fk:flex fk:flex-col fk:gap-1\">\n <span className=\"fk:text-xs fk:font-medium fk:uppercase fk:tracking-wide fk:text-muted-foreground\">\n {entityLabel}\n </span>\n <ul className=\"fk:flex fk:flex-col fk:-mx-2\">\n {group.links.map((link) => {\n const label = getKnowledgeLinkLabel(link, labels[link.recordId]);\n const key = `${link.entity}:${link.recordId}`;\n\n return (\n <li\n className=\"fk:flex fk:min-w-0 fk:items-center fk:gap-2 fk:rounded-lg fk:px-2 fk:py-1 fk:corner-squircle\"\n key={key}\n >\n <button\n className=\"fk:min-w-0 fk:flex-1 fk:truncate fk:text-left fk:text-sm fk:hover:underline fk:underline-offset-2\"\n title={link.recordId}\n type=\"button\"\n onClick={() =>\n dispatch({\n type: 'EditEntity',\n payload: { entityId: link.recordId, entityNamePlural: group.entityPlural },\n })\n }\n >\n {label}\n </button>\n {canMutate ? (\n <DetachButton\n disabled={detachingKey !== null}\n label={`Detach from ${label}`}\n onClick={() => onDetach(link)}\n />\n ) : null}\n </li>\n );\n })}\n </ul>\n </div>\n );\n}\n\n/**\n * The records a document is attached to, on the document page. Attaching\n * happens from the record's side in the Desk, where the record is in view.\n */\nexport function LinkedRecordsSection({\n api,\n projectId,\n source,\n}: {\n api: KnowledgeApi;\n projectId: string;\n source: KnowledgeSource;\n}): JSX.Element {\n const canMutate = useCanMutate();\n const [detachingKey, setDetachingKey] = useState<string | null>(null);\n const { data, error, isLoading, mutate } = useSWR<KnowledgeLinksList>(\n knowledgePaths(projectId).links(source.id),\n fetcher\n );\n const links = useMemo(() => data?.links ?? [], [data]);\n const entities = useMemo(() => data?.entities ?? [], [data]);\n const groups = useMemo(() => groupKnowledgeLinks(links), [links]);\n\n async function detach(link: KnowledgeLink): Promise<void> {\n setDetachingKey(`${link.entity}:${link.recordId}`);\n\n try {\n await api.unlinkSource(source.id, { entity: link.entity, recordId: link.recordId });\n toast.success('Detached.');\n await mutate(\n {\n links: links.filter((entry) => entry.entity !== link.entity || entry.recordId !== link.recordId),\n entities,\n },\n { revalidate: false }\n );\n void invalidateKnowledgeLinks();\n } catch (detachError) {\n toast.error(describeApiError(detachError, 'The document could not be detached.'));\n } finally {\n setDetachingKey(null);\n }\n }\n\n async function detachEntity(link: KnowledgeEntityLink): Promise<void> {\n setDetachingKey(`${link.entity}:*`);\n\n try {\n await api.unlinkSource(source.id, { entity: link.entity });\n toast.success('Detached.');\n await mutate(\n { links, entities: entities.filter((entry) => entry.entity !== link.entity) },\n { revalidate: false }\n );\n void invalidateKnowledgeLinks();\n } catch (detachError) {\n toast.error(describeApiError(detachError, 'The document could not be detached.'));\n } finally {\n setDetachingKey(null);\n }\n }\n\n async function attachEntity(entity: Entity): Promise<void> {\n setDetachingKey(`${entity.name}:+`);\n\n try {\n const result = await api.linkSource(source.id, { entity: entity.name });\n\n toast.success(\n result.linked\n ? `Applies to every ${getEntityLinkLabel(entity)} now.`\n : `Already applied to every ${getEntityLinkLabel(entity)}.`\n );\n await mutate(\n {\n links,\n entities: entities.some((entry) => entry.entity === entity.name)\n ? entities\n : [...entities, { entity: entity.name, entityPlural: entity.plural }],\n },\n { revalidate: false }\n );\n void invalidateKnowledgeLinks();\n } catch (attachError) {\n toast.error(describeApiError(attachError, 'The document could not be attached.'));\n } finally {\n setDetachingKey(null);\n }\n }\n\n let body: JSX.Element;\n\n if (error) {\n body = <p className=\"fk:text-sm fk:text-destructive\">The linked records could not be loaded.</p>;\n } else if (isLoading && !data) {\n body = (\n <div aria-busy=\"true\" className=\"fk:flex fk:flex-col fk:gap-2\">\n <Skeleton className=\"fk:h-6 fk:w-1/2\" />\n <Skeleton className=\"fk:h-6 fk:w-1/3\" />\n </div>\n );\n } else if (groups.length === 0) {\n body = (\n <p className=\"fk:text-sm fk:text-muted-foreground\">\n Not attached to any record. Open a record in the Desk and use \"Attach document\" so the agent searches this\n document before the rest of Knowledge when asked about that record.\n </p>\n );\n } else {\n body = (\n <div className=\"fk:flex fk:flex-col fk:gap-4\">\n {groups.map((group) => (\n <LinkedRecordGroup\n canMutate={canMutate}\n detachingKey={detachingKey}\n group={group}\n key={group.entityPlural}\n onDetach={(link) => void detach(link)}\n />\n ))}\n </div>\n );\n }\n\n return (\n <>\n <AppliesToEntities\n busy={detachingKey !== null}\n canMutate={canMutate}\n entities={entities}\n isLoading={isLoading && !data}\n onAttach={(entity) => void attachEntity(entity)}\n onDetach={(link) => void detachEntity(link)}\n />\n <div className=\"fk:mt-2 fk:flex fk:items-center fk:gap-2\">\n <PaperclipIcon className=\"fk:size-4 fk:text-muted-foreground\" />\n <h3 className=\"fk:text-sm fk:font-semibold fk:tracking-tight\">Linked records</h3>\n {links.length > 0 ? (\n <Badge className=\"fk:corner-squircle\" variant=\"outline\">\n {links.length}\n </Badge>\n ) : null}\n </div>\n <p className=\"fk:text-sm fk:text-muted-foreground\">\n When a question is about one of these records, the agent searches this document before the rest.\n </p>\n {body}\n </>\n );\n}\n\n/**\n * The entity types a document applies to, on the document page: chips for\n * the current ones, and a picker over the project's remaining entities. A\n * document attached to a type is searched first for any record of it.\n */\nfunction AppliesToEntities({\n busy,\n canMutate,\n entities,\n isLoading,\n onAttach,\n onDetach,\n}: {\n /** An attach or detach is in flight: no second one until it answers. */\n busy: boolean;\n canMutate: boolean;\n entities: KnowledgeEntityLink[];\n isLoading: boolean;\n onAttach: (_entity: Entity) => void;\n onDetach: (_link: KnowledgeEntityLink) => void;\n}): JSX.Element {\n const { currentProject } = useConfig();\n const schema = useMemo(() => currentProject?.schema ?? [], [currentProject]);\n const [isPickerOpen, setIsPickerOpen] = useState(false);\n const candidates = useMemo(() => getLinkableEntities(schema, entities), [entities, schema]);\n const labelOf = (link: KnowledgeEntityLink): string => {\n const entity = findEntityByName(schema, link.entity);\n\n return entity ? getEntityLinkLabel(entity) : link.entityPlural;\n };\n\n return (\n <div className=\"fk:flex fk:flex-col fk:gap-3\" data-knowledge-applies-to>\n <div className=\"fk:flex fk:items-center fk:gap-2\">\n <LayersIcon className=\"fk:size-4 fk:text-muted-foreground\" />\n <h3 className=\"fk:text-sm fk:font-semibold fk:tracking-tight\">Applies to</h3>\n {entities.length > 0 ? (\n <Badge className=\"fk:corner-squircle\" variant=\"outline\">\n {entities.length}\n </Badge>\n ) : null}\n </div>\n <p className=\"fk:text-sm fk:text-muted-foreground\">\n Attach this document to a whole entity type and the agent searches it before the rest of Knowledge for every\n record of that type, and for questions about the type in general.\n </p>\n {isLoading ? (\n <Skeleton className=\"fk:h-7 fk:w-1/3\" />\n ) : (\n <div className=\"fk:flex fk:flex-wrap fk:items-center fk:gap-2\">\n {entities.map((link) => {\n const label = labelOf(link);\n\n return (\n <Badge\n className=\"fk:gap-1 fk:py-1 fk:pl-2.5 fk:pr-1 fk:corner-squircle\"\n key={link.entity}\n variant=\"secondary\"\n >\n <span className=\"fk:capitalize\">{label}</span>\n {canMutate ? (\n <Tooltip>\n <TooltipTrigger asChild>\n <button\n aria-label={`No longer apply to every ${label}`}\n className=\"fk:rounded-sm fk:p-0.5 fk:text-muted-foreground fk:hover:text-foreground fk:focus-visible:outline-none fk:focus-visible:ring-2 fk:focus-visible:ring-ring fk:disabled:opacity-50\"\n disabled={busy}\n type=\"button\"\n onClick={() => onDetach(link)}\n >\n <XIcon className=\"fk:size-3\" />\n </button>\n </TooltipTrigger>\n <TooltipContent>No longer apply to every {label}</TooltipContent>\n </Tooltip>\n ) : null}\n </Badge>\n );\n })}\n <Popover open={isPickerOpen} onOpenChange={setIsPickerOpen}>\n <PermissionTooltip disabled={!canMutate}>\n <PopoverTrigger asChild>\n <Button className=\"fk:h-7\" disabled={!canMutate || candidates.length === 0} size=\"sm\" variant=\"outline\">\n <PlusIcon />\n Add entity type\n </Button>\n </PopoverTrigger>\n </PermissionTooltip>\n <PopoverContent align=\"start\" className=\"fk:w-64 fk:p-0\">\n <Command>\n <CommandInput placeholder=\"Search entity types\" />\n <CommandList>\n <CommandEmpty>No entity type matches.</CommandEmpty>\n <CommandGroup>\n {candidates.map((entity) => (\n <CommandItem\n key={entity.name}\n keywords={[getEntityLinkLabel(entity), entity.name, entity.plural]}\n value={entity.name}\n onSelect={() => {\n setIsPickerOpen(false);\n onAttach(entity);\n }}\n >\n <span className=\"fk:capitalize\">{getEntityLinkLabel(entity)}</span>\n </CommandItem>\n ))}\n </CommandGroup>\n </CommandList>\n </Command>\n </PopoverContent>\n </Popover>\n </div>\n )}\n </div>\n );\n}\n"]}
1
+ {"version":3,"sources":["../src/apps/knowledge/record-links.tsx"],"names":["PICKER_PAGE_SIZE","DetachButton","disabled","label","onClick","jsxs","Tooltip","jsx","TooltipTrigger","Button","Unlink2Icon","TooltipContent","AttachDocumentDialog","api","attachedIds","onAttached","onOpenChange","open","projectId","target","targetLabel","search","setSearch","useState","deferredSearch","useDeferredValue","attachingId","setAttachingId","getPageKey","pageIndex","previousPage","knowledgePaths","pages","error","isLoading","setSize","size","useSWRInfinite","fetcher","candidates","useMemo","getAttachableSources","page","hasMore","isLoadingMore","attach","source","result","toast","attachError","describeApiError","body","Skeleton","status","summarizeKnowledgeStatus","isAttaching","SourceIcon","getKnowledgeKindLabel","LoaderCircle","StatusBadge","InfiniteScrollSentinel","current","Dialog","DialogContent","DialogHeader","DialogTitle","DialogDescription","SearchIcon","Input","event","SourceRows","renderAction","sources","Link","getKnowledgeSourcePath","EntityKnowledgeButton","entityName","useProjectKnowledge","currentProject","useConfig","isOpen","setIsOpen","entitySchema","findEntityByName","isLinkable","isKnowledgeLinkableEntity","data","useSWR","FetcherError","count","Fragment","getEntityLinkLabel","BookOpenTextIcon","Badge","EntityKnowledgeDialog","entityLabel","canMutate","useCanMutate","isPickerOpen","setIsPickerOpen","detachingId","setDetachingId","mutate","detach","entry","invalidateKnowledgeLinks","detachError","PermissionTooltip","PaperclipIcon","RecordKnowledgePanel","recordId","recordLabel","record","inherited","knowledgeApi","TooltipProvider","Collapsible","CollapsibleTrigger","ChevronRightIcon","CollapsibleContent","useRecordLabels","group","schema","displayAttribute","scope","useAppContext","ids","link","useEntityQuery","labels","item","value","LinkedRecordGroup","props","getEntitySchema","getDisplayAttribute","LinkedRecordRows","ResolvedLinkedRecordGroup","detachingKey","onDetach","dispatch","useDispatch","getKnowledgeLinkLabel","key","LinkedRecordsSection","setDetachingKey","links","entities","groups","groupKnowledgeLinks","detachEntity","attachEntity","entity","AppliesToEntities","busy","onAttach","getLinkableEntities","labelOf","LayersIcon","XIcon","Popover","PopoverTrigger","PlusIcon","PopoverContent","Command","CommandInput","CommandList","CommandEmpty","CommandGroup","CommandItem"],"mappings":"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","file":"chunk-Q4CWQHBJ.js","sourcesContent":["import type { JSX } from 'react';\nimport { useDeferredValue, useMemo, useState } from 'react';\nimport {\n BookOpenTextIcon,\n ChevronRightIcon,\n LayersIcon,\n LoaderCircle,\n PaperclipIcon,\n PlusIcon,\n SearchIcon,\n Unlink2Icon,\n XIcon,\n} from 'lucide-react';\nimport { Link } from 'react-router-dom';\nimport useSWR from 'swr';\nimport useSWRInfinite from 'swr/infinite';\nimport { useAppContext } from '../../core/app-context';\nimport { useConfig } from '../../core/config/config-context';\nimport { getDisplayAttribute } from '../../core/get-display-attribute';\nimport type { Attribute, Entity } from '../../core/types';\nimport { useCanMutate } from '../../core/permissions';\nimport { useDispatch } from '../../entities/actions-context';\nimport { getEntitySchema } from '../../graphql-client/queries';\nimport type { MappedEntityItem } from '../../graphql-client/types';\nimport { useEntityQuery } from '../../graphql-client/use-entity-query';\nimport {\n Badge,\n Button,\n Collapsible,\n CollapsibleContent,\n CollapsibleTrigger,\n Command,\n CommandEmpty,\n CommandGroup,\n CommandInput,\n CommandItem,\n CommandList,\n Dialog,\n DialogContent,\n DialogDescription,\n DialogHeader,\n DialogTitle,\n Input,\n PermissionTooltip,\n Popover,\n PopoverContent,\n PopoverTrigger,\n Skeleton,\n toast,\n Tooltip,\n TooltipContent,\n TooltipProvider,\n TooltipTrigger,\n} from '../../ui';\nimport { fetcher, FetcherError, knowledgePaths, type KnowledgeApi } from './api';\nimport {\n findEntityByName,\n getAttachableSources,\n getEntityLinkLabel,\n getKnowledgeKindLabel,\n getKnowledgeLinkLabel,\n getKnowledgeSourcePath,\n getLinkableEntities,\n groupKnowledgeLinks,\n isKnowledgeLinkableEntity,\n summarizeKnowledgeStatus,\n type KnowledgeEntityLink,\n type KnowledgeEntitySources,\n type KnowledgeLink,\n type KnowledgeLinkGroup,\n type KnowledgeLinksList,\n type KnowledgeLinkTarget,\n type KnowledgeRecordRef,\n type KnowledgeRecordSources,\n type KnowledgeSource,\n type KnowledgeSourcesList,\n} from './knowledge';\nimport {\n describeApiError,\n InfiniteScrollSentinel,\n invalidateKnowledgeLinks,\n SourceIcon,\n StatusBadge,\n useProjectKnowledge,\n} from './shared';\n\nconst PICKER_PAGE_SIZE = 50;\n\nfunction DetachButton({\n disabled,\n label,\n onClick,\n}: {\n disabled: boolean;\n label: string;\n onClick: () => void;\n}): JSX.Element {\n return (\n <Tooltip>\n <TooltipTrigger asChild>\n <Button\n aria-label={label}\n className=\"fk:size-7 fk:shrink-0\"\n disabled={disabled}\n size=\"icon\"\n variant=\"ghost\"\n onClick={onClick}\n >\n <Unlink2Icon className=\"fk:size-4\" />\n </Button>\n </TooltipTrigger>\n <TooltipContent>{label}</TooltipContent>\n </Tooltip>\n );\n}\n\n/**\n * The picker behind \"Attach document\": the project's documents, searched by\n * title, minus the ones already attached. Picking one links it right away.\n */\nfunction AttachDocumentDialog({\n api,\n attachedIds,\n onAttached,\n onOpenChange,\n open,\n projectId,\n target,\n targetLabel,\n}: {\n api: KnowledgeApi;\n attachedIds: string[];\n onAttached: (_source: KnowledgeSource) => void;\n onOpenChange: (_open: boolean) => void;\n open: boolean;\n projectId: string;\n /** A record, or an entity type when it names no record. */\n target: KnowledgeLinkTarget;\n /** How the target is called in the dialog copy: a record title, or an entity label. */\n targetLabel: string;\n}): JSX.Element {\n const [search, setSearch] = useState('');\n const deferredSearch = useDeferredValue(search.trim());\n const [attachingId, setAttachingId] = useState<string | null>(null);\n const getPageKey = (pageIndex: number, previousPage: KnowledgeSourcesList | null): string | null => {\n if (!open || (previousPage && !previousPage.hasMore)) {\n return null;\n }\n\n return knowledgePaths(projectId).sources({\n limit: PICKER_PAGE_SIZE,\n offset: pageIndex * PICKER_PAGE_SIZE,\n search: deferredSearch || undefined,\n });\n };\n // Paged like the document list: the attached ones are hidden client-side,\n // so a record with many documents attached must be able to reach the next page.\n const {\n data: pages,\n error,\n isLoading,\n setSize,\n size,\n } = useSWRInfinite<KnowledgeSourcesList>(getPageKey, fetcher, { keepPreviousData: true });\n const candidates = useMemo(\n () => getAttachableSources(pages?.flatMap((page) => page.sources) ?? [], attachedIds),\n [attachedIds, pages]\n );\n const hasMore = pages?.[pages.length - 1]?.hasMore ?? false;\n const isLoadingMore = pages !== undefined && size > pages.length;\n\n async function attach(source: KnowledgeSource): Promise<void> {\n setAttachingId(source.id);\n\n try {\n const result = await api.linkSource(source.id, target);\n\n toast.success(result.linked ? `Attached \"${source.title}\".` : `\"${source.title}\" was already attached.`);\n onAttached(source);\n onOpenChange(false);\n } catch (attachError) {\n toast.error(describeApiError(attachError, 'The document could not be attached.'));\n } finally {\n setAttachingId(null);\n }\n }\n\n let body: JSX.Element;\n\n if (error) {\n body = <p className=\"fk:py-6 fk:text-center fk:text-sm fk:text-destructive\">The documents could not be loaded.</p>;\n } else if (isLoading && !pages) {\n body = (\n <div aria-busy=\"true\" className=\"fk:flex fk:flex-col fk:gap-3 fk:py-2\">\n <Skeleton className=\"fk:h-10 fk:w-full\" />\n <Skeleton className=\"fk:h-10 fk:w-full\" />\n <Skeleton className=\"fk:h-10 fk:w-full\" />\n </div>\n );\n } else if (candidates.length === 0 && !hasMore) {\n body = (\n <p className=\"fk:py-6 fk:text-center fk:text-sm fk:text-muted-foreground\">\n {deferredSearch\n ? 'No documents match your search.'\n : 'Every document is attached already, or there are none yet. Add documents in Knowledge first.'}\n </p>\n );\n } else {\n body = (\n <ul className=\"fk:flex fk:max-h-80 fk:flex-col fk:overflow-y-auto fk:-mx-2\">\n {candidates.map((source) => {\n const status = summarizeKnowledgeStatus(source);\n const isAttaching = attachingId === source.id;\n\n return (\n <li key={source.id}>\n <button\n className=\"fk:flex fk:w-full fk:min-w-0 fk:items-center fk:gap-3 fk:rounded-lg fk:px-2 fk:py-2 fk:text-left fk:transition-colors fk:hover:bg-accent fk:focus-visible:bg-accent fk:focus-visible:outline-none fk:disabled:opacity-60 fk:corner-squircle\"\n disabled={attachingId !== null}\n type=\"button\"\n onClick={() => void attach(source)}\n >\n <SourceIcon source={source} />\n <span className=\"fk:min-w-0 fk:flex-1\">\n <span className=\"fk:block fk:truncate fk:text-sm fk:font-medium\">{source.title}</span>\n <span className=\"fk:block fk:truncate fk:text-xs fk:text-muted-foreground\">\n {getKnowledgeKindLabel(source)} · {status.detail}\n </span>\n </span>\n {isAttaching ? (\n <LoaderCircle className=\"fk:size-4 fk:shrink-0 fk:animate-spin fk:text-muted-foreground\" />\n ) : (\n <StatusBadge label={status.label} tone={status.tone} />\n )}\n </button>\n </li>\n );\n })}\n {hasMore && !isLoadingMore ? (\n <li aria-hidden>\n <InfiniteScrollSentinel onVisible={() => void setSize((current) => current + 1)} />\n </li>\n ) : null}\n {isLoadingMore ? (\n <li className=\"fk:flex fk:justify-center fk:py-2\">\n <LoaderCircle className=\"fk:size-4 fk:animate-spin fk:text-muted-foreground\" />\n </li>\n ) : null}\n </ul>\n );\n }\n\n return (\n <Dialog open={open} onOpenChange={onOpenChange}>\n <DialogContent className=\"fk:sm:max-w-lg\">\n <DialogHeader>\n <DialogTitle>Attach a document</DialogTitle>\n <DialogDescription>\n {target.recordId === undefined\n ? `Documents attached to every ${targetLabel} are searched before the rest of Knowledge when a question is about one of them, or about ${targetLabel} in general.`\n : `Documents attached to ${targetLabel} are searched before the rest of Knowledge when a question is about it.`}\n </DialogDescription>\n </DialogHeader>\n <div className=\"fk:relative\">\n <SearchIcon className=\"fk:pointer-events-none fk:absolute fk:left-2.5 fk:top-1/2 fk:size-4 fk:-translate-y-1/2 fk:text-muted-foreground\" />\n <Input\n aria-label=\"Search documents\"\n autoFocus\n className=\"fk:pl-8\"\n placeholder=\"Search documents by title\"\n value={search}\n onChange={(event) => setSearch(event.target.value)}\n />\n </div>\n {body}\n </DialogContent>\n </Dialog>\n );\n}\n\n/** Document rows shared by the record panel and the entity dialog: icon, title, kind and status, plus an optional action. */\nfunction SourceRows({\n projectId,\n renderAction,\n sources,\n}: {\n projectId: string;\n renderAction?: (_source: KnowledgeSource) => JSX.Element | null;\n sources: KnowledgeSource[];\n}): JSX.Element {\n return (\n <ul className=\"fk:flex fk:flex-col fk:gap-1 fk:-mx-2\">\n {sources.map((source) => {\n const status = summarizeKnowledgeStatus(source);\n\n return (\n <li\n className=\"fk:flex fk:min-w-0 fk:items-center fk:gap-2 fk:rounded-lg fk:px-2 fk:py-1.5 fk:corner-squircle\"\n key={source.id}\n >\n <Link\n className=\"fk:flex fk:min-w-0 fk:flex-1 fk:items-center fk:gap-3 fk:rounded-md fk:hover:underline fk:underline-offset-2\"\n to={getKnowledgeSourcePath(projectId, source.id)}\n >\n <SourceIcon source={source} />\n <span className=\"fk:min-w-0 fk:flex-1\">\n <span className=\"fk:block fk:truncate fk:text-sm fk:font-medium\">{source.title}</span>\n <span className=\"fk:block fk:truncate fk:text-xs fk:text-muted-foreground\">\n {getKnowledgeKindLabel(source)} · {status.detail}\n </span>\n </span>\n </Link>\n <StatusBadge label={status.label} tone={status.tone} />\n {renderAction?.(source)}\n </li>\n );\n })}\n </ul>\n );\n}\n\n/**\n * The documents attached to every record of an entity type, behind a button\n * in the Desk list toolbar: open them, attach another, detach one. Only the\n * project's own entities qualify, and the button stays out while the schema\n * is unknown.\n */\nexport function EntityKnowledgeButton({ entityName }: { entityName: string }): JSX.Element | null {\n const { api, projectId } = useProjectKnowledge();\n const { currentProject } = useConfig();\n const [isOpen, setIsOpen] = useState(false);\n const entitySchema = findEntityByName(currentProject?.schema ?? [], entityName);\n const isLinkable = Boolean(entitySchema) && isKnowledgeLinkableEntity(entityName);\n const { data, error } = useSWR<KnowledgeEntitySources>(\n projectId && isLinkable ? knowledgePaths(projectId).entitySources(entityName) : null,\n fetcher\n );\n\n if (!api || !projectId || !entitySchema || !isLinkable || (error instanceof FetcherError && error.status === 404)) {\n return null;\n }\n\n const count = data?.sources.length ?? 0;\n\n return (\n <>\n <Button\n aria-label={`Knowledge for every ${getEntityLinkLabel(entitySchema)}`}\n className=\"fk:mr-2 fk:h-8\"\n data-knowledge-entity-button\n size=\"sm\"\n variant=\"outline\"\n onClick={() => setIsOpen(true)}\n >\n <BookOpenTextIcon />\n Knowledge\n {count > 0 ? (\n <Badge className=\"fk:corner-squircle\" variant=\"secondary\">\n {count}\n </Badge>\n ) : null}\n </Button>\n <EntityKnowledgeDialog\n api={api}\n entityLabel={getEntityLinkLabel(entitySchema)}\n entityName={entityName}\n open={isOpen}\n projectId={projectId}\n onOpenChange={setIsOpen}\n />\n </>\n );\n}\n\nfunction EntityKnowledgeDialog({\n api,\n entityLabel,\n entityName,\n onOpenChange,\n open,\n projectId,\n}: {\n api: KnowledgeApi;\n entityLabel: string;\n entityName: string;\n onOpenChange: (_open: boolean) => void;\n open: boolean;\n projectId: string;\n}): JSX.Element {\n const canMutate = useCanMutate();\n const [isPickerOpen, setIsPickerOpen] = useState(false);\n const [detachingId, setDetachingId] = useState<string | null>(null);\n const { data, error, isLoading, mutate } = useSWR<KnowledgeEntitySources>(\n knowledgePaths(projectId).entitySources(entityName),\n fetcher\n );\n const sources = data?.sources ?? [];\n const target = useMemo<KnowledgeLinkTarget>(() => ({ entity: entityName }), [entityName]);\n\n async function detach(source: KnowledgeSource): Promise<void> {\n setDetachingId(source.id);\n\n try {\n await api.unlinkSource(source.id, target);\n toast.success(`\"${source.title}\" no longer applies to every ${entityLabel}.`);\n await mutate({ sources: sources.filter((entry) => entry.id !== source.id) }, { revalidate: false });\n void invalidateKnowledgeLinks();\n } catch (detachError) {\n toast.error(describeApiError(detachError, 'The document could not be detached.'));\n } finally {\n setDetachingId(null);\n }\n }\n\n let body: JSX.Element;\n\n if (error) {\n body = <p className=\"fk:text-sm fk:text-destructive\">The documents could not be loaded.</p>;\n } else if (isLoading && !data) {\n body = (\n <div aria-busy=\"true\" className=\"fk:flex fk:flex-col fk:gap-2\">\n <Skeleton className=\"fk:h-9 fk:w-full\" />\n <Skeleton className=\"fk:h-9 fk:w-2/3\" />\n </div>\n );\n } else if (sources.length === 0) {\n body = (\n <p className=\"fk:text-sm fk:text-muted-foreground\">\n No documents apply to every {entityLabel} yet. Attach a guide, a policy or a spec that holds for all of them and\n the agent searches it first when asked about any {entityLabel}, or about {entityLabel} in general.\n </p>\n );\n } else {\n body = (\n <SourceRows\n projectId={projectId}\n sources={sources}\n renderAction={(source) =>\n canMutate ? (\n <DetachButton\n disabled={detachingId !== null}\n label={`Detach ${source.title}`}\n onClick={() => void detach(source)}\n />\n ) : null\n }\n />\n );\n }\n\n return (\n <Dialog open={open} onOpenChange={onOpenChange}>\n <DialogContent className=\"fk:sm:max-w-lg\" data-knowledge-entity-dialog>\n <DialogHeader>\n <DialogTitle className=\"fk:flex fk:items-center fk:gap-2\">\n <BookOpenTextIcon className=\"fk:size-4 fk:text-muted-foreground\" />\n Knowledge for every {entityLabel}\n {data ? (\n <Badge className=\"fk:corner-squircle\" variant=\"outline\">\n {sources.length}\n </Badge>\n ) : null}\n </DialogTitle>\n <DialogDescription>\n Documents attached here apply to every {entityLabel}; each record can hold its own on top.\n </DialogDescription>\n </DialogHeader>\n <div className=\"fk:max-h-80 fk:overflow-y-auto\">{body}</div>\n <div className=\"fk:flex fk:justify-end\">\n <PermissionTooltip disabled={!canMutate}>\n <Button disabled={!canMutate} size=\"sm\" variant=\"outline\" onClick={() => setIsPickerOpen(true)}>\n <PaperclipIcon />\n Attach document\n </Button>\n </PermissionTooltip>\n </div>\n <AttachDocumentDialog\n api={api}\n attachedIds={sources.map((source) => source.id)}\n open={isPickerOpen}\n projectId={projectId}\n target={target}\n targetLabel={entityLabel}\n onAttached={(source) => {\n // \"Already attached\" (a stale picker in another tab) must not list the document twice.\n void mutate(\n { sources: sources.some((entry) => entry.id === source.id) ? sources : [...sources, source] },\n { revalidate: false }\n );\n void invalidateKnowledgeLinks();\n }}\n onOpenChange={setIsPickerOpen}\n />\n </DialogContent>\n </Dialog>\n );\n}\n\n/**\n * The documents attached to one record, pinned under the record form in the\n * Desk as a collapsible panel: open them, attach another, detach one. The\n * agent searches these before the rest of Knowledge when a question is about this record.\n */\nexport function RecordKnowledgePanel({\n entityName,\n recordId,\n recordLabel,\n}: {\n entityName: string;\n recordId: string;\n recordLabel: string;\n}): JSX.Element | null {\n const { api, projectId } = useProjectKnowledge();\n const { currentProject } = useConfig();\n const canMutate = useCanMutate();\n const [isOpen, setIsOpen] = useState(false);\n const [isPickerOpen, setIsPickerOpen] = useState(false);\n const [detachingId, setDetachingId] = useState<string | null>(null);\n const record = useMemo<KnowledgeRecordRef>(() => ({ entity: entityName, recordId }), [entityName, recordId]);\n const entitySchema = findEntityByName(currentProject?.schema ?? [], entityName);\n const entityLabel = entitySchema ? getEntityLinkLabel(entitySchema) : entityName;\n // Built-in entities (assets, tags) are not something a document is about, so they are not even asked.\n const isLinkable = isKnowledgeLinkableEntity(entityName);\n const { data, error, isLoading, mutate } = useSWR<KnowledgeRecordSources>(\n projectId && isLinkable ? knowledgePaths(projectId).recordSources(record) : null,\n fetcher\n );\n\n if (!api || !projectId || !isLinkable) {\n return null;\n }\n\n // The platform answers 404 for an entity it does not know, which can\n // happen while a schema change is still deploying; the panel then stays out of the way.\n if (error instanceof FetcherError && error.status === 404) {\n return null;\n }\n\n const sources = data?.sources ?? [];\n const inherited = data?.inherited ?? [];\n const attachedIds = sources.map((source) => source.id);\n const knowledgeApi = api;\n\n async function detach(source: KnowledgeSource): Promise<void> {\n setDetachingId(source.id);\n\n try {\n await knowledgeApi.unlinkSource(source.id, record);\n toast.success(`Detached \"${source.title}\".`);\n // The global invalidation below revalidates this key too; one refetch is enough.\n await mutate({ sources: sources.filter((entry) => entry.id !== source.id), inherited }, { revalidate: false });\n void invalidateKnowledgeLinks();\n } catch (detachError) {\n toast.error(describeApiError(detachError, 'The document could not be detached.'));\n } finally {\n setDetachingId(null);\n }\n }\n\n let body: JSX.Element;\n\n if (error) {\n body = <p className=\"fk:text-sm fk:text-destructive\">The attached documents could not be loaded.</p>;\n } else if (isLoading && !data) {\n body = (\n <div aria-busy=\"true\" className=\"fk:flex fk:flex-col fk:gap-2\">\n <Skeleton className=\"fk:h-9 fk:w-full\" />\n <Skeleton className=\"fk:h-9 fk:w-2/3\" />\n </div>\n );\n } else {\n body = (\n <div className=\"fk:flex fk:flex-col fk:gap-4\">\n {sources.length === 0 ? (\n <p className=\"fk:text-sm fk:text-muted-foreground\">\n {inherited.length > 0\n ? 'No documents attached to this record itself; the ones below apply to every record of its type.'\n : \"No documents attached. Attach a manual, spec or contract and the agent will use it alongside the record's data when asked about this record.\"}\n </p>\n ) : (\n <SourceRows\n projectId={projectId}\n sources={sources}\n renderAction={(source) =>\n canMutate ? (\n <DetachButton\n disabled={detachingId !== null}\n label={`Detach ${source.title}`}\n onClick={() => void detach(source)}\n />\n ) : null\n }\n />\n )}\n {inherited.length > 0 ? (\n <div className=\"fk:flex fk:flex-col fk:gap-1\" data-knowledge-inherited>\n <span className=\"fk:text-xs fk:font-medium fk:uppercase fk:tracking-wide fk:text-muted-foreground\">\n Applies to every {entityLabel}\n </span>\n <SourceRows projectId={projectId} sources={inherited} />\n </div>\n ) : null}\n </div>\n );\n }\n\n // The record drawer is mounted by the actions manager outside any app, so\n // it carries no tooltip provider of its own. The panel is a flex child of\n // the drawer: collapsed it is one row, open it claims half the height and\n // scrolls inside, so the form above stays visible and scrollable.\n return (\n <TooltipProvider>\n <Collapsible\n className={`fk:z-10 fk:flex fk:shrink-0 fk:flex-col fk:border-t fk:bg-background ${isOpen ? 'fk:h-1/2' : ''}`}\n data-knowledge-record-panel\n open={isOpen}\n onOpenChange={setIsOpen}\n >\n <div className=\"fk:flex fk:shrink-0 fk:items-center fk:gap-2 fk:pl-4 fk:pr-10 fk:py-2\">\n <CollapsibleTrigger asChild>\n <button\n className=\"fk:flex fk:min-w-0 fk:flex-1 fk:items-center fk:gap-2 fk:rounded-md fk:py-1 fk:text-left fk:text-sm fk:font-semibold fk:tracking-tight fk:hover:text-foreground/80 fk:focus-visible:outline-none fk:focus-visible:ring-2 fk:focus-visible:ring-ring\"\n type=\"button\"\n >\n <ChevronRightIcon\n className={`fk:size-4 fk:shrink-0 fk:text-muted-foreground fk:transition-transform ${isOpen ? 'fk:rotate-90' : ''}`}\n />\n <BookOpenTextIcon className=\"fk:size-4 fk:shrink-0 fk:text-muted-foreground\" />\n Knowledge\n {data ? (\n <Badge className=\"fk:corner-squircle\" variant=\"outline\">\n {sources.length + inherited.length}\n </Badge>\n ) : null}\n </button>\n </CollapsibleTrigger>\n {isOpen ? (\n <PermissionTooltip disabled={!canMutate}>\n <Button disabled={!canMutate} size=\"sm\" variant=\"outline\" onClick={() => setIsPickerOpen(true)}>\n <PaperclipIcon />\n Attach document\n </Button>\n </PermissionTooltip>\n ) : null}\n </div>\n <CollapsibleContent className=\"fk:min-h-0 fk:flex-1 fk:overflow-y-auto fk:px-10 fk:pb-6\">\n {body}\n </CollapsibleContent>\n <AttachDocumentDialog\n api={api}\n attachedIds={attachedIds}\n open={isPickerOpen}\n projectId={projectId}\n target={record}\n targetLabel={recordLabel || `this ${entityName}`}\n onAttached={(source) => {\n // A document that applied to the whole type now hangs on the record itself; one that\n // was already attached (a stale picker in another tab) is not listed twice.\n void mutate(\n {\n sources: sources.some((entry) => entry.id === source.id) ? sources : [...sources, source],\n inherited: inherited.filter((entry) => entry.id !== source.id),\n },\n { revalidate: false }\n );\n void invalidateKnowledgeLinks();\n }}\n onOpenChange={setIsPickerOpen}\n />\n </Collapsible>\n </TooltipProvider>\n );\n}\n\ntype RecordLabels = { [recordId: string]: string };\n\n/**\n * Resolves the labels of one entity's linked records with a single display\n * query. The platform only names records whose display attribute it can read\n * from the node; a space-scoped one lives on other nodes, so the Studio asks\n * the data itself, the way the Desk grid does. Only called for a deployed entity.\n */\nfunction useRecordLabels(group: KnowledgeLinkGroup, schema: Entity[], displayAttribute: Attribute): RecordLabels {\n const { scope } = useAppContext();\n const ids = group.links.map((link) => link.recordId);\n const { data } = useEntityQuery({\n entityNamePlural: group.entityPlural,\n schema,\n scope,\n selection: 'display',\n includeCount: false,\n variables: { where: { _id: { in: ids } }, limit: ids.length },\n });\n\n return useMemo(() => {\n const labels: RecordLabels = {};\n\n for (const item of (data ?? []) as MappedEntityItem[]) {\n const value = item[displayAttribute.name];\n\n if (typeof value === 'string' && value.trim()) {\n labels[item._id] = value;\n }\n }\n\n return labels;\n }, [data, displayAttribute]);\n}\n\ninterface LinkedRecordGroupProps {\n canMutate: boolean;\n detachingKey: string | null;\n group: KnowledgeLinkGroup;\n onDetach: (_link: KnowledgeLink) => void;\n}\n\nfunction LinkedRecordGroup(props: LinkedRecordGroupProps): JSX.Element {\n const { currentProject } = useConfig();\n const schema = useMemo(() => currentProject?.schema ?? [], [currentProject]);\n const entitySchema = getEntitySchema(schema, props.group.entityPlural);\n const displayAttribute = getDisplayAttribute(entitySchema);\n\n // The query builder throws for an entity it does not know or that has no\n // attributes yet, which can happen between a deploy and the next schema\n // load; such a group shows what the platform knew.\n if (!entitySchema || !displayAttribute) {\n return (\n <LinkedRecordRows\n {...props}\n entityLabel={entitySchema ? getEntityLinkLabel(entitySchema) : props.group.entityPlural}\n labels={{}}\n />\n );\n }\n\n return (\n <ResolvedLinkedRecordGroup\n {...props}\n displayAttribute={displayAttribute}\n schema={schema}\n entityLabel={getEntityLinkLabel(entitySchema)}\n />\n );\n}\n\nfunction ResolvedLinkedRecordGroup({\n displayAttribute,\n entityLabel,\n schema,\n ...props\n}: LinkedRecordGroupProps & { displayAttribute: Attribute; entityLabel: string; schema: Entity[] }): JSX.Element {\n const labels = useRecordLabels(props.group, schema, displayAttribute);\n\n return <LinkedRecordRows {...props} entityLabel={entityLabel} labels={labels} />;\n}\n\nfunction LinkedRecordRows({\n canMutate,\n detachingKey,\n entityLabel,\n group,\n labels,\n onDetach,\n}: LinkedRecordGroupProps & { entityLabel: string; labels: RecordLabels }): JSX.Element {\n const dispatch = useDispatch();\n\n return (\n <div className=\"fk:flex fk:flex-col fk:gap-1\">\n <span className=\"fk:text-xs fk:font-medium fk:uppercase fk:tracking-wide fk:text-muted-foreground\">\n {entityLabel}\n </span>\n <ul className=\"fk:flex fk:flex-col fk:-mx-2\">\n {group.links.map((link) => {\n const label = getKnowledgeLinkLabel(link, labels[link.recordId]);\n const key = `${link.entity}:${link.recordId}`;\n\n return (\n <li\n className=\"fk:flex fk:min-w-0 fk:items-center fk:gap-2 fk:rounded-lg fk:px-2 fk:py-1 fk:corner-squircle\"\n key={key}\n >\n <button\n className=\"fk:min-w-0 fk:flex-1 fk:truncate fk:text-left fk:text-sm fk:hover:underline fk:underline-offset-2\"\n title={link.recordId}\n type=\"button\"\n onClick={() =>\n dispatch({\n type: 'EditEntity',\n payload: { entityId: link.recordId, entityNamePlural: group.entityPlural },\n })\n }\n >\n {label}\n </button>\n {canMutate ? (\n <DetachButton\n disabled={detachingKey !== null}\n label={`Detach from ${label}`}\n onClick={() => onDetach(link)}\n />\n ) : null}\n </li>\n );\n })}\n </ul>\n </div>\n );\n}\n\n/**\n * The records a document is attached to, on the document page. Attaching\n * happens from the record's side in the Desk, where the record is in view.\n */\nexport function LinkedRecordsSection({\n api,\n projectId,\n source,\n}: {\n api: KnowledgeApi;\n projectId: string;\n source: KnowledgeSource;\n}): JSX.Element {\n const canMutate = useCanMutate();\n const [detachingKey, setDetachingKey] = useState<string | null>(null);\n const { data, error, isLoading, mutate } = useSWR<KnowledgeLinksList>(\n knowledgePaths(projectId).links(source.id),\n fetcher\n );\n const links = useMemo(() => data?.links ?? [], [data]);\n const entities = useMemo(() => data?.entities ?? [], [data]);\n const groups = useMemo(() => groupKnowledgeLinks(links), [links]);\n\n async function detach(link: KnowledgeLink): Promise<void> {\n setDetachingKey(`${link.entity}:${link.recordId}`);\n\n try {\n await api.unlinkSource(source.id, { entity: link.entity, recordId: link.recordId });\n toast.success('Detached.');\n await mutate(\n {\n links: links.filter((entry) => entry.entity !== link.entity || entry.recordId !== link.recordId),\n entities,\n },\n { revalidate: false }\n );\n void invalidateKnowledgeLinks();\n } catch (detachError) {\n toast.error(describeApiError(detachError, 'The document could not be detached.'));\n } finally {\n setDetachingKey(null);\n }\n }\n\n async function detachEntity(link: KnowledgeEntityLink): Promise<void> {\n setDetachingKey(`${link.entity}:*`);\n\n try {\n await api.unlinkSource(source.id, { entity: link.entity });\n toast.success('Detached.');\n await mutate(\n { links, entities: entities.filter((entry) => entry.entity !== link.entity) },\n { revalidate: false }\n );\n void invalidateKnowledgeLinks();\n } catch (detachError) {\n toast.error(describeApiError(detachError, 'The document could not be detached.'));\n } finally {\n setDetachingKey(null);\n }\n }\n\n async function attachEntity(entity: Entity): Promise<void> {\n setDetachingKey(`${entity.name}:+`);\n\n try {\n const result = await api.linkSource(source.id, { entity: entity.name });\n\n toast.success(\n result.linked\n ? `Applies to every ${getEntityLinkLabel(entity)} now.`\n : `Already applied to every ${getEntityLinkLabel(entity)}.`\n );\n await mutate(\n {\n links,\n entities: entities.some((entry) => entry.entity === entity.name)\n ? entities\n : [...entities, { entity: entity.name, entityPlural: entity.plural }],\n },\n { revalidate: false }\n );\n void invalidateKnowledgeLinks();\n } catch (attachError) {\n toast.error(describeApiError(attachError, 'The document could not be attached.'));\n } finally {\n setDetachingKey(null);\n }\n }\n\n let body: JSX.Element;\n\n if (error) {\n body = <p className=\"fk:text-sm fk:text-destructive\">The linked records could not be loaded.</p>;\n } else if (isLoading && !data) {\n body = (\n <div aria-busy=\"true\" className=\"fk:flex fk:flex-col fk:gap-2\">\n <Skeleton className=\"fk:h-6 fk:w-1/2\" />\n <Skeleton className=\"fk:h-6 fk:w-1/3\" />\n </div>\n );\n } else if (groups.length === 0) {\n body = (\n <p className=\"fk:text-sm fk:text-muted-foreground\">\n Not attached to any record. Open a record in the Desk and use \"Attach document\" so the agent searches this\n document before the rest of Knowledge when asked about that record.\n </p>\n );\n } else {\n body = (\n <div className=\"fk:flex fk:flex-col fk:gap-4\">\n {groups.map((group) => (\n <LinkedRecordGroup\n canMutate={canMutate}\n detachingKey={detachingKey}\n group={group}\n key={group.entityPlural}\n onDetach={(link) => void detach(link)}\n />\n ))}\n </div>\n );\n }\n\n return (\n <>\n <AppliesToEntities\n busy={detachingKey !== null}\n canMutate={canMutate}\n entities={entities}\n isLoading={isLoading && !data}\n onAttach={(entity) => void attachEntity(entity)}\n onDetach={(link) => void detachEntity(link)}\n />\n <div className=\"fk:mt-2 fk:flex fk:items-center fk:gap-2\">\n <PaperclipIcon className=\"fk:size-4 fk:text-muted-foreground\" />\n <h3 className=\"fk:text-sm fk:font-semibold fk:tracking-tight\">Linked records</h3>\n {links.length > 0 ? (\n <Badge className=\"fk:corner-squircle\" variant=\"outline\">\n {links.length}\n </Badge>\n ) : null}\n </div>\n <p className=\"fk:text-sm fk:text-muted-foreground\">\n When a question is about one of these records, the agent searches this document before the rest.\n </p>\n {body}\n </>\n );\n}\n\n/**\n * The entity types a document applies to, on the document page: chips for\n * the current ones, and a picker over the project's remaining entities. A\n * document attached to a type is searched first for any record of it.\n */\nfunction AppliesToEntities({\n busy,\n canMutate,\n entities,\n isLoading,\n onAttach,\n onDetach,\n}: {\n /** An attach or detach is in flight: no second one until it answers. */\n busy: boolean;\n canMutate: boolean;\n entities: KnowledgeEntityLink[];\n isLoading: boolean;\n onAttach: (_entity: Entity) => void;\n onDetach: (_link: KnowledgeEntityLink) => void;\n}): JSX.Element {\n const { currentProject } = useConfig();\n const schema = useMemo(() => currentProject?.schema ?? [], [currentProject]);\n const [isPickerOpen, setIsPickerOpen] = useState(false);\n const candidates = useMemo(() => getLinkableEntities(schema, entities), [entities, schema]);\n const labelOf = (link: KnowledgeEntityLink): string => {\n const entity = findEntityByName(schema, link.entity);\n\n return entity ? getEntityLinkLabel(entity) : link.entityPlural;\n };\n\n return (\n <div className=\"fk:flex fk:flex-col fk:gap-3\" data-knowledge-applies-to>\n <div className=\"fk:flex fk:items-center fk:gap-2\">\n <LayersIcon className=\"fk:size-4 fk:text-muted-foreground\" />\n <h3 className=\"fk:text-sm fk:font-semibold fk:tracking-tight\">Applies to</h3>\n {entities.length > 0 ? (\n <Badge className=\"fk:corner-squircle\" variant=\"outline\">\n {entities.length}\n </Badge>\n ) : null}\n </div>\n <p className=\"fk:text-sm fk:text-muted-foreground\">\n Attach this document to a whole entity type and the agent searches it before the rest of Knowledge for every\n record of that type, and for questions about the type in general.\n </p>\n {isLoading ? (\n <Skeleton className=\"fk:h-7 fk:w-1/3\" />\n ) : (\n <div className=\"fk:flex fk:flex-wrap fk:items-center fk:gap-2\">\n {entities.map((link) => {\n const label = labelOf(link);\n\n return (\n <Badge\n className=\"fk:gap-1 fk:py-1 fk:pl-2.5 fk:pr-1 fk:corner-squircle\"\n key={link.entity}\n variant=\"secondary\"\n >\n <span className=\"fk:capitalize\">{label}</span>\n {canMutate ? (\n <Tooltip>\n <TooltipTrigger asChild>\n <button\n aria-label={`No longer apply to every ${label}`}\n className=\"fk:rounded-sm fk:p-0.5 fk:text-muted-foreground fk:hover:text-foreground fk:focus-visible:outline-none fk:focus-visible:ring-2 fk:focus-visible:ring-ring fk:disabled:opacity-50\"\n disabled={busy}\n type=\"button\"\n onClick={() => onDetach(link)}\n >\n <XIcon className=\"fk:size-3\" />\n </button>\n </TooltipTrigger>\n <TooltipContent>No longer apply to every {label}</TooltipContent>\n </Tooltip>\n ) : null}\n </Badge>\n );\n })}\n <Popover open={isPickerOpen} onOpenChange={setIsPickerOpen}>\n <PermissionTooltip disabled={!canMutate}>\n <PopoverTrigger asChild>\n <Button className=\"fk:h-7\" disabled={!canMutate || candidates.length === 0} size=\"sm\" variant=\"outline\">\n <PlusIcon />\n Add entity type\n </Button>\n </PopoverTrigger>\n </PermissionTooltip>\n <PopoverContent align=\"start\" className=\"fk:w-64 fk:p-0\">\n <Command>\n <CommandInput placeholder=\"Search entity types\" />\n <CommandList>\n <CommandEmpty>No entity type matches.</CommandEmpty>\n <CommandGroup>\n {candidates.map((entity) => (\n <CommandItem\n key={entity.name}\n keywords={[getEntityLinkLabel(entity), entity.name, entity.plural]}\n value={entity.name}\n onSelect={() => {\n setIsPickerOpen(false);\n onAttach(entity);\n }}\n >\n <span className=\"fk:capitalize\">{getEntityLinkLabel(entity)}</span>\n </CommandItem>\n ))}\n </CommandGroup>\n </CommandList>\n </Command>\n </PopoverContent>\n </Popover>\n </div>\n )}\n </div>\n );\n}\n"]}
@@ -1,4 +1,4 @@
1
- import'./index.css';import {a}from'./chunk-HKN6BX5U.js';import {c}from'./chunk-MBBE4RND.js';import {b,c as c$1}from'./chunk-5CVDMZUI.js';import {useState,useEffect}from'react';var d="0.2.34",L={current:d,latest:d,isCurrent:true};function le(){let e=b(),[t,r]=useState(null);return useEffect(()=>{if(e)return e.onUpdateDownloaded(o=>{r(o.version);})},[e]),e?t===null?{current:e.version,latest:e.version,isCurrent:true}:{current:e.version,latest:t,isCurrent:false,upgrade:()=>e.installUpdate()}:L}var U="flexkit:telemetry",V="flexkit:error-reporting",B=/^(localhost|127\.0\.0\.1|\[::1\]|0\.0\.0\.0)$|\.(localhost|test|local)$/i;function M(e){return e.override==="on"?true:e.override==="off"||!e.hosted||e.domain!=="flexkit.io"||e.version==="0.0.0"?false:e.isDesktop||!B.test(e.hostname)}function Y(){try{let e=a();return e?.getItem(U)??e?.getItem(V)??null}catch{return null}}function k({hosted:e}){return typeof window>"u"?false:M({hosted:e,isDesktop:!!b(),domain:c$1,hostname:window.location.hostname,version:d,override:Y()})}async function v(e,t,{keepalive:r=false}={}){return (await fetch(e,{body:t,credentials:"same-origin",headers:{"Content-Type":"application/json"},keepalive:r,method:"POST"})).status}var f={componentStack:4e3,message:2e3,name:200,path:500,stack:8e3,userAgent:500},$=10,G=6e4,K=50,z=5e3,R=/^[a-z0-9]{1,64}$/;function g(e,t){return e.length>t?e.slice(0,t):e}function J(e){let t=e;try{/^[a-z][a-z0-9+.-]*:/i.test(e)&&(t=new URL(e).pathname);}catch{}return g(t.split(/[?#]/)[0]??"",f.path)}function H(e){if(e instanceof Error)return {name:e.name||"Error",message:typeof e.message=="string"?e.message:"",stack:typeof e.stack=="string"?e.stack:void 0};if(typeof e=="string")return {name:"Error",message:e};if(e==null)return {name:"Error",message:`Non-Error thrown: ${String(e)}`};if(typeof e=="object"){let t=e.constructor?.name;return {name:"Error",message:`Non-Error thrown: ${typeof t=="string"&&t?t:"object"}`}}return {name:"Error",message:`Non-Error thrown: ${typeof e}`}}var X=[/ResizeObserver loop (limit exceeded|completed with undelivered notifications)/i,/^Script error\.?$/i,/The (operation|user) (was )?aborted/i,/signal is aborted without reason/i],A=/\b(chrome|moz|safari(-web)?|ms-browser)-extension:\/\//i,F=/[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}/gi;function Z(e){return e.replace(F,"<email>")}function q(e,t){return e.name==="AbortError"||X.some(r=>r.test(e.message))||t&&A.test(t)?true:!!(e.stack&&A.test(e.stack))}function W(e,t){let r=H(e);if(q(r,t.sourceUrl))return null;let o={source:t.source,name:g(r.name,f.name),message:g(Z(r.message),f.message),occurredAt:(t.now??new Date).toISOString()};return r.stack&&(o.stack=g(r.stack,f.stack)),t.componentStack&&(o.componentStack=g(t.componentStack,f.componentStack)),t.path&&(o.path=J(t.path)),t.projectId&&R.test(t.projectId)&&(o.projectId=t.projectId),o}function Q(e){return `${e.name}
1
+ import'./index.css';import {a}from'./chunk-HKN6BX5U.js';import {c}from'./chunk-MBBE4RND.js';import {b,c as c$1}from'./chunk-5CVDMZUI.js';import {useState,useEffect}from'react';var d="0.2.35",L={current:d,latest:d,isCurrent:true};function le(){let e=b(),[t,r]=useState(null);return useEffect(()=>{if(e)return e.onUpdateDownloaded(o=>{r(o.version);})},[e]),e?t===null?{current:e.version,latest:e.version,isCurrent:true}:{current:e.version,latest:t,isCurrent:false,upgrade:()=>e.installUpdate()}:L}var U="flexkit:telemetry",V="flexkit:error-reporting",B=/^(localhost|127\.0\.0\.1|\[::1\]|0\.0\.0\.0)$|\.(localhost|test|local)$/i;function M(e){return e.override==="on"?true:e.override==="off"||!e.hosted||e.domain!=="flexkit.io"||e.version==="0.0.0"?false:e.isDesktop||!B.test(e.hostname)}function Y(){try{let e=a();return e?.getItem(U)??e?.getItem(V)??null}catch{return null}}function k({hosted:e}){return typeof window>"u"?false:M({hosted:e,isDesktop:!!b(),domain:c$1,hostname:window.location.hostname,version:d,override:Y()})}async function v(e,t,{keepalive:r=false}={}){return (await fetch(e,{body:t,credentials:"same-origin",headers:{"Content-Type":"application/json"},keepalive:r,method:"POST"})).status}var f={componentStack:4e3,message:2e3,name:200,path:500,stack:8e3,userAgent:500},$=10,G=6e4,K=50,z=5e3,R=/^[a-z0-9]{1,64}$/;function g(e,t){return e.length>t?e.slice(0,t):e}function J(e){let t=e;try{/^[a-z][a-z0-9+.-]*:/i.test(e)&&(t=new URL(e).pathname);}catch{}return g(t.split(/[?#]/)[0]??"",f.path)}function H(e){if(e instanceof Error)return {name:e.name||"Error",message:typeof e.message=="string"?e.message:"",stack:typeof e.stack=="string"?e.stack:void 0};if(typeof e=="string")return {name:"Error",message:e};if(e==null)return {name:"Error",message:`Non-Error thrown: ${String(e)}`};if(typeof e=="object"){let t=e.constructor?.name;return {name:"Error",message:`Non-Error thrown: ${typeof t=="string"&&t?t:"object"}`}}return {name:"Error",message:`Non-Error thrown: ${typeof e}`}}var X=[/ResizeObserver loop (limit exceeded|completed with undelivered notifications)/i,/^Script error\.?$/i,/The (operation|user) (was )?aborted/i,/signal is aborted without reason/i],A=/\b(chrome|moz|safari(-web)?|ms-browser)-extension:\/\//i,F=/[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}/gi;function Z(e){return e.replace(F,"<email>")}function q(e,t){return e.name==="AbortError"||X.some(r=>r.test(e.message))||t&&A.test(t)?true:!!(e.stack&&A.test(e.stack))}function W(e,t){let r=H(e);if(q(r,t.sourceUrl))return null;let o={source:t.source,name:g(r.name,f.name),message:g(Z(r.message),f.message),occurredAt:(t.now??new Date).toISOString()};return r.stack&&(o.stack=g(r.stack,f.stack)),t.componentStack&&(o.componentStack=g(t.componentStack,f.componentStack)),t.path&&(o.path=J(t.path)),t.projectId&&R.test(t.projectId)&&(o.projectId=t.projectId),o}function Q(e){return `${e.name}
2
2
  ${e.message}
3
- ${e.stack??""}`}var ee=new TextEncoder;function P(e){return ee.encode(e).length}function te(e,t,r=G){let o=P(JSON.stringify({app:e,errors:[]})),s=[];for(let i of t.slice(0,$)){let n=P(JSON.stringify(i))+(s.length?1:0);if(o+n>r)break;o+=n,s.push(i);}return s.length===0?null:{body:JSON.stringify({app:e,errors:s}),count:s.length}}function re({app:e,send:t,getPath:r,now:o=()=>new Date,setTimer:s=(n,u)=>setTimeout(n,u),clearTimer:i=n=>clearTimeout(n)}){let n=[],u=new Set,_=0,y=null,a=null,E=false;function T(){a===null&&n.length>0&&!E&&(a=s(()=>{a=null,x();},z));}function D(l){let S=te(e,n);return S?(n.splice(0,S.count),t(S.body,{unloading:l}).then(p=>{(p===401||p===403||p===429)&&(E=true,n.length=0);}).catch(()=>{}),n.length>0):(n.shift(),n.length>0)}function x({unloading:l=false}={}){try{if(E)return;a!==null&&l&&(i(a),a=null),n.length>0&&D(l),T();}catch{}}return {report(l,S){try{if(E||_>=K)return;let p=W(l,{...S,now:o(),path:r?.(),projectId:y});if(!p)return;let b=Q(p);if(u.has(b))return;u.add(b),_+=1,n.push(p),T();}catch{}},setProjectId(l){y=l;},flush:x,pending:()=>n,dispose(){a!==null&&(i(a),a=null),E=true,n.length=0;}}}var ne=async(e,{unloading:t})=>{let r=c.clientErrors;if(t&&!b()&&typeof navigator<"u"&&typeof navigator.sendBeacon=="function")try{if(navigator.sendBeacon(r,new Blob([e],{type:"text/plain"})))return null}catch{}return v(r,e,{keepalive:t})},m=null;function we(e,{source:t="manual",componentStack:r}={}){m?.report(e,{source:t,componentStack:r});}function Re(e){m?.setProjectId(e);}function _e({hosted:e}){if(typeof window>"u"||m)return ()=>{};if(!k({hosted:e}))return ()=>{};let t=b(),r=re({app:{platform:t?"desktop":"web",version:d,userAgent:g(navigator.userAgent,f.userAgent)},send:ne,getPath:()=>window.location.pathname});function o(n){r.report(n.error??n.message,{source:"window",sourceUrl:n.filename});}function s(n){r.report(n.reason,{source:"unhandledrejection"});}function i(){r.flush({unloading:true});}return window.addEventListener("error",o),window.addEventListener("unhandledrejection",s),window.addEventListener("pagehide",i),m=r,()=>{window.removeEventListener("error",o),window.removeEventListener("unhandledrejection",s),window.removeEventListener("pagehide",i),r.flush({unloading:true}),r.dispose(),m===r&&(m=null);}}function oe(e){return e.event==="graph_opened"?`graph_opened:${e.projectId}`:e.event}function ie(e){let t=new Set,r=new Set;return {track(o,{onDelivered:s}={}){let i=oe(o);if(t.has(i)||r.has(i))return;t.add(i);let n=u=>{t.delete(i),u!==null&&u>=200&&u<300&&(r.add(i),s?.());};try{e(JSON.stringify(o)).then(n,()=>n(null));}catch{n(null);}}}}var w=null;function Oe(e){e.event==="graph_opened"&&!R.test(e.projectId)||w?.track(e);}var C="flexkit:desktop-launch-recorded";function se(e){try{return sessionStorage.getItem(e)==="1"}catch{return false}}function ae(e){try{sessionStorage.setItem(e,"1");}catch{}}function Ae({hosted:e}){if(!k({hosted:e}))return ()=>{};let t=ie(o=>v(c.productEvents,o,{keepalive:true})),r=b();return w=t,r&&!se(C)&&t.track({appVersion:r.version,event:"desktop_app_opened",os:r.platform},{onDelivered:()=>ae(C)}),()=>{w===t&&(w=null);}}export{le as a,we as b,Re as c,_e as d,Oe as e,Ae as f};//# sourceMappingURL=chunk-PSECMZJC.js.map
4
- //# sourceMappingURL=chunk-PSECMZJC.js.map
3
+ ${e.stack??""}`}var ee=new TextEncoder;function P(e){return ee.encode(e).length}function te(e,t,r=G){let o=P(JSON.stringify({app:e,errors:[]})),s=[];for(let i of t.slice(0,$)){let n=P(JSON.stringify(i))+(s.length?1:0);if(o+n>r)break;o+=n,s.push(i);}return s.length===0?null:{body:JSON.stringify({app:e,errors:s}),count:s.length}}function re({app:e,send:t,getPath:r,now:o=()=>new Date,setTimer:s=(n,u)=>setTimeout(n,u),clearTimer:i=n=>clearTimeout(n)}){let n=[],u=new Set,_=0,y=null,a=null,E=false;function T(){a===null&&n.length>0&&!E&&(a=s(()=>{a=null,x();},z));}function D(l){let S=te(e,n);return S?(n.splice(0,S.count),t(S.body,{unloading:l}).then(p=>{(p===401||p===403||p===429)&&(E=true,n.length=0);}).catch(()=>{}),n.length>0):(n.shift(),n.length>0)}function x({unloading:l=false}={}){try{if(E)return;a!==null&&l&&(i(a),a=null),n.length>0&&D(l),T();}catch{}}return {report(l,S){try{if(E||_>=K)return;let p=W(l,{...S,now:o(),path:r?.(),projectId:y});if(!p)return;let b=Q(p);if(u.has(b))return;u.add(b),_+=1,n.push(p),T();}catch{}},setProjectId(l){y=l;},flush:x,pending:()=>n,dispose(){a!==null&&(i(a),a=null),E=true,n.length=0;}}}var ne=async(e,{unloading:t})=>{let r=c.clientErrors;if(t&&!b()&&typeof navigator<"u"&&typeof navigator.sendBeacon=="function")try{if(navigator.sendBeacon(r,new Blob([e],{type:"text/plain"})))return null}catch{}return v(r,e,{keepalive:t})},m=null;function we(e,{source:t="manual",componentStack:r}={}){m?.report(e,{source:t,componentStack:r});}function Re(e){m?.setProjectId(e);}function _e({hosted:e}){if(typeof window>"u"||m)return ()=>{};if(!k({hosted:e}))return ()=>{};let t=b(),r=re({app:{platform:t?"desktop":"web",version:d,userAgent:g(navigator.userAgent,f.userAgent)},send:ne,getPath:()=>window.location.pathname});function o(n){r.report(n.error??n.message,{source:"window",sourceUrl:n.filename});}function s(n){r.report(n.reason,{source:"unhandledrejection"});}function i(){r.flush({unloading:true});}return window.addEventListener("error",o),window.addEventListener("unhandledrejection",s),window.addEventListener("pagehide",i),m=r,()=>{window.removeEventListener("error",o),window.removeEventListener("unhandledrejection",s),window.removeEventListener("pagehide",i),r.flush({unloading:true}),r.dispose(),m===r&&(m=null);}}function oe(e){return e.event==="graph_opened"?`graph_opened:${e.projectId}`:e.event}function ie(e){let t=new Set,r=new Set;return {track(o,{onDelivered:s}={}){let i=oe(o);if(t.has(i)||r.has(i))return;t.add(i);let n=u=>{t.delete(i),u!==null&&u>=200&&u<300&&(r.add(i),s?.());};try{e(JSON.stringify(o)).then(n,()=>n(null));}catch{n(null);}}}}var w=null;function Oe(e){e.event==="graph_opened"&&!R.test(e.projectId)||w?.track(e);}var C="flexkit:desktop-launch-recorded";function se(e){try{return sessionStorage.getItem(e)==="1"}catch{return false}}function ae(e){try{sessionStorage.setItem(e,"1");}catch{}}function Ae({hosted:e}){if(!k({hosted:e}))return ()=>{};let t=ie(o=>v(c.productEvents,o,{keepalive:true})),r=b();return w=t,r&&!se(C)&&t.track({appVersion:r.version,event:"desktop_app_opened",os:r.platform},{onDelivered:()=>ae(C)}),()=>{w===t&&(w=null);}}export{le as a,we as b,Re as c,_e as d,Oe as e,Ae as f};//# sourceMappingURL=chunk-R6DPAMLC.js.map
4
+ //# sourceMappingURL=chunk-R6DPAMLC.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/core/use-studio-version.ts","../src/core/telemetry.ts","../src/core/error/error-reporter.ts","../src/core/analytics/product-events.ts"],"names":["STUDIO_VERSION","WEB_VERSION","useStudioVersion","desktop","getDesktop","downloaded","setDownloaded","useState","useEffect","update","TELEMETRY_STORAGE_KEY","LEGACY_TELEMETRY_STORAGE_KEY","LOCAL_HOSTNAME","isTelemetryEnabled","env","readOverride","storage","getLocalStorage","isStudioTelemetryEnabled","hosted","flexkitDomain","postToUserApi","path","body","keepalive","LIMITS","MAX_ERRORS_PER_REPORT","MAX_REPORT_BYTES","MAX_ERRORS_PER_SESSION","FLUSH_INTERVAL_MS","PROJECT_ID_PATTERN","truncate","value","max","toReportPath","pathOrUrl","describeError","error","constructorName","NOISE_MESSAGES","EXTENSION_URL","EMAIL_ADDRESS","maskMessage","message","isNoiseError","details","sourceUrl","pattern","buildClientError","options","entry","errorKey","encoder","byteLength","buildReportBody","app","queue","maxBytes","size","errors","errorSize","createErrorReporter","send","getPath","now","setTimer","callback","ms","clearTimer","timer","seen","accepted","projectId","stopped","schedule","flush","sendNext","unloading","next","status","key","id","sendReportToBff","url","userApiPaths","installed","reportStudioError","source","componentStack","setErrorReportingProjectId","installErrorReporter","reporter","onError","event","onUnhandledRejection","onPageHide","productEventKey","studioEvent","createProductEventTracker","pending","delivered","onDelivered","settle","tracker","trackStudioEvent","DESKTOP_LAUNCH_STORAGE_KEY","readSessionFlag","writeSessionFlag","installProductEvents","current"],"mappings":"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","file":"chunk-PSECMZJC.js","sourcesContent":["'use client';\n\nimport { useEffect, useState } from 'react';\nimport { getDesktop } from './desktop';\n\nexport interface StudioVersion {\n current: string;\n latest: string;\n isCurrent: boolean;\n /** Applies the newer version (the desktop app restarts into it); undefined when there is nothing to apply. */\n upgrade?: () => void;\n}\n\n// `@flexkit/studio`'s own version, inlined at build time; a build without the\n// define (tests, tooling) shows a placeholder.\nexport const STUDIO_VERSION = typeof __STUDIO_VERSION__ === 'string' ? __STUDIO_VERSION__ : '0.0.0';\nconst WEB_VERSION: StudioVersion = { current: STUDIO_VERSION, latest: STUDIO_VERSION, isCurrent: true };\n\n/**\n * The Studio version shown in the app bar. The web Studio is always current\n * (the hosted Studio deploys with the packages); the desktop app reports its\n * own version and the update it has downloaded, if any.\n */\nexport function useStudioVersion(): StudioVersion {\n const desktop = getDesktop();\n const [downloaded, setDownloaded] = useState<string | null>(null);\n\n useEffect(() => {\n if (!desktop) {\n return;\n }\n\n return desktop.onUpdateDownloaded((update) => {\n setDownloaded(update.version);\n });\n }, [desktop]);\n\n if (!desktop) {\n return WEB_VERSION;\n }\n\n if (downloaded === null) {\n return { current: desktop.version, latest: desktop.version, isCurrent: true };\n }\n\n return { current: desktop.version, latest: downloaded, isCurrent: false, upgrade: () => desktop.installUpdate() };\n}\n","import { getDesktop } from './desktop';\nimport { flexkitDomain } from './domains';\nimport { getLocalStorage } from './local-storage';\nimport { STUDIO_VERSION } from './use-studio-version';\n\n/**\n * The on/off decision and the transport shared by the Studio's error reports\n * (core/error/error-reporter.ts) and product events\n * (core/analytics/product-events.ts).\n */\n\n/** Turns error reports and product events on (`on`) or off (`off`) in this browser, whatever the environment. */\nexport const TELEMETRY_STORAGE_KEY = 'flexkit:telemetry';\n/** The key error reporting shipped with; still honoured. */\nexport const LEGACY_TELEMETRY_STORAGE_KEY = 'flexkit:error-reporting';\n\nexport interface TelemetryEnvironment {\n /** The Studio lists the signed-in user's projects (hosted Studio, desktop app). */\n hosted: boolean;\n isDesktop: boolean;\n domain: string;\n hostname: string;\n version: string;\n /** The local storage setting in this browser, if any. */\n override: string | null;\n}\n\nconst LOCAL_HOSTNAME = /^(localhost|127\\.0\\.0\\.1|\\[::1\\]|0\\.0\\.0\\.0)$|\\.(localhost|test|local)$/i;\n\n/**\n * On for the hosted Studio and the desktop app against the production\n * backend, and off in development: a local backend (`flexkit.test`), a local\n * host, or a build without a version. The local storage setting forces it\n * `on` or `off`.\n */\nexport function isTelemetryEnabled(env: TelemetryEnvironment): boolean {\n if (env.override === 'on') {\n return true;\n }\n\n if (env.override === 'off') {\n return false;\n }\n\n if (!env.hosted || env.domain !== 'flexkit.io' || env.version === '0.0.0') {\n return false;\n }\n\n return env.isDesktop || !LOCAL_HOSTNAME.test(env.hostname);\n}\n\nfunction readOverride(): string | null {\n try {\n const storage = getLocalStorage();\n\n return storage?.getItem(TELEMETRY_STORAGE_KEY) ?? storage?.getItem(LEGACY_TELEMETRY_STORAGE_KEY) ?? null;\n } catch {\n return null;\n }\n}\n\n/** Whether this Studio sends error reports and product events to Flexkit. */\nexport function isStudioTelemetryEnabled({ hosted }: { hosted: boolean }): boolean {\n if (typeof window === 'undefined') {\n return false;\n }\n\n return isTelemetryEnabled({\n hosted,\n isDesktop: Boolean(getDesktop()),\n domain: flexkitDomain,\n hostname: window.location.hostname,\n version: STUDIO_VERSION,\n override: readOverride(),\n });\n}\n\n/**\n * POSTs JSON to a user-level backend route through the Studio's server proxy,\n * which adds the session. `keepalive` lets the request outlive the page.\n * Returns the response status.\n */\nexport async function postToUserApi(path: string, body: string, { keepalive = false } = {}): Promise<number> {\n const response = await fetch(path, {\n body,\n credentials: 'same-origin',\n headers: { 'Content-Type': 'application/json' },\n keepalive,\n method: 'POST',\n });\n\n return response.status;\n}\n","import { userApiPaths } from '../api-paths';\nimport { getDesktop } from '../desktop';\nimport { STUDIO_VERSION } from '../use-studio-version';\nimport { isStudioTelemetryEnabled, postToUserApi } from '../telemetry';\n\n/**\n * Reports unexpected Studio errors (uncaught errors, unhandled rejections and\n * errors caught by the error boundaries) to Flexkit through the Studio's own\n * BFF (`/api/flexkit/-/client-errors`), which forwards the session so the\n * report is attributed to the signed-in user.\n *\n * Reports carry the error, the Studio path (no query string or hash) and the\n * current project id; never request bodies, form values or record data.\n */\n\nexport type ClientErrorSource = 'window' | 'unhandledrejection' | 'react' | 'manual';\n\nexport interface ClientError {\n source: ClientErrorSource;\n name: string;\n message: string;\n stack?: string;\n componentStack?: string;\n path?: string;\n projectId?: string;\n occurredAt: string;\n}\n\nexport interface ClientErrorApp {\n platform: 'web' | 'desktop';\n version: string;\n userAgent?: string;\n}\n\nexport interface ClientErrorReport {\n app: ClientErrorApp;\n errors: ClientError[];\n}\n\n/** Field limits of the backend schema. */\nconst LIMITS = {\n componentStack: 4_000,\n message: 2_000,\n name: 200,\n path: 500,\n stack: 8_000,\n userAgent: 500,\n} as const;\n\n/** The backend accepts 1 to 10 errors per report. */\nexport const MAX_ERRORS_PER_REPORT = 10;\n/** The backend refuses bodies over 64 KB; `keepalive` requests share a 64 KB budget too. */\nexport const MAX_REPORT_BYTES = 60_000;\n/** Reports kept per page session; anything after that is dropped. */\nexport const MAX_ERRORS_PER_SESSION = 50;\n/** At most one report is sent per interval; errors in between are batched. */\nexport const FLUSH_INTERVAL_MS = 5_000;\n\n/** Kept for callers and tests that name the gate after error reporting. */\nexport {\n isTelemetryEnabled as isErrorReportingEnabled,\n LEGACY_TELEMETRY_STORAGE_KEY as ERROR_REPORTING_STORAGE_KEY,\n type TelemetryEnvironment as ErrorReportingEnvironment,\n} from '../telemetry';\n\nexport const PROJECT_ID_PATTERN = /^[a-z0-9]{1,64}$/;\n\nfunction truncate(value: string, max: number): string {\n return value.length > max ? value.slice(0, max) : value;\n}\n\n/** The pathname of a path or URL, without query string or hash. */\nexport function toReportPath(pathOrUrl: string): string {\n let path = pathOrUrl;\n\n try {\n if (/^[a-z][a-z0-9+.-]*:/i.test(pathOrUrl)) {\n path = new URL(pathOrUrl).pathname;\n }\n } catch {\n // Not a URL; strip by hand below.\n }\n\n return truncate(path.split(/[?#]/)[0] ?? '', LIMITS.path);\n}\n\nexport interface ErrorDetails {\n name: string;\n message: string;\n stack?: string;\n}\n\n/**\n * Name, message and stack of anything thrown. A non-Error value (a rejected\n * promise with a plain object) is described by its type only, so record data\n * someone rejected with never leaves the browser.\n */\nexport function describeError(error: unknown): ErrorDetails {\n if (error instanceof Error) {\n return {\n name: error.name || 'Error',\n message: typeof error.message === 'string' ? error.message : '',\n stack: typeof error.stack === 'string' ? error.stack : undefined,\n };\n }\n\n if (typeof error === 'string') {\n return { name: 'Error', message: error };\n }\n\n if (error === null || error === undefined) {\n return { name: 'Error', message: `Non-Error thrown: ${String(error)}` };\n }\n\n if (typeof error === 'object') {\n const constructorName = (error as { constructor?: { name?: unknown } }).constructor?.name;\n\n return {\n name: 'Error',\n message: `Non-Error thrown: ${typeof constructorName === 'string' && constructorName ? constructorName : 'object'}`,\n };\n }\n\n return { name: 'Error', message: `Non-Error thrown: ${typeof error}` };\n}\n\nconst NOISE_MESSAGES = [\n /ResizeObserver loop (limit exceeded|completed with undelivered notifications)/i,\n // Cross-origin script errors carry nothing to act on.\n /^Script error\\.?$/i,\n /The (operation|user) (was )?aborted/i,\n /signal is aborted without reason/i,\n];\n\nconst EXTENSION_URL = /\\b(chrome|moz|safari(-web)?|ms-browser)-extension:\\/\\//i;\n\nconst EMAIL_ADDRESS = /[A-Z0-9._%+-]+@[A-Z0-9.-]+\\.[A-Z]{2,}/gi;\n\n/** Masks email addresses an error message may quote from a record or form. */\nexport function maskMessage(message: string): string {\n return message.replace(EMAIL_ADDRESS, '<email>');\n}\n\n/** Errors that are not Studio bugs: browser quirks, aborted requests, browser extensions. */\nexport function isNoiseError(details: ErrorDetails, sourceUrl?: string): boolean {\n if (details.name === 'AbortError') {\n return true;\n }\n\n if (NOISE_MESSAGES.some((pattern) => pattern.test(details.message))) {\n return true;\n }\n\n if (sourceUrl && EXTENSION_URL.test(sourceUrl)) {\n return true;\n }\n\n return Boolean(details.stack && EXTENSION_URL.test(details.stack));\n}\n\nexport interface BuildClientErrorOptions {\n source: ClientErrorSource;\n componentStack?: string;\n path?: string;\n projectId?: string | null;\n /** The script the error came from (`ErrorEvent.filename`), checked for browser extensions. */\n sourceUrl?: string;\n now?: Date;\n}\n\n/** The report entry for an error, or null when it is noise. */\nexport function buildClientError(error: unknown, options: BuildClientErrorOptions): ClientError | null {\n const details = describeError(error);\n\n if (isNoiseError(details, options.sourceUrl)) {\n return null;\n }\n\n const entry: ClientError = {\n source: options.source,\n name: truncate(details.name, LIMITS.name),\n message: truncate(maskMessage(details.message), LIMITS.message),\n occurredAt: (options.now ?? new Date()).toISOString(),\n };\n\n if (details.stack) {\n entry.stack = truncate(details.stack, LIMITS.stack);\n }\n\n if (options.componentStack) {\n entry.componentStack = truncate(options.componentStack, LIMITS.componentStack);\n }\n\n if (options.path) {\n entry.path = toReportPath(options.path);\n }\n\n if (options.projectId && PROJECT_ID_PATTERN.test(options.projectId)) {\n entry.projectId = options.projectId;\n }\n\n return entry;\n}\n\n/** Identity of an error within a page session: the same message and stack are reported once. */\nexport function errorKey(error: Pick<ClientError, 'message' | 'name' | 'stack'>): string {\n return `${error.name}\\n${error.message}\\n${error.stack ?? ''}`;\n}\n\nconst encoder = new TextEncoder();\n\nfunction byteLength(value: string): number {\n return encoder.encode(value).length;\n}\n\n/**\n * Takes the errors that fit one report (at most 10 and `maxBytes` of JSON)\n * off the front of the queue. Returns the request body and how many errors it holds.\n */\nexport function buildReportBody(\n app: ClientErrorApp,\n queue: readonly ClientError[],\n maxBytes = MAX_REPORT_BYTES\n): { body: string; count: number } | null {\n // The envelope plus each error once, joined by commas.\n let size = byteLength(JSON.stringify({ app, errors: [] } satisfies ClientErrorReport));\n const errors: ClientError[] = [];\n\n for (const error of queue.slice(0, MAX_ERRORS_PER_REPORT)) {\n const errorSize = byteLength(JSON.stringify(error)) + (errors.length ? 1 : 0);\n\n if (size + errorSize > maxBytes) {\n break;\n }\n\n size += errorSize;\n errors.push(error);\n }\n\n if (errors.length === 0) {\n return null;\n }\n\n return { body: JSON.stringify({ app, errors } satisfies ClientErrorReport), count: errors.length };\n}\n\nexport type SendReport = (body: string, options: { unloading: boolean }) => Promise<number | null>;\n\nexport interface ErrorReporterOptions {\n app: ClientErrorApp;\n send: SendReport;\n /** The current Studio path; only its pathname is sent. */\n getPath?: () => string | undefined;\n now?: () => Date;\n setTimer?: (callback: () => void, ms: number) => unknown;\n clearTimer?: (timer: unknown) => void;\n}\n\nexport interface ErrorReporter {\n report: (error: unknown, options: Omit<BuildClientErrorOptions, 'path' | 'projectId' | 'now'>) => void;\n setProjectId: (projectId: string | null) => void;\n /** Sends the next report now; `unloading` uses a beacon or keepalive request that outlives the page. */\n flush: (options?: { unloading?: boolean }) => void;\n /** Errors waiting to be sent (tests). */\n pending: () => readonly ClientError[];\n dispose: () => void;\n}\n\n/** The queue, dedupe, cap and batching; the transport and the clock are injected. */\nexport function createErrorReporter({\n app,\n send,\n getPath,\n now = () => new Date(),\n setTimer = (callback, ms) => setTimeout(callback, ms),\n clearTimer = (timer) => clearTimeout(timer as ReturnType<typeof setTimeout>),\n}: ErrorReporterOptions): ErrorReporter {\n const queue: ClientError[] = [];\n const seen = new Set<string>();\n let accepted = 0;\n let projectId: string | null = null;\n let timer: unknown = null;\n let stopped = false;\n\n function schedule(): void {\n if (timer === null && queue.length > 0 && !stopped) {\n timer = setTimer(() => {\n timer = null;\n flush();\n }, FLUSH_INTERVAL_MS);\n }\n }\n\n function sendNext(unloading: boolean): boolean {\n const next = buildReportBody(app, queue);\n\n if (!next) {\n // An error that cannot fit a report on its own; drop it.\n queue.shift();\n\n return queue.length > 0;\n }\n\n queue.splice(0, next.count);\n send(next.body, { unloading })\n .then((status) => {\n // Rate limited or not allowed: stop for this page session rather than retry.\n if (status === 401 || status === 403 || status === 429) {\n stopped = true;\n queue.length = 0;\n }\n })\n .catch(() => {\n // Never report the reporter's own failures.\n });\n\n return queue.length > 0;\n }\n\n function flush({ unloading = false }: { unloading?: boolean } = {}): void {\n try {\n if (stopped) {\n return;\n }\n\n if (timer !== null && unloading) {\n clearTimer(timer);\n timer = null;\n }\n\n // One report per flush, also when the page goes away: beacons and\n // keepalive requests share a 64 KB budget, so a second would be refused.\n if (queue.length > 0) {\n sendNext(unloading);\n }\n\n schedule();\n } catch {\n // Error reporting must never break the Studio.\n }\n }\n\n return {\n report(error, options) {\n try {\n if (stopped || accepted >= MAX_ERRORS_PER_SESSION) {\n return;\n }\n\n const entry = buildClientError(error, {\n ...options,\n now: now(),\n path: getPath?.(),\n projectId,\n });\n\n if (!entry) {\n return;\n }\n\n const key = errorKey(entry);\n\n if (seen.has(key)) {\n return;\n }\n\n seen.add(key);\n accepted += 1;\n queue.push(entry);\n schedule();\n } catch {\n // Error reporting must never break the Studio.\n }\n },\n setProjectId(id) {\n projectId = id;\n },\n flush,\n pending: () => queue,\n dispose() {\n if (timer !== null) {\n clearTimer(timer);\n timer = null;\n }\n\n stopped = true;\n queue.length = 0;\n },\n };\n}\n\n/** Sends a report through the BFF; a beacon when the page is going away on the web. */\nexport const sendReportToBff: SendReport = async (body, { unloading }) => {\n const url = userApiPaths.clientErrors;\n\n // A beacon outlives the page. The BFF reads its JSON body like a fetch's\n // and the browser sends the session cookie with it (same origin). The\n // desktop app's BFF only answers script requests, so it uses `keepalive`.\n if (unloading && !getDesktop() && typeof navigator !== 'undefined' && typeof navigator.sendBeacon === 'function') {\n try {\n // text/plain is CORS-safelisted, so no browser refuses the beacon; the body is still JSON.\n if (navigator.sendBeacon(url, new Blob([body], { type: 'text/plain' }))) {\n return null;\n }\n } catch {\n // Fall back to a keepalive fetch.\n }\n }\n\n return postToUserApi(url, body, { keepalive: unloading });\n};\n\nlet installed: ErrorReporter | null = null;\n\n/** Reports an error caught by the Studio (an error boundary, for example). Does nothing when reporting is off. */\nexport function reportStudioError(\n error: unknown,\n { source = 'manual', componentStack }: { source?: ClientErrorSource; componentStack?: string } = {}\n): void {\n installed?.report(error, { source, componentStack });\n}\n\n/** The project the Studio shows, added to reports; null outside a project. */\nexport function setErrorReportingProjectId(projectId: string | null): void {\n installed?.setProjectId(projectId);\n}\n\n/**\n * Starts reporting uncaught errors and unhandled rejections when reporting is\n * enabled for this environment. Returns the function that stops it.\n */\nexport function installErrorReporter({ hosted }: { hosted: boolean }): () => void {\n if (typeof window === 'undefined' || installed) {\n return () => undefined;\n }\n\n if (!isStudioTelemetryEnabled({ hosted })) {\n return () => undefined;\n }\n\n const desktop = getDesktop();\n\n const reporter = createErrorReporter({\n app: {\n platform: desktop ? 'desktop' : 'web',\n version: STUDIO_VERSION,\n userAgent: truncate(navigator.userAgent, LIMITS.userAgent),\n },\n send: sendReportToBff,\n getPath: () => window.location.pathname,\n });\n\n function onError(event: ErrorEvent): void {\n reporter.report(event.error ?? event.message, { source: 'window', sourceUrl: event.filename });\n }\n\n function onUnhandledRejection(event: PromiseRejectionEvent): void {\n reporter.report(event.reason, { source: 'unhandledrejection' });\n }\n\n function onPageHide(): void {\n reporter.flush({ unloading: true });\n }\n\n window.addEventListener('error', onError);\n window.addEventListener('unhandledrejection', onUnhandledRejection);\n window.addEventListener('pagehide', onPageHide);\n installed = reporter;\n\n return () => {\n window.removeEventListener('error', onError);\n window.removeEventListener('unhandledrejection', onUnhandledRejection);\n window.removeEventListener('pagehide', onPageHide);\n reporter.flush({ unloading: true });\n reporter.dispose();\n\n if (installed === reporter) {\n installed = null;\n }\n };\n}\n","import { userApiPaths } from '../api-paths';\nimport { getDesktop } from '../desktop';\nimport { PROJECT_ID_PATTERN } from '../error/error-reporter';\nimport { isStudioTelemetryEnabled, postToUserApi } from '../telemetry';\n\n/**\n * Product events only the Studio can see, sent to Flexkit through the\n * Studio's own server proxy (`/api/flexkit/-/product-events`) with the\n * session: the Graph app opened, the desktop app launched. They follow the\n * Studio's telemetry setting (see core/telemetry.ts) and carry ids, the\n * desktop app version and the OS only.\n */\nexport type StudioProductEvent =\n | { event: 'graph_opened'; projectId: string }\n | { event: 'desktop_app_opened'; appVersion: string; os: 'darwin' | 'linux' | 'win32' };\n\n/** Identity of an event within a page session; the backend also counts each once per day. */\nexport function productEventKey(studioEvent: StudioProductEvent): string {\n return studioEvent.event === 'graph_opened' ? `graph_opened:${studioEvent.projectId}` : studioEvent.event;\n}\n\n/** Sends the event body; resolves with the response status. */\nexport type SendProductEvent = (body: string) => Promise<number>;\n\nexport interface ProductEventTracker {\n track: (studioEvent: StudioProductEvent, options?: { onDelivered?: () => void }) => void;\n}\n\n/**\n * Sends each event once per page session through `send`. An event counts as\n * sent only once the backend accepted it, so one sent while offline or signed\n * out is sent again next time. Never throws.\n */\nexport function createProductEventTracker(send: SendProductEvent): ProductEventTracker {\n const pending = new Set<string>();\n const delivered = new Set<string>();\n\n return {\n track(studioEvent, { onDelivered } = {}) {\n const key = productEventKey(studioEvent);\n\n if (pending.has(key) || delivered.has(key)) {\n return;\n }\n\n pending.add(key);\n\n const settle = (status: number | null): void => {\n pending.delete(key);\n\n if (status !== null && status >= 200 && status < 300) {\n delivered.add(key);\n onDelivered?.();\n }\n };\n\n try {\n send(JSON.stringify(studioEvent)).then(settle, () => settle(null));\n } catch {\n settle(null);\n }\n },\n };\n}\n\nlet tracker: ProductEventTracker | null = null;\n\n/** Records an event when product events are on (see `installProductEvents`); otherwise does nothing. */\nexport function trackStudioEvent(studioEvent: StudioProductEvent): void {\n if (studioEvent.event === 'graph_opened' && !PROJECT_ID_PATTERN.test(studioEvent.projectId)) {\n return;\n }\n\n tracker?.track(studioEvent);\n}\n\n/** Session storage flag: this desktop app launch was recorded (page reloads keep it, a relaunch does not). */\nexport const DESKTOP_LAUNCH_STORAGE_KEY = 'flexkit:desktop-launch-recorded';\n\nfunction readSessionFlag(key: string): boolean {\n try {\n return sessionStorage.getItem(key) === '1';\n } catch {\n return false;\n }\n}\n\nfunction writeSessionFlag(key: string): void {\n try {\n sessionStorage.setItem(key, '1');\n } catch {\n // Without session storage the backend's once-per-day count still applies.\n }\n}\n\n/**\n * Turns product events on for this environment and records the desktop app\n * launch (once per launch, once the user is signed in). Returns the function\n * that turns them off again.\n */\nexport function installProductEvents({ hosted }: { hosted: boolean }): () => void {\n if (!isStudioTelemetryEnabled({ hosted })) {\n return () => undefined;\n }\n\n const current = createProductEventTracker((body) =>\n postToUserApi(userApiPaths.productEvents, body, { keepalive: true })\n );\n const desktop = getDesktop();\n\n tracker = current;\n\n if (desktop && !readSessionFlag(DESKTOP_LAUNCH_STORAGE_KEY)) {\n current.track(\n { appVersion: desktop.version, event: 'desktop_app_opened', os: desktop.platform },\n { onDelivered: () => writeSessionFlag(DESKTOP_LAUNCH_STORAGE_KEY) }\n );\n }\n\n return () => {\n if (tracker === current) {\n tracker = null;\n }\n };\n}\n"]}
1
+ {"version":3,"sources":["../src/core/use-studio-version.ts","../src/core/telemetry.ts","../src/core/error/error-reporter.ts","../src/core/analytics/product-events.ts"],"names":["STUDIO_VERSION","WEB_VERSION","useStudioVersion","desktop","getDesktop","downloaded","setDownloaded","useState","useEffect","update","TELEMETRY_STORAGE_KEY","LEGACY_TELEMETRY_STORAGE_KEY","LOCAL_HOSTNAME","isTelemetryEnabled","env","readOverride","storage","getLocalStorage","isStudioTelemetryEnabled","hosted","flexkitDomain","postToUserApi","path","body","keepalive","LIMITS","MAX_ERRORS_PER_REPORT","MAX_REPORT_BYTES","MAX_ERRORS_PER_SESSION","FLUSH_INTERVAL_MS","PROJECT_ID_PATTERN","truncate","value","max","toReportPath","pathOrUrl","describeError","error","constructorName","NOISE_MESSAGES","EXTENSION_URL","EMAIL_ADDRESS","maskMessage","message","isNoiseError","details","sourceUrl","pattern","buildClientError","options","entry","errorKey","encoder","byteLength","buildReportBody","app","queue","maxBytes","size","errors","errorSize","createErrorReporter","send","getPath","now","setTimer","callback","ms","clearTimer","timer","seen","accepted","projectId","stopped","schedule","flush","sendNext","unloading","next","status","key","id","sendReportToBff","url","userApiPaths","installed","reportStudioError","source","componentStack","setErrorReportingProjectId","installErrorReporter","reporter","onError","event","onUnhandledRejection","onPageHide","productEventKey","studioEvent","createProductEventTracker","pending","delivered","onDelivered","settle","tracker","trackStudioEvent","DESKTOP_LAUNCH_STORAGE_KEY","readSessionFlag","writeSessionFlag","installProductEvents","current"],"mappings":"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","file":"chunk-R6DPAMLC.js","sourcesContent":["'use client';\n\nimport { useEffect, useState } from 'react';\nimport { getDesktop } from './desktop';\n\nexport interface StudioVersion {\n current: string;\n latest: string;\n isCurrent: boolean;\n /** Applies the newer version (the desktop app restarts into it); undefined when there is nothing to apply. */\n upgrade?: () => void;\n}\n\n// `@flexkit/studio`'s own version, inlined at build time; a build without the\n// define (tests, tooling) shows a placeholder.\nexport const STUDIO_VERSION = typeof __STUDIO_VERSION__ === 'string' ? __STUDIO_VERSION__ : '0.0.0';\nconst WEB_VERSION: StudioVersion = { current: STUDIO_VERSION, latest: STUDIO_VERSION, isCurrent: true };\n\n/**\n * The Studio version shown in the app bar. The web Studio is always current\n * (the hosted Studio deploys with the packages); the desktop app reports its\n * own version and the update it has downloaded, if any.\n */\nexport function useStudioVersion(): StudioVersion {\n const desktop = getDesktop();\n const [downloaded, setDownloaded] = useState<string | null>(null);\n\n useEffect(() => {\n if (!desktop) {\n return;\n }\n\n return desktop.onUpdateDownloaded((update) => {\n setDownloaded(update.version);\n });\n }, [desktop]);\n\n if (!desktop) {\n return WEB_VERSION;\n }\n\n if (downloaded === null) {\n return { current: desktop.version, latest: desktop.version, isCurrent: true };\n }\n\n return { current: desktop.version, latest: downloaded, isCurrent: false, upgrade: () => desktop.installUpdate() };\n}\n","import { getDesktop } from './desktop';\nimport { flexkitDomain } from './domains';\nimport { getLocalStorage } from './local-storage';\nimport { STUDIO_VERSION } from './use-studio-version';\n\n/**\n * The on/off decision and the transport shared by the Studio's error reports\n * (core/error/error-reporter.ts) and product events\n * (core/analytics/product-events.ts).\n */\n\n/** Turns error reports and product events on (`on`) or off (`off`) in this browser, whatever the environment. */\nexport const TELEMETRY_STORAGE_KEY = 'flexkit:telemetry';\n/** The key error reporting shipped with; still honoured. */\nexport const LEGACY_TELEMETRY_STORAGE_KEY = 'flexkit:error-reporting';\n\nexport interface TelemetryEnvironment {\n /** The Studio lists the signed-in user's projects (hosted Studio, desktop app). */\n hosted: boolean;\n isDesktop: boolean;\n domain: string;\n hostname: string;\n version: string;\n /** The local storage setting in this browser, if any. */\n override: string | null;\n}\n\nconst LOCAL_HOSTNAME = /^(localhost|127\\.0\\.0\\.1|\\[::1\\]|0\\.0\\.0\\.0)$|\\.(localhost|test|local)$/i;\n\n/**\n * On for the hosted Studio and the desktop app against the production\n * backend, and off in development: a local backend (`flexkit.test`), a local\n * host, or a build without a version. The local storage setting forces it\n * `on` or `off`.\n */\nexport function isTelemetryEnabled(env: TelemetryEnvironment): boolean {\n if (env.override === 'on') {\n return true;\n }\n\n if (env.override === 'off') {\n return false;\n }\n\n if (!env.hosted || env.domain !== 'flexkit.io' || env.version === '0.0.0') {\n return false;\n }\n\n return env.isDesktop || !LOCAL_HOSTNAME.test(env.hostname);\n}\n\nfunction readOverride(): string | null {\n try {\n const storage = getLocalStorage();\n\n return storage?.getItem(TELEMETRY_STORAGE_KEY) ?? storage?.getItem(LEGACY_TELEMETRY_STORAGE_KEY) ?? null;\n } catch {\n return null;\n }\n}\n\n/** Whether this Studio sends error reports and product events to Flexkit. */\nexport function isStudioTelemetryEnabled({ hosted }: { hosted: boolean }): boolean {\n if (typeof window === 'undefined') {\n return false;\n }\n\n return isTelemetryEnabled({\n hosted,\n isDesktop: Boolean(getDesktop()),\n domain: flexkitDomain,\n hostname: window.location.hostname,\n version: STUDIO_VERSION,\n override: readOverride(),\n });\n}\n\n/**\n * POSTs JSON to a user-level backend route through the Studio's server proxy,\n * which adds the session. `keepalive` lets the request outlive the page.\n * Returns the response status.\n */\nexport async function postToUserApi(path: string, body: string, { keepalive = false } = {}): Promise<number> {\n const response = await fetch(path, {\n body,\n credentials: 'same-origin',\n headers: { 'Content-Type': 'application/json' },\n keepalive,\n method: 'POST',\n });\n\n return response.status;\n}\n","import { userApiPaths } from '../api-paths';\nimport { getDesktop } from '../desktop';\nimport { STUDIO_VERSION } from '../use-studio-version';\nimport { isStudioTelemetryEnabled, postToUserApi } from '../telemetry';\n\n/**\n * Reports unexpected Studio errors (uncaught errors, unhandled rejections and\n * errors caught by the error boundaries) to Flexkit through the Studio's own\n * BFF (`/api/flexkit/-/client-errors`), which forwards the session so the\n * report is attributed to the signed-in user.\n *\n * Reports carry the error, the Studio path (no query string or hash) and the\n * current project id; never request bodies, form values or record data.\n */\n\nexport type ClientErrorSource = 'window' | 'unhandledrejection' | 'react' | 'manual';\n\nexport interface ClientError {\n source: ClientErrorSource;\n name: string;\n message: string;\n stack?: string;\n componentStack?: string;\n path?: string;\n projectId?: string;\n occurredAt: string;\n}\n\nexport interface ClientErrorApp {\n platform: 'web' | 'desktop';\n version: string;\n userAgent?: string;\n}\n\nexport interface ClientErrorReport {\n app: ClientErrorApp;\n errors: ClientError[];\n}\n\n/** Field limits of the backend schema. */\nconst LIMITS = {\n componentStack: 4_000,\n message: 2_000,\n name: 200,\n path: 500,\n stack: 8_000,\n userAgent: 500,\n} as const;\n\n/** The backend accepts 1 to 10 errors per report. */\nexport const MAX_ERRORS_PER_REPORT = 10;\n/** The backend refuses bodies over 64 KB; `keepalive` requests share a 64 KB budget too. */\nexport const MAX_REPORT_BYTES = 60_000;\n/** Reports kept per page session; anything after that is dropped. */\nexport const MAX_ERRORS_PER_SESSION = 50;\n/** At most one report is sent per interval; errors in between are batched. */\nexport const FLUSH_INTERVAL_MS = 5_000;\n\n/** Kept for callers and tests that name the gate after error reporting. */\nexport {\n isTelemetryEnabled as isErrorReportingEnabled,\n LEGACY_TELEMETRY_STORAGE_KEY as ERROR_REPORTING_STORAGE_KEY,\n type TelemetryEnvironment as ErrorReportingEnvironment,\n} from '../telemetry';\n\nexport const PROJECT_ID_PATTERN = /^[a-z0-9]{1,64}$/;\n\nfunction truncate(value: string, max: number): string {\n return value.length > max ? value.slice(0, max) : value;\n}\n\n/** The pathname of a path or URL, without query string or hash. */\nexport function toReportPath(pathOrUrl: string): string {\n let path = pathOrUrl;\n\n try {\n if (/^[a-z][a-z0-9+.-]*:/i.test(pathOrUrl)) {\n path = new URL(pathOrUrl).pathname;\n }\n } catch {\n // Not a URL; strip by hand below.\n }\n\n return truncate(path.split(/[?#]/)[0] ?? '', LIMITS.path);\n}\n\nexport interface ErrorDetails {\n name: string;\n message: string;\n stack?: string;\n}\n\n/**\n * Name, message and stack of anything thrown. A non-Error value (a rejected\n * promise with a plain object) is described by its type only, so record data\n * someone rejected with never leaves the browser.\n */\nexport function describeError(error: unknown): ErrorDetails {\n if (error instanceof Error) {\n return {\n name: error.name || 'Error',\n message: typeof error.message === 'string' ? error.message : '',\n stack: typeof error.stack === 'string' ? error.stack : undefined,\n };\n }\n\n if (typeof error === 'string') {\n return { name: 'Error', message: error };\n }\n\n if (error === null || error === undefined) {\n return { name: 'Error', message: `Non-Error thrown: ${String(error)}` };\n }\n\n if (typeof error === 'object') {\n const constructorName = (error as { constructor?: { name?: unknown } }).constructor?.name;\n\n return {\n name: 'Error',\n message: `Non-Error thrown: ${typeof constructorName === 'string' && constructorName ? constructorName : 'object'}`,\n };\n }\n\n return { name: 'Error', message: `Non-Error thrown: ${typeof error}` };\n}\n\nconst NOISE_MESSAGES = [\n /ResizeObserver loop (limit exceeded|completed with undelivered notifications)/i,\n // Cross-origin script errors carry nothing to act on.\n /^Script error\\.?$/i,\n /The (operation|user) (was )?aborted/i,\n /signal is aborted without reason/i,\n];\n\nconst EXTENSION_URL = /\\b(chrome|moz|safari(-web)?|ms-browser)-extension:\\/\\//i;\n\nconst EMAIL_ADDRESS = /[A-Z0-9._%+-]+@[A-Z0-9.-]+\\.[A-Z]{2,}/gi;\n\n/** Masks email addresses an error message may quote from a record or form. */\nexport function maskMessage(message: string): string {\n return message.replace(EMAIL_ADDRESS, '<email>');\n}\n\n/** Errors that are not Studio bugs: browser quirks, aborted requests, browser extensions. */\nexport function isNoiseError(details: ErrorDetails, sourceUrl?: string): boolean {\n if (details.name === 'AbortError') {\n return true;\n }\n\n if (NOISE_MESSAGES.some((pattern) => pattern.test(details.message))) {\n return true;\n }\n\n if (sourceUrl && EXTENSION_URL.test(sourceUrl)) {\n return true;\n }\n\n return Boolean(details.stack && EXTENSION_URL.test(details.stack));\n}\n\nexport interface BuildClientErrorOptions {\n source: ClientErrorSource;\n componentStack?: string;\n path?: string;\n projectId?: string | null;\n /** The script the error came from (`ErrorEvent.filename`), checked for browser extensions. */\n sourceUrl?: string;\n now?: Date;\n}\n\n/** The report entry for an error, or null when it is noise. */\nexport function buildClientError(error: unknown, options: BuildClientErrorOptions): ClientError | null {\n const details = describeError(error);\n\n if (isNoiseError(details, options.sourceUrl)) {\n return null;\n }\n\n const entry: ClientError = {\n source: options.source,\n name: truncate(details.name, LIMITS.name),\n message: truncate(maskMessage(details.message), LIMITS.message),\n occurredAt: (options.now ?? new Date()).toISOString(),\n };\n\n if (details.stack) {\n entry.stack = truncate(details.stack, LIMITS.stack);\n }\n\n if (options.componentStack) {\n entry.componentStack = truncate(options.componentStack, LIMITS.componentStack);\n }\n\n if (options.path) {\n entry.path = toReportPath(options.path);\n }\n\n if (options.projectId && PROJECT_ID_PATTERN.test(options.projectId)) {\n entry.projectId = options.projectId;\n }\n\n return entry;\n}\n\n/** Identity of an error within a page session: the same message and stack are reported once. */\nexport function errorKey(error: Pick<ClientError, 'message' | 'name' | 'stack'>): string {\n return `${error.name}\\n${error.message}\\n${error.stack ?? ''}`;\n}\n\nconst encoder = new TextEncoder();\n\nfunction byteLength(value: string): number {\n return encoder.encode(value).length;\n}\n\n/**\n * Takes the errors that fit one report (at most 10 and `maxBytes` of JSON)\n * off the front of the queue. Returns the request body and how many errors it holds.\n */\nexport function buildReportBody(\n app: ClientErrorApp,\n queue: readonly ClientError[],\n maxBytes = MAX_REPORT_BYTES\n): { body: string; count: number } | null {\n // The envelope plus each error once, joined by commas.\n let size = byteLength(JSON.stringify({ app, errors: [] } satisfies ClientErrorReport));\n const errors: ClientError[] = [];\n\n for (const error of queue.slice(0, MAX_ERRORS_PER_REPORT)) {\n const errorSize = byteLength(JSON.stringify(error)) + (errors.length ? 1 : 0);\n\n if (size + errorSize > maxBytes) {\n break;\n }\n\n size += errorSize;\n errors.push(error);\n }\n\n if (errors.length === 0) {\n return null;\n }\n\n return { body: JSON.stringify({ app, errors } satisfies ClientErrorReport), count: errors.length };\n}\n\nexport type SendReport = (body: string, options: { unloading: boolean }) => Promise<number | null>;\n\nexport interface ErrorReporterOptions {\n app: ClientErrorApp;\n send: SendReport;\n /** The current Studio path; only its pathname is sent. */\n getPath?: () => string | undefined;\n now?: () => Date;\n setTimer?: (callback: () => void, ms: number) => unknown;\n clearTimer?: (timer: unknown) => void;\n}\n\nexport interface ErrorReporter {\n report: (error: unknown, options: Omit<BuildClientErrorOptions, 'path' | 'projectId' | 'now'>) => void;\n setProjectId: (projectId: string | null) => void;\n /** Sends the next report now; `unloading` uses a beacon or keepalive request that outlives the page. */\n flush: (options?: { unloading?: boolean }) => void;\n /** Errors waiting to be sent (tests). */\n pending: () => readonly ClientError[];\n dispose: () => void;\n}\n\n/** The queue, dedupe, cap and batching; the transport and the clock are injected. */\nexport function createErrorReporter({\n app,\n send,\n getPath,\n now = () => new Date(),\n setTimer = (callback, ms) => setTimeout(callback, ms),\n clearTimer = (timer) => clearTimeout(timer as ReturnType<typeof setTimeout>),\n}: ErrorReporterOptions): ErrorReporter {\n const queue: ClientError[] = [];\n const seen = new Set<string>();\n let accepted = 0;\n let projectId: string | null = null;\n let timer: unknown = null;\n let stopped = false;\n\n function schedule(): void {\n if (timer === null && queue.length > 0 && !stopped) {\n timer = setTimer(() => {\n timer = null;\n flush();\n }, FLUSH_INTERVAL_MS);\n }\n }\n\n function sendNext(unloading: boolean): boolean {\n const next = buildReportBody(app, queue);\n\n if (!next) {\n // An error that cannot fit a report on its own; drop it.\n queue.shift();\n\n return queue.length > 0;\n }\n\n queue.splice(0, next.count);\n send(next.body, { unloading })\n .then((status) => {\n // Rate limited or not allowed: stop for this page session rather than retry.\n if (status === 401 || status === 403 || status === 429) {\n stopped = true;\n queue.length = 0;\n }\n })\n .catch(() => {\n // Never report the reporter's own failures.\n });\n\n return queue.length > 0;\n }\n\n function flush({ unloading = false }: { unloading?: boolean } = {}): void {\n try {\n if (stopped) {\n return;\n }\n\n if (timer !== null && unloading) {\n clearTimer(timer);\n timer = null;\n }\n\n // One report per flush, also when the page goes away: beacons and\n // keepalive requests share a 64 KB budget, so a second would be refused.\n if (queue.length > 0) {\n sendNext(unloading);\n }\n\n schedule();\n } catch {\n // Error reporting must never break the Studio.\n }\n }\n\n return {\n report(error, options) {\n try {\n if (stopped || accepted >= MAX_ERRORS_PER_SESSION) {\n return;\n }\n\n const entry = buildClientError(error, {\n ...options,\n now: now(),\n path: getPath?.(),\n projectId,\n });\n\n if (!entry) {\n return;\n }\n\n const key = errorKey(entry);\n\n if (seen.has(key)) {\n return;\n }\n\n seen.add(key);\n accepted += 1;\n queue.push(entry);\n schedule();\n } catch {\n // Error reporting must never break the Studio.\n }\n },\n setProjectId(id) {\n projectId = id;\n },\n flush,\n pending: () => queue,\n dispose() {\n if (timer !== null) {\n clearTimer(timer);\n timer = null;\n }\n\n stopped = true;\n queue.length = 0;\n },\n };\n}\n\n/** Sends a report through the BFF; a beacon when the page is going away on the web. */\nexport const sendReportToBff: SendReport = async (body, { unloading }) => {\n const url = userApiPaths.clientErrors;\n\n // A beacon outlives the page. The BFF reads its JSON body like a fetch's\n // and the browser sends the session cookie with it (same origin). The\n // desktop app's BFF only answers script requests, so it uses `keepalive`.\n if (unloading && !getDesktop() && typeof navigator !== 'undefined' && typeof navigator.sendBeacon === 'function') {\n try {\n // text/plain is CORS-safelisted, so no browser refuses the beacon; the body is still JSON.\n if (navigator.sendBeacon(url, new Blob([body], { type: 'text/plain' }))) {\n return null;\n }\n } catch {\n // Fall back to a keepalive fetch.\n }\n }\n\n return postToUserApi(url, body, { keepalive: unloading });\n};\n\nlet installed: ErrorReporter | null = null;\n\n/** Reports an error caught by the Studio (an error boundary, for example). Does nothing when reporting is off. */\nexport function reportStudioError(\n error: unknown,\n { source = 'manual', componentStack }: { source?: ClientErrorSource; componentStack?: string } = {}\n): void {\n installed?.report(error, { source, componentStack });\n}\n\n/** The project the Studio shows, added to reports; null outside a project. */\nexport function setErrorReportingProjectId(projectId: string | null): void {\n installed?.setProjectId(projectId);\n}\n\n/**\n * Starts reporting uncaught errors and unhandled rejections when reporting is\n * enabled for this environment. Returns the function that stops it.\n */\nexport function installErrorReporter({ hosted }: { hosted: boolean }): () => void {\n if (typeof window === 'undefined' || installed) {\n return () => undefined;\n }\n\n if (!isStudioTelemetryEnabled({ hosted })) {\n return () => undefined;\n }\n\n const desktop = getDesktop();\n\n const reporter = createErrorReporter({\n app: {\n platform: desktop ? 'desktop' : 'web',\n version: STUDIO_VERSION,\n userAgent: truncate(navigator.userAgent, LIMITS.userAgent),\n },\n send: sendReportToBff,\n getPath: () => window.location.pathname,\n });\n\n function onError(event: ErrorEvent): void {\n reporter.report(event.error ?? event.message, { source: 'window', sourceUrl: event.filename });\n }\n\n function onUnhandledRejection(event: PromiseRejectionEvent): void {\n reporter.report(event.reason, { source: 'unhandledrejection' });\n }\n\n function onPageHide(): void {\n reporter.flush({ unloading: true });\n }\n\n window.addEventListener('error', onError);\n window.addEventListener('unhandledrejection', onUnhandledRejection);\n window.addEventListener('pagehide', onPageHide);\n installed = reporter;\n\n return () => {\n window.removeEventListener('error', onError);\n window.removeEventListener('unhandledrejection', onUnhandledRejection);\n window.removeEventListener('pagehide', onPageHide);\n reporter.flush({ unloading: true });\n reporter.dispose();\n\n if (installed === reporter) {\n installed = null;\n }\n };\n}\n","import { userApiPaths } from '../api-paths';\nimport { getDesktop } from '../desktop';\nimport { PROJECT_ID_PATTERN } from '../error/error-reporter';\nimport { isStudioTelemetryEnabled, postToUserApi } from '../telemetry';\n\n/**\n * Product events only the Studio can see, sent to Flexkit through the\n * Studio's own server proxy (`/api/flexkit/-/product-events`) with the\n * session: the Graph app opened, the desktop app launched. They follow the\n * Studio's telemetry setting (see core/telemetry.ts) and carry ids, the\n * desktop app version and the OS only.\n */\nexport type StudioProductEvent =\n | { event: 'graph_opened'; projectId: string }\n | { event: 'desktop_app_opened'; appVersion: string; os: 'darwin' | 'linux' | 'win32' };\n\n/** Identity of an event within a page session; the backend also counts each once per day. */\nexport function productEventKey(studioEvent: StudioProductEvent): string {\n return studioEvent.event === 'graph_opened' ? `graph_opened:${studioEvent.projectId}` : studioEvent.event;\n}\n\n/** Sends the event body; resolves with the response status. */\nexport type SendProductEvent = (body: string) => Promise<number>;\n\nexport interface ProductEventTracker {\n track: (studioEvent: StudioProductEvent, options?: { onDelivered?: () => void }) => void;\n}\n\n/**\n * Sends each event once per page session through `send`. An event counts as\n * sent only once the backend accepted it, so one sent while offline or signed\n * out is sent again next time. Never throws.\n */\nexport function createProductEventTracker(send: SendProductEvent): ProductEventTracker {\n const pending = new Set<string>();\n const delivered = new Set<string>();\n\n return {\n track(studioEvent, { onDelivered } = {}) {\n const key = productEventKey(studioEvent);\n\n if (pending.has(key) || delivered.has(key)) {\n return;\n }\n\n pending.add(key);\n\n const settle = (status: number | null): void => {\n pending.delete(key);\n\n if (status !== null && status >= 200 && status < 300) {\n delivered.add(key);\n onDelivered?.();\n }\n };\n\n try {\n send(JSON.stringify(studioEvent)).then(settle, () => settle(null));\n } catch {\n settle(null);\n }\n },\n };\n}\n\nlet tracker: ProductEventTracker | null = null;\n\n/** Records an event when product events are on (see `installProductEvents`); otherwise does nothing. */\nexport function trackStudioEvent(studioEvent: StudioProductEvent): void {\n if (studioEvent.event === 'graph_opened' && !PROJECT_ID_PATTERN.test(studioEvent.projectId)) {\n return;\n }\n\n tracker?.track(studioEvent);\n}\n\n/** Session storage flag: this desktop app launch was recorded (page reloads keep it, a relaunch does not). */\nexport const DESKTOP_LAUNCH_STORAGE_KEY = 'flexkit:desktop-launch-recorded';\n\nfunction readSessionFlag(key: string): boolean {\n try {\n return sessionStorage.getItem(key) === '1';\n } catch {\n return false;\n }\n}\n\nfunction writeSessionFlag(key: string): void {\n try {\n sessionStorage.setItem(key, '1');\n } catch {\n // Without session storage the backend's once-per-day count still applies.\n }\n}\n\n/**\n * Turns product events on for this environment and records the desktop app\n * launch (once per launch, once the user is signed in). Returns the function\n * that turns them off again.\n */\nexport function installProductEvents({ hosted }: { hosted: boolean }): () => void {\n if (!isStudioTelemetryEnabled({ hosted })) {\n return () => undefined;\n }\n\n const current = createProductEventTracker((body) =>\n postToUserApi(userApiPaths.productEvents, body, { keepalive: true })\n );\n const desktop = getDesktop();\n\n tracker = current;\n\n if (desktop && !readSessionFlag(DESKTOP_LAUNCH_STORAGE_KEY)) {\n current.track(\n { appVersion: desktop.version, event: 'desktop_app_opened', os: desktop.platform },\n { onDelivered: () => writeSessionFlag(DESKTOP_LAUNCH_STORAGE_KEY) }\n );\n }\n\n return () => {\n if (tracker === current) {\n tracker = null;\n }\n };\n}\n"]}