@burdenoff/microfe-devportal 2026.629.9 → 2026.629.11
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.
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SectionNav.js","names":[],"sources":["../../../src/devportal/components/SectionNav.tsx"],"sourcesContent":["import type { FC } from \"react\";\nimport { useEffect, useRef, useState } from \"react\";\nimport { useLocation } from \"react-router-dom\";\nimport {\n LayoutDashboard,\n AppWindow,\n Building2,\n KeyRound,\n Key,\n Webhook,\n Share2,\n BarChart3,\n DollarSign,\n BookOpen,\n Library,\n User,\n Settings,\n ChevronDown,\n Check,\n} from \"lucide-react\";\nimport { useI18n } from \"@burdenoff/fe-libs/shared/providers/shell/I18nProvider\";\nimport { useDevportal } from \"../providers/DevportalProvider\";\nimport {\n useGetMyOrganizationsQuery,\n type GetMyOrganizationsQuery,\n} from \"../hooks/useGraphQL\";\nimport { cn } from \"../utils/cn\";\n\ntype OrgItem = GetMyOrganizationsQuery[\"myDeveloperOrganizations\"][number];\n\ninterface SectionDef {\n path: string;\n i18nKey: string;\n fallback: string;\n icon: typeof LayoutDashboard;\n /** Additional sub-paths that should mark this section as active. */\n matchPrefixes?: string[];\n}\n\nconst SECTIONS: ReadonlyArray<SectionDef> = [\n {\n path: \"\",\n i18nKey: \"devportal.nav.dashboard\",\n fallback: \"Dashboard\",\n icon: LayoutDashboard,\n },\n {\n path: \"apps\",\n i18nKey: \"devportal.nav.apps\",\n fallback: \"Apps\",\n icon: AppWindow,\n },\n {\n path: \"organizations\",\n i18nKey: \"devportal.nav.organizations\",\n fallback: \"Organizations\",\n icon: Building2,\n },\n {\n path: \"oauth\",\n i18nKey: \"devportal.nav.oauth\",\n fallback: \"OAuth Clients\",\n icon: KeyRound,\n },\n {\n path: \"api-keys\",\n i18nKey: \"devportal.nav.apiKeys\",\n fallback: \"API Keys\",\n icon: Key,\n },\n {\n path: \"webhooks\",\n i18nKey: \"devportal.nav.webhooks\",\n fallback: \"Webhooks\",\n icon: Webhook,\n },\n {\n path: \"delegations\",\n i18nKey: \"devportal.nav.delegations\",\n fallback: \"Delegations\",\n icon: Share2,\n },\n {\n path: \"analytics\",\n i18nKey: \"devportal.nav.analytics\",\n fallback: \"Analytics\",\n icon: BarChart3,\n },\n {\n path: \"revenue\",\n i18nKey: \"devportal.nav.revenue\",\n fallback: \"Revenue\",\n icon: DollarSign,\n },\n {\n path: \"docs\",\n i18nKey: \"devportal.nav.docs\",\n fallback: \"Docs\",\n icon: BookOpen,\n },\n {\n path: \"resources\",\n i18nKey: \"devportal.nav.resources\",\n fallback: \"Resources\",\n icon: Library,\n },\n {\n path: \"publisher/profile\",\n i18nKey: \"devportal.nav.publisher\",\n fallback: \"Publisher\",\n icon: User,\n matchPrefixes: [\"publisher\"],\n },\n {\n path: \"settings\",\n i18nKey: \"devportal.nav.settings\",\n fallback: \"Settings\",\n icon: Settings,\n },\n];\n\nfunction joinBaseAndPath(basePath: string | undefined, path: string): string {\n const base = (basePath ?? \"\").replace(/\\/$/, \"\");\n if (!path) return base || \"/\";\n const suffix = path.startsWith(\"/\") ? path : `/${path}`;\n return `${base}${suffix}`;\n}\n\nfunction isSectionActive(\n pathname: string,\n basePath: string | undefined,\n section: SectionDef,\n): boolean {\n const sectionUrl = joinBaseAndPath(basePath, section.path);\n // Index/dashboard is active only when exactly matched (or with empty extra).\n if (section.path === \"\") {\n return pathname === sectionUrl || pathname === `${sectionUrl}/`;\n }\n const candidates = [section.path, ...(section.matchPrefixes ?? [])];\n return candidates.some((prefix) => {\n const url = joinBaseAndPath(basePath, prefix);\n return pathname === url || pathname.startsWith(`${url}/`);\n });\n}\n\n/** Org context switcher — can be embedded standalone or inside SectionNav. */\nexport const OrgSwitcher: FC = () => {\n const { activeOrgId, setActiveOrgId } = useDevportal();\n const { t } = useI18n();\n const tr = (key: string, fallback: string): string => {\n const value = t(key);\n return value === key ? fallback : value;\n };\n const [open, setOpen] = useState(false);\n const ref = useRef<HTMLDivElement>(null);\n\n const { data } = useGetMyOrganizationsQuery({ fetchPolicy: \"cache-first\" });\n const orgs: OrgItem[] = data?.myDeveloperOrganizations ?? [];\n\n // Auto-select when there's exactly one org and nothing is stored yet\n useEffect(() => {\n if (!activeOrgId && orgs.length === 1 && orgs[0]?.id) {\n setActiveOrgId(orgs[0].id);\n }\n }, [orgs, activeOrgId, setActiveOrgId]);\n\n // Close on outside click\n useEffect(() => {\n const handler = (e: MouseEvent) => {\n if (ref.current && !ref.current.contains(e.target as Node)) {\n setOpen(false);\n }\n };\n if (open) document.addEventListener(\"mousedown\", handler);\n return () => document.removeEventListener(\"mousedown\", handler);\n }, [open]);\n\n if (orgs.length === 0) return null;\n\n const activeOrg = orgs.find((o) => o.id === activeOrgId);\n const label = activeOrg?.name ?? tr(\"devportal.nav.selectOrg\", \"Select org\");\n\n return (\n <div ref={ref} className=\"relative flex-shrink-0\">\n <button\n type=\"button\"\n onClick={() => setOpen((v) => !v)}\n className={cn(\n \"inline-flex items-center gap-1.5 rounded-md border px-3 py-1.5 text-sm font-medium transition-colors\",\n \"bg-bg-surface text-text-primary border-border-subtle hover:bg-accent\",\n )}\n aria-label={tr(\"devportal.nav.switchOrg\", \"Switch organization\")}\n >\n <Building2 size={13} className=\"text-primary flex-shrink-0\" />\n <span className=\"max-w-[140px] truncate\">{label}</span>\n <ChevronDown\n size={13}\n className={cn(\"transition-transform\", open && \"rotate-180\")}\n />\n </button>\n\n {open && (\n <div className=\"absolute right-0 top-full z-50 mt-1 min-w-[200px] rounded-md border border-border-subtle bg-popover shadow-lg py-1\">\n {orgs.map((org) => (\n <button\n key={org.id}\n type=\"button\"\n onClick={() => {\n setActiveOrgId(org.id);\n setOpen(false);\n }}\n className={cn(\n \"flex w-full items-center gap-2 px-3 py-2 text-sm text-left transition-colors hover:bg-accent\",\n org.id === activeOrgId\n ? \"text-primary font-medium\"\n : \"text-text-primary\",\n )}\n >\n <Check\n size={13}\n className={cn(\n \"flex-shrink-0\",\n org.id === activeOrgId\n ? \"opacity-100 text-primary\"\n : \"opacity-0\",\n )}\n />\n <span className=\"truncate\">{org.name}</span>\n </button>\n ))}\n </div>\n )}\n </div>\n );\n};\n\n/**\n * Horizontal section nav rendered above the page content on every devportal\n * route. Gives developers a one-click jump between all top-level sections so\n * routes like OAuth, Delegations, Analytics, Revenue, Docs, Resources,\n * Publisher Profile, and Settings are reachable without typing URLs.\n *\n * Renders nothing when `basePath` is undefined (standalone/dev mode) so it\n * doesn't conflict with whatever harness wraps the MFE there.\n */\nexport const SectionNav: FC = () => {\n const { navigate, basePath } = useDevportal();\n const { pathname } = useLocation();\n const { t } = useI18n();\n const tr = (key: string, fallback: string): string => {\n const value = t(key);\n return value === key ? fallback : value;\n };\n\n if (!basePath || !navigate) return null;\n\n const handleClick = (path: string) => {\n navigate(joinBaseAndPath(basePath, path));\n };\n\n return (\n <nav\n aria-label={tr(\"devportal.nav.sections\", \"Developer portal sections\")}\n className=\"border-b border-border-subtle bg-bg-surface/40 px-4 py-2\"\n data-testid=\"devportal-section-nav\"\n >\n <div className=\"flex items-center gap-2\">\n <ul className=\"flex flex-1 flex-wrap gap-1\">\n {SECTIONS.map((section) => {\n const active = isSectionActive(pathname, basePath, section);\n const Icon = section.icon;\n return (\n <li key={section.path}>\n <button\n type=\"button\"\n onClick={() => handleClick(section.path)}\n aria-current={active ? \"page\" : undefined}\n className={cn(\n \"inline-flex items-center gap-1.5 rounded-md px-3 py-1.5 text-sm font-medium transition-colors\",\n active\n ? \"bg-action-primary-bg text-action-primary-text\"\n : \"text-text-secondary hover:bg-accent hover:text-text-primary\",\n )}\n >\n <Icon size={14} />\n <span>{tr(section.i18nKey, section.fallback)}</span>\n </button>\n </li>\n );\n })}\n </ul>\n <OrgSwitcher />\n </div>\n </nav>\n );\n};\n\n// Exported for unit tests that want to assert against the canonical list.\nexport const __SECTION_NAV_SECTIONS = SECTIONS;\n"],"mappings":";;;;;;;;;;AAkJA,IAAa,UAAwB;CACnC,IAAM,EAAE,gBAAa,sBAAmB,GAAc,EAChD,EAAE,SAAM,GAAS,EACjB,KAAM,GAAa,MAA6B;EACpD,IAAM,IAAQ,EAAE,EAAI;AACpB,SAAO,MAAU,IAAM,IAAW;IAE9B,CAAC,GAAM,KAAW,EAAS,GAAM,EACjC,IAAM,EAAuB,KAAK,EAElC,EAAE,YAAS,EAA2B,EAAE,aAAa,eAAe,CAAC,EACrE,IAAkB,GAAM,4BAA4B,EAAE;AAoB5D,KAjBA,QAAgB;AACd,EAAI,CAAC,KAAe,EAAK,WAAW,KAAK,EAAK,IAAI,MAChD,EAAe,EAAK,GAAG,GAAG;IAE3B;EAAC;EAAM;EAAa;EAAe,CAAC,EAGvC,QAAgB;EACd,IAAM,KAAW,MAAkB;AACjC,GAAI,EAAI,WAAW,CAAC,EAAI,QAAQ,SAAS,EAAE,OAAe,IACxD,EAAQ,GAAM;;AAIlB,SADI,KAAM,SAAS,iBAAiB,aAAa,EAAQ,QAC5C,SAAS,oBAAoB,aAAa,EAAQ;IAC9D,CAAC,EAAK,CAAC,EAEN,EAAK,WAAW,EAAG,QAAO;CAG9B,IAAM,IADY,EAAK,MAAM,MAAM,EAAE,OAAO,EAAY,EAC/B,QAAQ,EAAG,2BAA2B,aAAa;AAE5E,QACE,kBAAC,OAAD;EAAU;EAAK,WAAU;YAAzB,CACE,kBAAC,UAAD;GACE,MAAK;GACL,eAAe,GAAS,MAAM,CAAC,EAAE;GACjC,WAAW,EACT,wGACA,uEACD;GACD,cAAY,EAAG,2BAA2B,sBAAsB;aAPlE;IASE,kBAAC,GAAD;KAAW,MAAM;KAAI,WAAU;KAA+B,CAAA;IAC9D,kBAAC,QAAD;KAAM,WAAU;eAA0B;KAAa,CAAA;IACvD,kBAAC,GAAD;KACE,MAAM;KACN,WAAW,EAAG,wBAAwB,KAAQ,aAAa;KAC3D,CAAA;IACK;MAER,KACC,kBAAC,OAAD;GAAK,WAAU;aACZ,EAAK,KAAK,MACT,kBAAC,UAAD;IAEE,MAAK;IACL,eAAe;AAEb,KADA,EAAe,EAAI,GAAG,EACtB,EAAQ,GAAM;;IAEhB,WAAW,EACT,gGACA,EAAI,OAAO,IACP,6BACA,oBACL;cAZH,CAcE,kBAAC,GAAD;KACE,MAAM;KACN,WAAW,EACT,iBACA,EAAI,OAAO,IACP,6BACA,YACL;KACD,CAAA,EACF,kBAAC,QAAD;KAAM,WAAU;eAAY,EAAI;KAAY,CAAA,CACrC;MAvBF,EAAI,GAuBF,CACT;GACE,CAAA,CAEJ"}
|
|
1
|
+
{"version":3,"file":"SectionNav.js","names":[],"sources":["../../../src/devportal/components/SectionNav.tsx"],"sourcesContent":["import type { FC } from \"react\";\nimport { useEffect, useRef, useState } from \"react\";\nimport { useLocation } from \"react-router-dom\";\nimport {\n LayoutDashboard,\n AppWindow,\n Building2,\n Share2,\n DollarSign,\n BookOpen,\n Library,\n User,\n Settings,\n ChevronDown,\n Check,\n} from \"lucide-react\";\nimport { useI18n } from \"@burdenoff/fe-libs/shared/providers/shell/I18nProvider\";\nimport { useDevportal } from \"../providers/DevportalProvider\";\nimport {\n useGetMyOrganizationsQuery,\n type GetMyOrganizationsQuery,\n} from \"../hooks/useGraphQL\";\nimport { cn } from \"../utils/cn\";\n\ntype OrgItem = GetMyOrganizationsQuery[\"myDeveloperOrganizations\"][number];\n\ninterface SectionDef {\n path: string;\n i18nKey: string;\n fallback: string;\n icon: typeof LayoutDashboard;\n /** Additional sub-paths that should mark this section as active. */\n matchPrefixes?: string[];\n}\n\nconst SECTIONS: ReadonlyArray<SectionDef> = [\n {\n path: \"\",\n i18nKey: \"devportal.nav.dashboard\",\n fallback: \"Dashboard\",\n icon: LayoutDashboard,\n },\n {\n path: \"apps\",\n i18nKey: \"devportal.nav.apps\",\n fallback: \"Apps\",\n icon: AppWindow,\n },\n {\n path: \"organizations\",\n i18nKey: \"devportal.nav.organizations\",\n fallback: \"Organizations\",\n icon: Building2,\n },\n {\n path: \"delegations\",\n i18nKey: \"devportal.nav.delegations\",\n fallback: \"Delegations\",\n icon: Share2,\n },\n {\n path: \"revenue\",\n i18nKey: \"devportal.nav.revenue\",\n fallback: \"Revenue\",\n icon: DollarSign,\n },\n {\n path: \"docs\",\n i18nKey: \"devportal.nav.docs\",\n fallback: \"Docs\",\n icon: BookOpen,\n },\n {\n path: \"resources\",\n i18nKey: \"devportal.nav.resources\",\n fallback: \"Resources\",\n icon: Library,\n },\n {\n path: \"publisher/profile\",\n i18nKey: \"devportal.nav.publisher\",\n fallback: \"Publisher\",\n icon: User,\n matchPrefixes: [\"publisher\"],\n },\n {\n path: \"settings\",\n i18nKey: \"devportal.nav.settings\",\n fallback: \"Settings\",\n icon: Settings,\n },\n];\n\nfunction joinBaseAndPath(basePath: string | undefined, path: string): string {\n const base = (basePath ?? \"\").replace(/\\/$/, \"\");\n if (!path) return base || \"/\";\n const suffix = path.startsWith(\"/\") ? path : `/${path}`;\n return `${base}${suffix}`;\n}\n\nfunction isSectionActive(\n pathname: string,\n basePath: string | undefined,\n section: SectionDef,\n): boolean {\n const sectionUrl = joinBaseAndPath(basePath, section.path);\n // Index/dashboard is active only when exactly matched (or with empty extra).\n if (section.path === \"\") {\n return pathname === sectionUrl || pathname === `${sectionUrl}/`;\n }\n const candidates = [section.path, ...(section.matchPrefixes ?? [])];\n return candidates.some((prefix) => {\n const url = joinBaseAndPath(basePath, prefix);\n return pathname === url || pathname.startsWith(`${url}/`);\n });\n}\n\n/** Org context switcher — can be embedded standalone or inside SectionNav. */\nexport const OrgSwitcher: FC = () => {\n const { activeOrgId, setActiveOrgId } = useDevportal();\n const { t } = useI18n();\n const tr = (key: string, fallback: string): string => {\n const value = t(key);\n return value === key ? fallback : value;\n };\n const [open, setOpen] = useState(false);\n const ref = useRef<HTMLDivElement>(null);\n\n const { data } = useGetMyOrganizationsQuery({ fetchPolicy: \"cache-first\" });\n const orgs: OrgItem[] = data?.myDeveloperOrganizations ?? [];\n\n // Auto-select when there's exactly one org and nothing is stored yet\n useEffect(() => {\n if (!activeOrgId && orgs.length === 1 && orgs[0]?.id) {\n setActiveOrgId(orgs[0].id);\n }\n }, [orgs, activeOrgId, setActiveOrgId]);\n\n // Close on outside click\n useEffect(() => {\n const handler = (e: MouseEvent) => {\n if (ref.current && !ref.current.contains(e.target as Node)) {\n setOpen(false);\n }\n };\n if (open) document.addEventListener(\"mousedown\", handler);\n return () => document.removeEventListener(\"mousedown\", handler);\n }, [open]);\n\n if (orgs.length === 0) return null;\n\n const activeOrg = orgs.find((o) => o.id === activeOrgId);\n const label = activeOrg?.name ?? tr(\"devportal.nav.selectOrg\", \"Select org\");\n\n return (\n <div ref={ref} className=\"relative flex-shrink-0\">\n <button\n type=\"button\"\n onClick={() => setOpen((v) => !v)}\n className={cn(\n \"inline-flex items-center gap-1.5 rounded-md border px-3 py-1.5 text-sm font-medium transition-colors\",\n \"bg-bg-surface text-text-primary border-border-subtle hover:bg-accent\",\n )}\n aria-label={tr(\"devportal.nav.switchOrg\", \"Switch organization\")}\n >\n <Building2 size={13} className=\"text-primary flex-shrink-0\" />\n <span className=\"max-w-[140px] truncate\">{label}</span>\n <ChevronDown\n size={13}\n className={cn(\"transition-transform\", open && \"rotate-180\")}\n />\n </button>\n\n {open && (\n <div className=\"absolute right-0 top-full z-50 mt-1 min-w-[200px] rounded-md border border-border-subtle bg-popover shadow-lg py-1\">\n {orgs.map((org) => (\n <button\n key={org.id}\n type=\"button\"\n onClick={() => {\n setActiveOrgId(org.id);\n setOpen(false);\n }}\n className={cn(\n \"flex w-full items-center gap-2 px-3 py-2 text-sm text-left transition-colors hover:bg-accent\",\n org.id === activeOrgId\n ? \"text-primary font-medium\"\n : \"text-text-primary\",\n )}\n >\n <Check\n size={13}\n className={cn(\n \"flex-shrink-0\",\n org.id === activeOrgId\n ? \"opacity-100 text-primary\"\n : \"opacity-0\",\n )}\n />\n <span className=\"truncate\">{org.name}</span>\n </button>\n ))}\n </div>\n )}\n </div>\n );\n};\n\n/**\n * Horizontal section nav rendered above the page content on every devportal\n * route. Gives developers a one-click jump between all top-level sections so\n * routes like OAuth, Delegations, Analytics, Revenue, Docs, Resources,\n * Publisher Profile, and Settings are reachable without typing URLs.\n *\n * Renders nothing when `basePath` is undefined (standalone/dev mode) so it\n * doesn't conflict with whatever harness wraps the MFE there.\n */\nexport const SectionNav: FC = () => {\n const { navigate, basePath } = useDevportal();\n const { pathname } = useLocation();\n const { t } = useI18n();\n const tr = (key: string, fallback: string): string => {\n const value = t(key);\n return value === key ? fallback : value;\n };\n\n if (!basePath || !navigate) return null;\n\n const handleClick = (path: string) => {\n navigate(joinBaseAndPath(basePath, path));\n };\n\n return (\n <nav\n aria-label={tr(\"devportal.nav.sections\", \"Developer portal sections\")}\n className=\"border-b border-border-subtle bg-bg-surface/40 px-4 py-2\"\n data-testid=\"devportal-section-nav\"\n >\n <div className=\"flex items-center gap-2\">\n <ul className=\"flex flex-1 flex-wrap gap-1\">\n {SECTIONS.map((section) => {\n const active = isSectionActive(pathname, basePath, section);\n const Icon = section.icon;\n return (\n <li key={section.path}>\n <button\n type=\"button\"\n onClick={() => handleClick(section.path)}\n aria-current={active ? \"page\" : undefined}\n className={cn(\n \"inline-flex items-center gap-1.5 rounded-md px-3 py-1.5 text-sm font-medium transition-colors\",\n active\n ? \"bg-action-primary-bg text-action-primary-text\"\n : \"text-text-secondary hover:bg-accent hover:text-text-primary\",\n )}\n >\n <Icon size={14} />\n <span>{tr(section.i18nKey, section.fallback)}</span>\n </button>\n </li>\n );\n })}\n </ul>\n <OrgSwitcher />\n </div>\n </nav>\n );\n};\n\n// Exported for unit tests that want to assert against the canonical list.\nexport const __SECTION_NAV_SECTIONS = SECTIONS;\n"],"mappings":";;;;;;;;;;AAsHA,IAAa,UAAwB;CACnC,IAAM,EAAE,gBAAa,sBAAmB,GAAc,EAChD,EAAE,SAAM,GAAS,EACjB,KAAM,GAAa,MAA6B;EACpD,IAAM,IAAQ,EAAE,EAAI;AACpB,SAAO,MAAU,IAAM,IAAW;IAE9B,CAAC,GAAM,KAAW,EAAS,GAAM,EACjC,IAAM,EAAuB,KAAK,EAElC,EAAE,YAAS,EAA2B,EAAE,aAAa,eAAe,CAAC,EACrE,IAAkB,GAAM,4BAA4B,EAAE;AAoB5D,KAjBA,QAAgB;AACd,EAAI,CAAC,KAAe,EAAK,WAAW,KAAK,EAAK,IAAI,MAChD,EAAe,EAAK,GAAG,GAAG;IAE3B;EAAC;EAAM;EAAa;EAAe,CAAC,EAGvC,QAAgB;EACd,IAAM,KAAW,MAAkB;AACjC,GAAI,EAAI,WAAW,CAAC,EAAI,QAAQ,SAAS,EAAE,OAAe,IACxD,EAAQ,GAAM;;AAIlB,SADI,KAAM,SAAS,iBAAiB,aAAa,EAAQ,QAC5C,SAAS,oBAAoB,aAAa,EAAQ;IAC9D,CAAC,EAAK,CAAC,EAEN,EAAK,WAAW,EAAG,QAAO;CAG9B,IAAM,IADY,EAAK,MAAM,MAAM,EAAE,OAAO,EAAY,EAC/B,QAAQ,EAAG,2BAA2B,aAAa;AAE5E,QACE,kBAAC,OAAD;EAAU;EAAK,WAAU;YAAzB,CACE,kBAAC,UAAD;GACE,MAAK;GACL,eAAe,GAAS,MAAM,CAAC,EAAE;GACjC,WAAW,EACT,wGACA,uEACD;GACD,cAAY,EAAG,2BAA2B,sBAAsB;aAPlE;IASE,kBAAC,GAAD;KAAW,MAAM;KAAI,WAAU;KAA+B,CAAA;IAC9D,kBAAC,QAAD;KAAM,WAAU;eAA0B;KAAa,CAAA;IACvD,kBAAC,GAAD;KACE,MAAM;KACN,WAAW,EAAG,wBAAwB,KAAQ,aAAa;KAC3D,CAAA;IACK;MAER,KACC,kBAAC,OAAD;GAAK,WAAU;aACZ,EAAK,KAAK,MACT,kBAAC,UAAD;IAEE,MAAK;IACL,eAAe;AAEb,KADA,EAAe,EAAI,GAAG,EACtB,EAAQ,GAAM;;IAEhB,WAAW,EACT,gGACA,EAAI,OAAO,IACP,6BACA,oBACL;cAZH,CAcE,kBAAC,GAAD;KACE,MAAM;KACN,WAAW,EACT,iBACA,EAAI,OAAO,IACP,6BACA,YACL;KACD,CAAA,EACF,kBAAC,QAAD;KAAM,WAAU;eAAY,EAAI;KAAY,CAAA,CACrC;MAvBF,EAAI,GAuBF,CACT;GACE,CAAA,CAEJ"}
|