@scalar/api-client
Version:
the open source API testing client
416 lines (415 loc) • 13.1 kB
JavaScript
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
};