UNPKG

@scalar/api-client

Version:

the open source API testing client

416 lines (415 loc) 13.1 kB
import { isDefined as j } from "@scalar/helpers/array/is-defined"; import { sortByOrder as he } from "@scalar/helpers/array/sort-by-order"; import { migrateLocalStorageToIndexDb as we } from "@scalar/oas-utils/migrations"; import { createSidebarState as ge, generateReverseIndex as ke } from "@scalar/sidebar"; import { createWorkspaceStore as H } from "@scalar/workspace-store/client"; import { createWorkspaceEventBus as xe } from "@scalar/workspace-store/events"; import { generateUniqueValue as be } from "@scalar/workspace-store/helpers/generate-unique-value"; import { getParentEntry as h } from "@scalar/workspace-store/navigation"; import { createWorkspaceStorePersistence as ye, getWorkspaceId as w } from "@scalar/workspace-store/persistence"; import { persistencePlugin as Se } from "@scalar/workspace-store/plugins/client"; import { extensions as Ee } from "@scalar/workspace-store/schemas/extensions"; import { ref as i, computed as u, shallowRef as z, readonly as We } from "vue"; import { getRouteParam as l } from "./helpers/get-route-param.js"; import { groupWorkspacesByTeam as Ue } from "./helpers/group-workspaces.js"; import { useTheme as Te } from "./hooks/use-theme.js"; import { getActiveEnvironment as Ie } from "../../helpers/get-active-environment.js"; import { getTabDetails as De } from "../../helpers/get-tab-details.js"; import { slugify as Re } from "../../helpers/slugify.js"; import { workspaceStorage as Ae } from "../../helpers/storage.js"; import { initializeAppEventHandlers as Ce } from "./app-events.js"; import { filterWorkspacesByTeam as Be, canLoadWorkspace as G } from "./helpers/filter-workspaces.js"; const Le = 1e3, Ne = 288, na = async ({ router: p, fileLoader: J, fallbackThemeSlug: Q = () => "default", customThemes: B = () => [] }) => { const E = xe({ debug: !1 }), { workspace: g } = await ye(); await we(); const k = i("local"), f = i(void 0), x = i(void 0), W = i(void 0), L = i(void 0), N = i(void 0), O = i(void 0), U = i(!1); p.afterEach((e) => de(e)); const d = u(() => p.currentRoute.value ?? null), T = z(null), v = i([]), b = u(() => Be(v.value, k.value)), X = u(() => Ue(b.value, k.value)), s = z(null), R = u(() => s.value?.workspace.documents[W.value ?? ""] || null), Z = u(() => Ie(s.value, R.value)); v.value = await g.getAll().then( (e) => e.map(({ teamUid: a, namespace: t, slug: n, name: c }) => ({ id: w(t, n), teamUid: a, namespace: t, slug: n, label: c })) ); const ee = async (e) => { const a = f.value, t = x.value; if (!a || !t) return; const n = w(a, t); await g.updateName({ namespace: a, slug: t }, e) !== void 0 && (v.value = v.value.map((o) => o.id === n ? { ...o, label: e } : o), T.value = { id: n, label: e }); }, ae = async ({ namespace: e, slug: a }) => H({ plugins: [ await Se({ workspaceId: w(e, a), debounceDelay: Le }) ], fileLoader: J }), te = async (e, a) => { const t = await g.getItem({ namespace: e, slug: a }); if (!t) return { success: !1 }; const n = await ae({ namespace: e, slug: a }); return n.loadWorkspace(t.workspace), T.value = { id: w(t.namespace, t.slug), label: t.name }, s.value = n, { success: !0, workspace: n.workspace }; }, ne = async ({ name: e, teamUid: a, namespace: t, slug: n }) => { const c = H(); await c.addDocument({ name: "drafts", document: { openapi: "3.1.0", info: { title: "Drafts", version: "1.0.0" }, paths: { "/": { get: {} } }, "x-scalar-original-document-hash": "drafts", "x-scalar-icon": "interface-edit-tool-pencil" } }); const o = await g.setItem( { namespace: t, slug: n }, { name: e, teamUid: a, workspace: c.exportWorkspace() } ); return v.value.push({ id: w(o.namespace, o.slug), teamUid: o.teamUid, namespace: o.namespace, slug: o.slug, label: o.name }), o; }, y = async (e, a) => { if (!e || !a) { await p.push("/"); return; } await p.push({ name: "example", params: { namespace: e, workspaceSlug: a, documentSlug: "drafts", pathEncoded: encodeURIComponent("/"), method: "get", exampleName: "default" } }); }, _ = async ({ teamUid: e, namespace: a, slug: t, name: n }) => { s.value = null; const c = await be({ defaultValue: t ?? n, // Use the provided id if it exists, otherwise use the name validation: async (D) => !await g.has({ namespace: a ?? "local", slug: D }), maxRetries: 100, transformation: Re }); if (!c) return; const m = await ne({ teamUid: e, namespace: a, slug: c, name: n }); return await y(m.namespace, m.slug), m; }, se = async (e, a) => { s.value = null, U.value = !0; const t = await te(e, a); if (t.success) { const o = t.workspace["x-scalar-active-tab"] ?? 0, m = t.workspace["x-scalar-tabs"], D = m?.[o]; D && await p.replace({ path: D.path, query: d.value?.query ?? {} }), m && o >= m.length && E.emit("tabs:update:tabs", { "x-scalar-active-tab": 0 }), m || E.emit("tabs:update:tabs", { "x-scalar-tabs": [C(d.value)], "x-scalar-active-tab": 0 }), U.value = !1; return; } const n = b.value.find((o) => o.teamUid === "local" && o.slug === "default") ?? b.value[0]; if (n) return y(n.namespace, n.slug); const c = await _({ name: "Default Workspace", slug: "default" }); if (U.value = !1, !c) return console.error("Failed to create the default workspace, something went wrong, can not load the workspace"); r.reset(); }, re = (e) => { k.value = e; const a = b.value.find( (t) => t.namespace === f.value && t.slug === x.value ); if (!(a && G(a.teamUid, e))) return y("local", "default"); }, P = u(() => { const e = s.value; if (!e) return []; const a = e.workspace["x-scalar-order"] ?? Object.keys(e.workspace.documents); return he(Object.keys(e.workspace.documents), a, (t) => t).map((t) => e.workspace.documents[t]?.["x-scalar-navigation"]).filter(j); }), r = ge(P), A = ({ document: e, path: a, method: t, example: n }) => JSON.stringify([e, a, t, n].filter(j)), V = u( () => ke({ items: P.value, nestedKey: "children", filter: (e) => e.type === "document" || e.type === "operation" || e.type === "example", getId: (e) => { const a = h("document", e), t = h("operation", e); return A({ document: a?.name ?? "", path: t?.path, method: t?.method, example: e.type === "example" ? e.name : void 0 }); } }) ), I = (e) => { const a = V.value.get(A(e)); return a || V.value.get( A({ document: e.document, path: e.path, method: e.method }) ); }, F = (e) => { const a = r.getEntryById(e); if (!a) { console.warn(`Could not find sidebar entry with id ${e} to select`); return; } const t = (n) => (S.value = !1, p.push(n)); if (a.type === "document") { if (r.selectedItem.value === e) { r.setExpanded(e, !r.isExpanded(e)); return; } return r.setSelected(e), r.setExpanded(e, !0), t({ name: "document.overview", params: { documentSlug: a.name } }); } if (a.type === "operation") { if (r.isSelected(e) && r.selectedItem.value !== e) { r.setExpanded(e, !r.isExpanded(e)); return; } const n = a.children?.find((c) => c.type === "example"); return n ? (r.setSelected(n.id), r.setExpanded(n.id, !0)) : r.setSelected(e), t({ name: "example", params: { documentSlug: h("document", a)?.name, pathEncoded: encodeURIComponent(a.path), method: a.method, exampleName: n?.name ?? "default" } }); } if (a.type === "example") { r.setSelected(e); const n = h("operation", a); return t({ name: "example", params: { documentSlug: h("document", a)?.name, pathEncoded: encodeURIComponent(n?.path ?? ""), method: n?.method, exampleName: a.name } }); } if (a.type === "text") return t({ name: "document.overview", params: { documentSlug: h("document", a)?.name } }); r.setExpanded(e, !r.isExpanded(e)); }, oe = async () => { if (!s.value) return; const e = s.value.workspace["x-scalar-active-tab"] ?? 0, a = s.value.workspace["x-scalar-tabs"]?.[e]; a && await p.replace(a.path); }, K = (e) => { e && s.value?.buildSidebar(e); }, ce = (e) => { const a = e.documentName ?? R.value?.["x-scalar-navigation"]?.name; if (!a) return; const t = I({ document: a, path: e.path, method: e.method, example: e.exampleKey }); (!t || t.type !== "example") && (K(a), d.value && M(d.value)); }, le = u(() => s.value?.workspace?.["x-scalar-sidebar-width"] ?? Ne), ue = (e) => s.value?.update("x-scalar-sidebar-width", e), S = i(!1), ie = "x-scalar-tabs", pe = "x-scalar-active-tab", C = (e) => { const a = l("method", e), t = l("pathEncoded", e), n = l("documentSlug", e), c = l("workspaceSlug", e); return { ...De({ workspace: c, document: n, path: t, method: a, getEntryByLocation: I }), path: d.value?.path ?? "" }; }, $ = u(() => s.value?.workspace[ie] ?? [C(d.value)]), me = u(() => s.value?.workspace[pe] ?? 0), q = async (e) => { const a = $.value[e]; if (!a) { console.warn(`Cannot copy URL: tab at index ${e} does not exist`); return; } const t = `${window.location.origin}${a.path}`; try { await navigator.clipboard.writeText(t); } catch (n) { console.error("Failed to copy URL to clipboard:", n); } }, de = (e) => { const a = l("workspaceSlug", e), t = l("documentSlug", e), n = l("namespace", e); if (!n || !a) return; const c = v.value.find( (o) => o.slug === a && o.namespace === n ); if (c && !G(c.teamUid, k.value)) return y("local", "default"); if (f.value = n, x.value = a, W.value = t, L.value = l("method", e), N.value = l("pathEncoded", e), O.value = l("exampleName", e), e.path !== "" && Ae.setCurrentPath(e.path), w(f.value, a) !== T.value?.id) return se(f.value, a); t && t !== s.value?.workspace[Ee.workspace.activeDocument] && s?.value?.update("x-scalar-active-document", t), ve(e), M(e); }, ve = (e) => { const a = s.value?.workspace["x-scalar-tabs"] ?? [], t = s.value?.workspace["x-scalar-active-tab"] ?? 0, n = a[t]; !n || n.path === e.path || (a[t] = C(e)); }, M = (e) => { const a = l("documentSlug", e); if (!a) { r.setSelected(null); return; } const t = I({ document: a, path: l("pathEncoded", e), method: l("method", e), example: l("exampleName", e) }); t && (r.setSelected(t.id), r.setExpanded(t.id, !0)); }; Ce({ eventBus: E, router: p, store: s, navigateToCurrentTab: oe, rebuildSidebar: K, onAfterExampleCreation: ce, onSelectSidebarItem: F, onCopyTabUrl: (e) => q(e), onToggleSidebar: () => S.value = !S.value, closeSidebar: () => S.value = !1, renameWorkspace: ee }); const Y = Te({ fallbackThemeSlug: Q, customThemes: B, store: s }), fe = u(() => { const e = s.value?.workspace["x-scalar-color-mode"] ?? "system"; return e === "system" ? window.matchMedia?.("(prefers-color-scheme: dark)")?.matches ?? !1 : e === "dark"; }); return { /** Active workspace store */ store: s, sidebar: { state: r, width: le, isOpen: S, handleSelectItem: F, handleSidebarWidthUpdate: ue, getEntryByLocation: I }, tabs: { state: $, activeTabIndex: me, copyTabUrl: q }, workspace: { create: _, workspaceList: v, filteredWorkspaceList: b, workspaceGroups: X, activeWorkspace: T, navigateToWorkspace: y, isOpen: u(() => !!(x.value && !W.value)) }, eventBus: E, router: p, currentRoute: d, loading: U, activeEntities: { namespace: f, workspaceSlug: x, documentSlug: W, path: N, method: L, exampleName: O, teamUid: We(k), setTeamUid: re }, environment: Z, document: R, isDarkMode: fe, theme: { styles: Y.themeStyles, themeStyleTag: Y.themeStyleTag, customThemes: B } }; }; export { na as createAppState };