@scalar/api-client
Version:
the open source API testing client
402 lines (401 loc) • 12.6 kB
JavaScript
import { isDefined as M } from "@scalar/helpers/array/is-defined";
import { sortByOrder as de } from "@scalar/helpers/array/sort-by-order";
import { migrateLocalStorageToIndexDb as ve } from "@scalar/oas-utils/migrations";
import { createSidebarState as fe, generateReverseIndex as he } from "@scalar/sidebar";
import { createWorkspaceStore as Y } from "@scalar/workspace-store/client";
import { createWorkspaceEventBus as we } from "@scalar/workspace-store/events";
import { generateUniqueValue as ge } from "@scalar/workspace-store/helpers/generate-unique-value";
import { getParentEntry as h } from "@scalar/workspace-store/navigation";
import { createWorkspaceStorePersistence as ke, getWorkspaceId as w } from "@scalar/workspace-store/persistence";
import { persistencePlugin as xe } from "@scalar/workspace-store/plugins/client";
import { extensions as be } from "@scalar/workspace-store/schemas/extensions";
import { ref as p, computed as u, shallowRef as j, readonly as ye } from "vue";
import { getRouteParam as l } from "./helpers/get-route-param.js";
import { groupWorkspacesByTeam as Se } from "./helpers/group-workspaces.js";
import { getActiveEnvironment as Ee } from "../../helpers/get-active-environment.js";
import { getTabDetails as We } from "../../helpers/get-tab-details.js";
import { slugify as Ue } from "../../helpers/slugify.js";
import { workspaceStorage as Te } from "../../helpers/storage.js";
import { initializeAppEventHandlers as Ie } from "./app-events.js";
import { filterWorkspacesByTeam as De, canLoadWorkspace as H } from "./helpers/filter-workspaces.js";
const Re = 1e3, Ae = 288, Xe = async ({
router: i,
fileLoader: z
}) => {
const S = we({
debug: !1
}), { workspace: g } = await ke();
await ve();
const k = p("local"), f = p(void 0), x = p(void 0), E = p(void 0), B = p(void 0), L = p(void 0), N = p(void 0), W = p(!1);
i.afterEach((e) => ue(e));
const d = u(() => i.currentRoute.value ?? null), U = j(null), v = p([]), b = u(() => De(v.value, k.value)), G = u(() => Se(b.value, k.value)), s = j(null), D = u(() => s.value?.workspace.documents[E.value ?? ""] || null), J = u(() => Ee(s.value, D.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 Q = 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), U.value = { id: n, label: e });
}, X = async ({
namespace: e,
slug: a
}) => Y({
plugins: [
await xe({
workspaceId: w(e, a),
debounceDelay: Re
})
],
fileLoader: z
}), Z = async (e, a) => {
const t = await g.getItem({ namespace: e, slug: a });
if (!t)
return {
success: !1
};
const n = await X({ namespace: e, slug: a });
return n.loadWorkspace(t.workspace), U.value = { id: w(t.namespace, t.slug), label: t.name }, s.value = n, {
success: !0,
workspace: n.workspace
};
}, ee = async ({
name: e,
teamUid: a,
namespace: t,
slug: n
}) => {
const c = Y();
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 i.push("/");
return;
}
await i.push({
name: "example",
params: {
namespace: e,
workspaceSlug: a,
documentSlug: "drafts",
pathEncoded: encodeURIComponent("/"),
method: "get",
exampleName: "default"
}
});
}, O = async ({
teamUid: e,
namespace: a,
slug: t,
name: n
}) => {
s.value = null;
const c = await ge({
defaultValue: t ?? n,
// Use the provided id if it exists, otherwise use the name
validation: async (I) => !await g.has({ namespace: a ?? "local", slug: I }),
maxRetries: 100,
transformation: Ue
});
if (!c)
return;
const m = await ee({
teamUid: e,
namespace: a,
slug: c,
name: n
});
return await y(m.namespace, m.slug), m;
}, ae = async (e, a) => {
s.value = null, W.value = !0;
const t = await Z(e, a);
if (t.success) {
const o = t.workspace["x-scalar-active-tab"] ?? 0, m = t.workspace["x-scalar-tabs"], I = m?.[o];
I && await i.replace({
path: I.path,
query: d.value?.query ?? {}
}), m && o >= m.length && S.emit("tabs:update:tabs", {
"x-scalar-active-tab": 0
}), m || S.emit("tabs:update:tabs", {
"x-scalar-tabs": [C(d.value)],
"x-scalar-active-tab": 0
}), W.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 O({
name: "Default Workspace",
slug: "default"
});
if (W.value = !1, !c)
return console.error("Failed to create the default workspace, something went wrong, can not load the workspace");
r.reset();
}, te = (e) => {
k.value = e;
const a = b.value.find(
(t) => t.namespace === f.value && t.slug === x.value
);
if (!(a && H(a.teamUid, e)))
return y("local", "default");
}, _ = u(() => {
const e = s.value;
if (!e)
return [];
const a = e.workspace["x-scalar-order"] ?? Object.keys(e.workspace.documents);
return de(Object.keys(e.workspace.documents), a, (t) => t).map((t) => e.workspace.documents[t]?.["x-scalar-navigation"]).filter(M);
}), r = fe(_), R = ({
document: e,
path: a,
method: t,
example: n
}) => JSON.stringify([e, a, t, n].filter(M)), P = u(
() => he({
items: _.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 R({
document: a?.name ?? "",
path: t?.path,
method: t?.method,
example: e.type === "example" ? e.name : void 0
});
}
})
), T = (e) => {
const a = P.value.get(R(e));
return a || P.value.get(
R({
document: e.document,
path: e.path,
method: e.method
})
);
}, V = (e) => {
const a = r.getEntryById(e);
if (!a) {
console.warn(`Could not find sidebar entry with id ${e} to select`);
return;
}
if (a.type === "document") {
if (r.selectedItem.value === e) {
r.setExpanded(e, !r.isExpanded(e));
return;
}
return r.setSelected(e), r.setExpanded(e, !0), i.push({
name: "document.overview",
params: { documentSlug: a.name }
});
}
if (a.type === "operation") {
if (r.isSelected(e)) {
r.setExpanded(e, !r.isExpanded(e));
return;
}
const t = a.children?.find((n) => n.type === "example");
return t ? (r.setSelected(t.id), r.setExpanded(t.id, !0)) : r.setSelected(e), i.push({
name: "example",
params: {
documentSlug: h("document", a)?.name,
pathEncoded: encodeURIComponent(a.path),
method: a.method,
exampleName: t?.name ?? "default"
}
});
}
if (a.type === "example") {
r.setSelected(e);
const t = h("operation", a);
return i.push({
name: "example",
params: {
documentSlug: h("document", a)?.name,
pathEncoded: encodeURIComponent(t?.path ?? ""),
method: t?.method,
exampleName: a.name
}
});
}
if (a.type === "text")
return i.push({
name: "document.overview",
params: {
documentSlug: h("document", a)?.name
}
});
r.setExpanded(e, !r.isExpanded(e));
}, ne = 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 i.replace(a.path);
}, F = (e) => {
e && s.value?.buildSidebar(e);
}, se = (e) => {
const a = D.value?.["x-scalar-navigation"]?.name;
if (!a)
return;
const t = T({
document: a,
path: e.path,
method: e.method,
example: e.exampleKey
});
(!t || t.type !== "example") && (F(a), d.value && q(d.value));
}, oe = u(() => s.value?.workspace?.["x-scalar-sidebar-width"] ?? Ae), re = (e) => s.value?.update("x-scalar-sidebar-width", e), A = p(!0), ce = "x-scalar-tabs", le = "x-scalar-active-tab", C = (e) => {
const a = l("method", e), t = l("pathEncoded", e), n = l("documentSlug", e), c = l("workspaceSlug", e);
return {
...We({
workspace: c,
document: n,
path: t,
method: a,
getEntryByLocation: T
}),
path: d.value?.path ?? ""
};
}, K = u(() => s.value?.workspace[ce] ?? [C(d.value)]), ie = u(() => s.value?.workspace[le] ?? 0), $ = async (e) => {
const a = K.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);
}
}, ue = (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 && !H(c.teamUid, k.value))
return y("local", "default");
if (f.value = n, x.value = a, E.value = t, B.value = l("method", e), L.value = l("pathEncoded", e), N.value = l("exampleName", e), e.path !== "" && Te.setCurrentPath(e.path), w(f.value, a) !== U.value?.id)
return ae(f.value, a);
t && t !== s.value?.workspace[be.workspace.activeDocument] && s?.value?.update("x-scalar-active-document", t), pe(e), q(e);
}, pe = (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));
}, q = (e) => {
const a = l("documentSlug", e);
if (!a) {
r.setSelected(null);
return;
}
const t = T({
document: a,
path: l("pathEncoded", e),
method: l("method", e),
example: l("exampleName", e)
});
t && (r.setSelected(t.id), r.setExpanded(t.id, !0));
};
Ie({
eventBus: S,
router: i,
store: s,
navigateToCurrentTab: ne,
rebuildSidebar: F,
onAfterExampleCreation: se,
onSelectSidebarItem: V,
onCopyTabUrl: (e) => $(e),
onToggleSidebar: () => A.value = !A.value,
renameWorkspace: Q
});
const me = 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: oe,
isOpen: A,
handleSelectItem: V,
handleSidebarWidthUpdate: re,
getEntryByLocation: T
},
tabs: {
state: K,
activeTabIndex: ie,
copyTabUrl: $
},
workspace: {
create: O,
workspaceList: v,
filteredWorkspaceList: b,
workspaceGroups: G,
activeWorkspace: U,
navigateToWorkspace: y,
isOpen: u(() => !!(x.value && !E.value))
},
eventBus: S,
router: i,
currentRoute: d,
loading: W,
activeEntities: {
namespace: f,
workspaceSlug: x,
documentSlug: E,
path: L,
method: B,
exampleName: N,
teamUid: ye(k),
setTeamUid: te
},
environment: J,
document: D,
isDarkMode: me
};
};
export {
Xe as createAppState
};