diff --git a/app/web/package.json b/app/web/package.json
index c7019d1bc5..57a4ab2a42 100644
--- a/app/web/package.json
+++ b/app/web/package.json
@@ -54,6 +54,10 @@
"date-fns": "^2.29.2",
"floating-vue": "^2.0.0-beta.16",
"fontfaceobserver": "^2.3.0",
+ "graphology": "^0.25.4",
+ "graphology-layout": "^0.6.1",
+ "graphology-layout-forceatlas2": "^0.10.1",
+ "graphology-layout-noverlap": "^0.4.2",
"is-promise": "^4.0.0",
"javascript-time-ago": "^2.5.7",
"js-base64": "^3.7.5",
@@ -70,6 +74,7 @@
"plur": "^5.1.0",
"posthog-js": "^1.76.0",
"reconnecting-websocket": "^4.4.0",
+ "sigma": "3.0.0-beta.2",
"tinycolor2": "^1.4.2",
"typescript": "^4.9.5",
"validator": "^13.7.0",
@@ -79,10 +84,10 @@
"vue-router": "^4.1.6",
"vue-safe-teleport": "^0.1.2",
"vue-toastification": "2.0.0-rc.5",
- "yjs-codemirror-plugin": "workspace:*",
"y-indexeddb": "^9.0.12",
"y-websocket": "^1.5.0",
- "yjs": "^13.6.8"
+ "yjs": "^13.6.8",
+ "yjs-codemirror-plugin": "workspace:*"
},
"devDependencies": {
"@iconify/json": "^2.2.135",
@@ -101,6 +106,7 @@
"cypress": "^9.6.0",
"eslint": "^8.36.0",
"faker": "^6.6.6",
+ "graphology-types": "^0.24.7",
"unplugin-icons": "^0.17.1",
"vite": "^4.4.9",
"vite-plugin-checker": "^0.6.1",
@@ -110,4 +116,4 @@
"volta": {
"extends": "../../package.json"
}
-}
\ No newline at end of file
+}
diff --git a/app/web/src/components/Workspace/WorkspaceViz.vue b/app/web/src/components/Workspace/WorkspaceViz.vue
new file mode 100644
index 0000000000..8a16061ea8
--- /dev/null
+++ b/app/web/src/components/Workspace/WorkspaceViz.vue
@@ -0,0 +1,31 @@
+
+
+
+
+
+
+
+
+
diff --git a/app/web/src/components/Workspace/WorkspaceVizSchemaVariant.vue b/app/web/src/components/Workspace/WorkspaceVizSchemaVariant.vue
new file mode 100644
index 0000000000..f008e4b984
--- /dev/null
+++ b/app/web/src/components/Workspace/WorkspaceVizSchemaVariant.vue
@@ -0,0 +1,95 @@
+
+
+
+
+
diff --git a/app/web/src/router.ts b/app/web/src/router.ts
index 41832512aa..c891ea49f8 100644
--- a/app/web/src/router.ts
+++ b/app/web/src/router.ts
@@ -49,6 +49,11 @@ const routes: RouteRecordRaw[] = [
};
},
},
+ {
+ path: ":changeSetId/viz",
+ name: "workspace-viz",
+ component: () => import("@/components/Workspace/WorkspaceViz.vue"),
+ },
{
path: ":changeSetId/c",
name: "workspace-compose",
diff --git a/app/web/src/store/viz.store.ts b/app/web/src/store/viz.store.ts
new file mode 100644
index 0000000000..d091404b7e
--- /dev/null
+++ b/app/web/src/store/viz.store.ts
@@ -0,0 +1,81 @@
+import { defineStore } from "pinia";
+import { ApiRequest, addStoreHooks } from "@si/vue-lib/pinia";
+import { useWorkspacesStore } from "@/store/workspaces.store";
+import { useChangeSetsStore } from "@/store/change_sets.store";
+import { Visibility } from "@/api/sdf/dal/visibility";
+import { nilId } from "@/utils/nilId";
+
+export type NodeKind = "Category" | "Content" | "Func" | "Ordering" | "Prop";
+
+export type ContentKind =
+ | "Root"
+ | "ActionPrototype"
+ | "AttributePrototype"
+ | "AttributePrototypeArgument"
+ | "AttributeValue"
+ | "Component"
+ | "ExternalProvider"
+ | "FuncArg"
+ | "Func"
+ | "InternalProvider"
+ | "Prop"
+ | "Schema"
+ | "SchemaVariant"
+ | "StaticArgumentValue"
+ | "ValidationPrototype";
+
+export interface VizResponse {
+ edges: {
+ from: string;
+ to: string;
+ }[];
+
+ nodes: {
+ id: string;
+ nodeKind: NodeKind;
+ contentKind: ContentKind | null;
+ name: string | null;
+ }[];
+
+ rootNodeId: string;
+}
+
+export const useVizStore = () => {
+ const changeSetStore = useChangeSetsStore();
+ const selectedChangeSetId = changeSetStore.selectedChangeSetId;
+ const workspacesStore = useWorkspacesStore();
+ const workspaceId = workspacesStore.selectedWorkspacePk;
+ const visibility: Visibility = {
+ visibility_change_set_pk: selectedChangeSetId ?? nilId(),
+ };
+
+ return addStoreHooks(
+ defineStore(
+ `ws${workspaceId || "NONE"}/cs${selectedChangeSetId || "NONE"}/viz`,
+ {
+ state: () => ({
+ edges: [],
+ nodes: [],
+ }),
+ getters: {
+ nodes: (state) => state.nodes,
+ edges: (state) => state.edges,
+ },
+ actions: {
+ async FETCH_VIZ() {
+ return new ApiRequest({
+ url: "/graphviz/nodes_edges",
+ params: { ...visibility },
+ });
+ },
+ async FETCH_SCHEMA_VARIANT_VIZ(schemaVariantId: string) {
+ return new ApiRequest({
+ url: "/graphviz/schema_variant",
+ params: { schemaVariantId, ...visibility },
+ });
+ },
+ },
+ },
+ ),
+ )();
+};
diff --git a/lib/dal/src/workspace_snapshot.rs b/lib/dal/src/workspace_snapshot.rs
index 23600f6ce7..8f18173f59 100644
--- a/lib/dal/src/workspace_snapshot.rs
+++ b/lib/dal/src/workspace_snapshot.rs
@@ -215,6 +215,10 @@ impl WorkspaceSnapshot {
self.id
}
+ pub fn root(&mut self) -> WorkspaceSnapshotResult {
+ Ok(self.working_copy()?.root())
+ }
+
fn working_copy(&mut self) -> WorkspaceSnapshotResult<&mut WorkspaceSnapshotGraph> {
if self.working_copy.is_none() {
self.working_copy = Some(postcard::from_bytes(&self.snapshot)?);
@@ -362,6 +366,18 @@ impl WorkspaceSnapshot {
Ok(())
}
+ pub fn nodes(
+ &mut self,
+ ) -> WorkspaceSnapshotResult> {
+ Ok(self.working_copy()?.nodes())
+ }
+
+ pub fn edges(
+ &mut self,
+ ) -> WorkspaceSnapshotResult> {
+ Ok(self.working_copy()?.edges())
+ }
+
pub fn dot(&mut self) {
self.working_copy()
.expect("failed on accessing or creating a working copy")
@@ -491,6 +507,42 @@ impl WorkspaceSnapshot {
.collect())
}
+ pub fn all_outgoing_targets(
+ &mut self,
+ id: impl Into,
+ ) -> WorkspaceSnapshotResult> {
+ let mut result = vec![];
+ let target_idxs: Vec = self
+ .edges_directed(id, Direction::Outgoing)?
+ .map(|edge_ref| edge_ref.target())
+ .collect();
+
+ for target_idx in target_idxs {
+ let node_weight = self.get_node_weight(target_idx)?;
+ result.push(node_weight.to_owned());
+ }
+
+ Ok(result)
+ }
+
+ pub fn all_incoming_sources(
+ &mut self,
+ id: impl Into,
+ ) -> WorkspaceSnapshotResult> {
+ let mut result = vec![];
+ let source_idxs: Vec = self
+ .edges_directed(id, Direction::Incoming)?
+ .map(|edge_ref| edge_ref.source())
+ .collect();
+
+ for source_idx in source_idxs {
+ let node_weight = self.get_node_weight(source_idx)?;
+ result.push(node_weight.to_owned());
+ }
+
+ Ok(result)
+ }
+
pub fn remove_incoming_edges_of_kind(
&mut self,
change_set: &ChangeSetPointer,
diff --git a/lib/dal/src/workspace_snapshot/graph.rs b/lib/dal/src/workspace_snapshot/graph.rs
index 6c5e8f946c..4d701c3f2a 100644
--- a/lib/dal/src/workspace_snapshot/graph.rs
+++ b/lib/dal/src/workspace_snapshot/graph.rs
@@ -233,6 +233,27 @@ impl WorkspaceSnapshotGraph {
self.graph.edges_directed(node_index, direction)
}
+ pub fn nodes(&self) -> impl Iterator- {
+ self.graph.node_indices().filter_map(|node_idx| {
+ self.graph
+ .node_weight(node_idx)
+ .map(|weight| (weight, node_idx))
+ })
+ }
+
+ pub fn edges(&self) -> impl Iterator
- {
+ self.graph.edge_indices().filter_map(|edge_idx| {
+ self.graph
+ .edge_weight(edge_idx)
+ .map(|weight| {
+ self.graph
+ .edge_endpoints(edge_idx)
+ .map(|(source, target)| (weight, source, target))
+ })
+ .flatten()
+ })
+ }
+
pub fn add_ordered_edge(
&mut self,
change_set: &ChangeSetPointer,
diff --git a/lib/dal/src/workspace_snapshot/node_weight/category_node_weight.rs b/lib/dal/src/workspace_snapshot/node_weight/category_node_weight.rs
index 994b9cb950..1b4061ef3b 100644
--- a/lib/dal/src/workspace_snapshot/node_weight/category_node_weight.rs
+++ b/lib/dal/src/workspace_snapshot/node_weight/category_node_weight.rs
@@ -1,13 +1,14 @@
use chrono::{DateTime, Utc};
use content_store::ContentHash;
use serde::{Deserialize, Serialize};
+use strum::Display;
use ulid::Ulid;
use crate::change_set_pointer::ChangeSetPointer;
use crate::workspace_snapshot::vector_clock::VectorClockId;
use crate::workspace_snapshot::{node_weight::NodeWeightResult, vector_clock::VectorClock};
-#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
+#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Display)]
pub enum CategoryNodeKind {
Component,
Func,
diff --git a/lib/sdf-server/src/server/routes.rs b/lib/sdf-server/src/server/routes.rs
index d84e6d9b59..e133c2b510 100644
--- a/lib/sdf-server/src/server/routes.rs
+++ b/lib/sdf-server/src/server/routes.rs
@@ -33,14 +33,15 @@ pub fn routes(state: AppState) -> Router {
)
.nest("/api/func", crate::server::service::func::routes())
.nest("/api/schema", crate::server::service::schema::routes())
- .nest("/api/diagram", crate::server::service::diagram::routes());
- // .nest("/api/fix", crate::server::service::fix::routes())
- // .nest("/api/pkg", crate::server::service::pkg::routes())
- // .nest("/api/provider", crate::server::service::provider::routes())
- // .nest(
- // "/api/qualification",
- // crate::server::service::qualification::routes(),
- // )
+ // .nest("/api/fix", crate::server::service::fix::routes())
+ // .nest("/api/pkg", crate::server::service::pkg::routes())
+ // .nest("/api/provider", crate::server::service::provider::routes())
+ // .nest(
+ // "/api/qualification",
+ // crate::server::service::qualification::routes(),
+ // )
+ .nest("/api/diagram", crate::server::service::diagram::routes())
+ .nest("/api/graphviz", crate::server::service::graphviz::routes());
// .nest("/api/secret", crate::server::service::secret::routes())
// .nest("/api/status", crate::server::service::status::routes())
// .nest(
diff --git a/lib/sdf-server/src/server/service.rs b/lib/sdf-server/src/server/service.rs
index 4e0d4ede90..f49fa9ecc3 100644
--- a/lib/sdf-server/src/server/service.rs
+++ b/lib/sdf-server/src/server/service.rs
@@ -2,6 +2,7 @@ pub mod change_set;
pub mod component;
pub mod diagram;
pub mod func;
+pub mod graphviz;
pub mod schema;
pub mod session;
pub mod ws;
diff --git a/lib/sdf-server/src/server/service/graphviz.rs b/lib/sdf-server/src/server/service/graphviz.rs
new file mode 100644
index 0000000000..1381697e7b
--- /dev/null
+++ b/lib/sdf-server/src/server/service/graphviz.rs
@@ -0,0 +1,308 @@
+use std::collections::{HashMap, HashSet, VecDeque};
+
+use axum::{extract::Query, response::Response, routing::get, Json, Router};
+use dal::{
+ schema::variant::SchemaVariantError,
+ workspace_snapshot::{
+ self,
+ content_address::ContentAddressDiscriminants,
+ edge_weight::EdgeWeightKindDiscriminants,
+ node_weight::{NodeWeight, NodeWeightDiscriminants},
+ WorkspaceSnapshotError,
+ },
+ SchemaVariant, SchemaVariantId, TransactionsError, Visibility,
+};
+use serde::{Deserialize, Serialize};
+use thiserror::Error;
+use ulid::Ulid;
+
+use crate::server::{
+ extract::{AccessBuilder, HandlerContext},
+ impl_default_error_into_response,
+ state::AppState,
+};
+
+#[remain::sorted]
+#[derive(Error, Debug)]
+pub enum GraphVizError {
+ #[error(transparent)]
+ ContextTransaction(#[from] TransactionsError),
+ #[error("graph did not have a root node, although this is an unreachable state")]
+ NoRootNode,
+ #[error(transparent)]
+ SchemaVariant(#[from] SchemaVariantError),
+ #[error("could not acquire lock: {0}")]
+ TryLock(#[from] tokio::sync::TryLockError),
+ #[error("workspace snapshot error")]
+ WorkspaceSnapshot(#[from] WorkspaceSnapshotError),
+}
+
+type GraphVizResult = Result;
+
+#[derive(Deserialize, Serialize, Debug)]
+#[serde(rename_all = "camelCase")]
+pub struct GraphVizRequest {
+ #[serde(flatten)]
+ pub visibility: Visibility,
+}
+
+#[derive(Deserialize, Serialize, Debug)]
+#[serde(rename_all = "camelCase")]
+pub struct SchemaVariantVizRequest {
+ #[serde(flatten)]
+ pub visibility: Visibility,
+ pub schema_variant_id: SchemaVariantId,
+}
+
+#[derive(Deserialize, Serialize, Debug)]
+#[serde(rename_all = "camelCase")]
+pub struct GraphVizNode {
+ id: Ulid,
+ content_kind: Option,
+ node_kind: NodeWeightDiscriminants,
+ name: Option,
+}
+
+#[derive(Deserialize, Serialize, Debug)]
+#[serde(rename_all = "camelCase")]
+pub struct GraphVizEdge {
+ from: Ulid,
+ to: Ulid,
+}
+
+#[derive(Deserialize, Serialize, Debug)]
+#[serde(rename_all = "camelCase")]
+pub struct GraphVizResponse {
+ pub nodes: Vec,
+ pub edges: Vec,
+ pub root_node_id: Ulid,
+}
+
+pub async fn schema_variant(
+ HandlerContext(builder): HandlerContext,
+ AccessBuilder(request_ctx): AccessBuilder,
+ Query(request): Query,
+) -> GraphVizResult> {
+ let ctx = builder.build(request_ctx.build(request.visibility)).await?;
+
+ let mut func_nodes = vec![];
+ let mut nodes = vec![];
+ let mut edges = vec![];
+ let mut added_nodes = HashSet::new();
+ let mut added_edges = HashSet::new();
+ let mut root_node_id: Option = None;
+
+ let sv = SchemaVariant::get_by_id(&ctx, request.schema_variant_id).await?;
+
+ let sv_node = {
+ let mut workspace_snapshot = ctx.workspace_snapshot()?.try_lock()?;
+ let node_idx = workspace_snapshot.get_node_index_by_id(request.schema_variant_id)?;
+ let sv_node_weight = workspace_snapshot.get_node_weight(node_idx)?;
+
+ added_nodes.insert(sv_node_weight.id());
+ GraphVizNode {
+ id: sv_node_weight.id(),
+ content_kind: sv_node_weight.content_address_discriminants(),
+ node_kind: sv_node_weight.into(),
+ name: Some(sv.name().to_owned()),
+ }
+ };
+
+ nodes.push(sv_node);
+
+ // descend
+ let mut work_queue: VecDeque = VecDeque::from([request.schema_variant_id.into()]);
+ while let Some(id) = work_queue.pop_front() {
+ let mut workspace_snapshot = ctx.workspace_snapshot()?.try_lock()?;
+ for target in workspace_snapshot.all_outgoing_targets(id)? {
+ work_queue.push_back(target.id());
+ if !added_edges.contains(&(id, target.id())) {
+ added_edges.insert((id, target.id()));
+ edges.push(GraphVizEdge {
+ from: id,
+ to: target.id(),
+ });
+ }
+ let name = match &target {
+ NodeWeight::Category(inner) => Some(inner.kind().to_string()),
+ NodeWeight::Func(inner) => {
+ func_nodes.push(inner.id());
+ Some(inner.name().to_owned())
+ }
+ NodeWeight::Prop(inner) => Some(inner.name().to_owned()),
+ _ => None,
+ };
+
+ if !added_nodes.contains(&target.id()) {
+ added_nodes.insert(target.id());
+ nodes.push(GraphVizNode {
+ id: target.id(),
+ content_kind: target.content_address_discriminants(),
+ node_kind: target.into(),
+ name,
+ })
+ }
+ }
+ }
+
+ // ascend
+ let mut work_queue: VecDeque = VecDeque::from([request.schema_variant_id.into()]);
+ while let Some(id) = work_queue.pop_front() {
+ let mut workspace_snapshot = ctx.workspace_snapshot()?.try_lock()?;
+ let sources = workspace_snapshot.all_incoming_sources(id)?;
+ if sources.is_empty() {
+ root_node_id = Some(id);
+ continue;
+ }
+
+ for source in sources {
+ work_queue.push_back(source.id());
+ if !added_edges.contains(&(source.id(), id)) {
+ added_edges.insert((source.id(), id));
+ edges.push(GraphVizEdge {
+ from: source.id(),
+ to: id,
+ });
+ }
+
+ let name = match &source {
+ NodeWeight::Category(inner) => Some(inner.kind().to_string()),
+ NodeWeight::Func(inner) => Some(inner.name().to_owned()),
+ NodeWeight::Prop(inner) => Some(inner.name().to_owned()),
+ _ => None,
+ };
+
+ if !added_nodes.contains(&source.id()) {
+ added_nodes.insert(source.id());
+ nodes.push(GraphVizNode {
+ id: source.id(),
+ content_kind: source.content_address_discriminants(),
+ node_kind: source.into(),
+ name,
+ })
+ }
+ }
+ }
+
+ // connect func_nodes to root
+ for func_id in func_nodes {
+ let mut workspace_snapshot = ctx.workspace_snapshot()?.try_lock()?;
+ for user_node_idx in workspace_snapshot
+ .incoming_sources_for_edge_weight_kind(func_id, EdgeWeightKindDiscriminants::Use)?
+ {
+ let user_node = workspace_snapshot
+ .get_node_weight(user_node_idx)?
+ .to_owned();
+
+ if let NodeWeight::Category(cat_inner) = &user_node {
+ let name = Some(cat_inner.kind().to_string());
+ if !added_edges.contains(&(func_id, cat_inner.id())) {
+ added_edges.insert((func_id, cat_inner.id()));
+ edges.push(GraphVizEdge {
+ from: cat_inner.id(),
+ to: func_id,
+ });
+ }
+ if !added_nodes.contains(&cat_inner.id()) {
+ added_nodes.insert(cat_inner.id());
+ nodes.push(GraphVizNode {
+ id: cat_inner.id(),
+ content_kind: user_node.content_address_discriminants(),
+ node_kind: user_node.to_owned().into(),
+ name,
+ })
+ }
+ for cat_user_node_idx in workspace_snapshot.incoming_sources_for_edge_weight_kind(
+ user_node.id(),
+ EdgeWeightKindDiscriminants::Use,
+ )? {
+ let node_weight = workspace_snapshot.get_node_weight(cat_user_node_idx)?;
+ match node_weight
+ .get_content_node_weight_of_kind(ContentAddressDiscriminants::Root)
+ {
+ Ok(root_content) => {
+ if !added_edges.contains(&(cat_inner.id(), root_content.id())) {
+ added_edges.insert((cat_inner.id(), root_content.id()));
+ edges.push(GraphVizEdge {
+ from: root_content.id(),
+ to: cat_inner.id(),
+ });
+ }
+ }
+ _ => continue,
+ }
+ }
+ }
+ }
+ }
+
+ let root_node_id = root_node_id.ok_or(GraphVizError::NoRootNode)?;
+
+ Ok(Json(GraphVizResponse {
+ nodes,
+ edges,
+ root_node_id,
+ }))
+}
+
+pub async fn nodes_edges(
+ HandlerContext(builder): HandlerContext,
+ AccessBuilder(request_ctx): AccessBuilder,
+ Query(request): Query,
+) -> GraphVizResult> {
+ let ctx = builder.build(request_ctx.build(request.visibility)).await?;
+
+ let mut workspace_snapshot = ctx.workspace_snapshot()?.try_lock()?;
+
+ let mut node_idx_to_id = HashMap::new();
+
+ let root_node_idx = workspace_snapshot.root()?;
+
+ let nodes = workspace_snapshot
+ .nodes()?
+ .map(|(weight, idx)| {
+ node_idx_to_id.insert(idx, weight.id());
+ let name = match weight {
+ NodeWeight::Category(inner) => Some(inner.kind().to_string()),
+ NodeWeight::Func(inner) => Some(inner.name().to_owned()),
+ NodeWeight::Prop(inner) => Some(inner.name().to_owned()),
+ _ => None,
+ };
+ GraphVizNode {
+ id: weight.id(),
+ content_kind: weight.content_address_discriminants(),
+ node_kind: weight.into(),
+ name,
+ }
+ })
+ .collect();
+
+ let edges = workspace_snapshot
+ .edges()?
+ .filter_map(
+ |(_, from, to)| match (node_idx_to_id.get(&from), node_idx_to_id.get(&to)) {
+ (None, _) | (_, None) => None,
+ (Some(&from), Some(&to)) => Some(GraphVizEdge { from, to }),
+ },
+ )
+ .collect();
+
+ let response = GraphVizResponse {
+ nodes,
+ edges,
+ root_node_id: node_idx_to_id
+ .get(&root_node_idx)
+ .copied()
+ .ok_or(GraphVizError::NoRootNode)?,
+ };
+
+ Ok(Json(response))
+}
+
+impl_default_error_into_response!(GraphVizError);
+
+pub fn routes() -> Router {
+ Router::new()
+ .route("/schema_variant", get(schema_variant))
+ .route("/nodes_edges", get(nodes_edges))
+}
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index c4e492fc88..0c7285008f 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -185,6 +185,18 @@ importers:
fontfaceobserver:
specifier: ^2.3.0
version: 2.3.0
+ graphology:
+ specifier: ^0.25.4
+ version: 0.25.4(graphology-types@0.24.7)
+ graphology-layout:
+ specifier: ^0.6.1
+ version: 0.6.1(graphology-types@0.24.7)
+ graphology-layout-forceatlas2:
+ specifier: ^0.10.1
+ version: 0.10.1(graphology-types@0.24.7)
+ graphology-layout-noverlap:
+ specifier: ^0.4.2
+ version: 0.4.2(graphology-types@0.24.7)
is-promise:
specifier: ^4.0.0
version: 4.0.0
@@ -233,6 +245,9 @@ importers:
reconnecting-websocket:
specifier: ^4.4.0
version: 4.4.0
+ sigma:
+ specifier: 3.0.0-beta.2
+ version: 3.0.0-beta.2(graphology-types@0.24.7)
tinycolor2:
specifier: ^1.4.2
version: 1.4.2
@@ -321,6 +336,9 @@ importers:
faker:
specifier: ^6.6.6
version: 6.6.6
+ graphology-types:
+ specifier: ^0.24.7
+ version: 0.24.7
unplugin-icons:
specifier: ^0.17.1
version: 0.17.1
@@ -5455,6 +5473,10 @@ packages:
vue: 3.3.4
dev: false
+ /@yomguithereal/helpers@1.1.1:
+ resolution: {integrity: sha512-UYvAq/XCA7xoh1juWDYsq3W0WywOB+pz8cgVnE1b45ZfdMhBvHDrgmSFG3jXeZSr2tMTYLGHFHON+ekG05Jebg==}
+ dev: false
+
/abab@2.0.6:
resolution: {integrity: sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==}
@@ -9343,7 +9365,6 @@ packages:
/events@3.3.0:
resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==}
engines: {node: '>=0.8.x'}
- dev: true
/execa@4.1.0:
resolution: {integrity: sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==}
@@ -10698,6 +10719,55 @@ packages:
resolution: {integrity: sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==}
dev: true
+ /graphology-layout-forceatlas2@0.10.1(graphology-types@0.24.7):
+ resolution: {integrity: sha512-ogzBeF1FvWzjkikrIFwxhlZXvD2+wlY54lqhsrWprcdPjopM2J9HoMweUmIgwaTvY4bUYVimpSsOdvDv1gPRFQ==}
+ peerDependencies:
+ graphology-types: '>=0.19.0'
+ dependencies:
+ graphology-types: 0.24.7
+ graphology-utils: 2.5.2(graphology-types@0.24.7)
+ dev: false
+
+ /graphology-layout-noverlap@0.4.2(graphology-types@0.24.7):
+ resolution: {integrity: sha512-13WwZSx96zim6l1dfZONcqLh3oqyRcjIBsqz2c2iJ3ohgs3605IDWjldH41Gnhh462xGB1j6VGmuGhZ2FKISXA==}
+ peerDependencies:
+ graphology-types: '>=0.19.0'
+ dependencies:
+ graphology-types: 0.24.7
+ graphology-utils: 2.5.2(graphology-types@0.24.7)
+ dev: false
+
+ /graphology-layout@0.6.1(graphology-types@0.24.7):
+ resolution: {integrity: sha512-m9aMvbd0uDPffUCFPng5ibRkb2pmfNvdKjQWeZrf71RS1aOoat5874+DcyNfMeCT4aQguKC7Lj9eCbqZj/h8Ag==}
+ peerDependencies:
+ graphology-types: '>=0.19.0'
+ dependencies:
+ graphology-types: 0.24.7
+ graphology-utils: 2.5.2(graphology-types@0.24.7)
+ pandemonium: 2.4.1
+ dev: false
+
+ /graphology-types@0.24.7:
+ resolution: {integrity: sha512-tdcqOOpwArNjEr0gNQKCXwaNCWnQJrog14nJNQPeemcLnXQUUGrsCWpWkVKt46zLjcS6/KGoayeJfHHyPDlvwA==}
+
+ /graphology-utils@2.5.2(graphology-types@0.24.7):
+ resolution: {integrity: sha512-ckHg8MXrXJkOARk56ZaSCM1g1Wihe2d6iTmz1enGOz4W/l831MBCKSayeFQfowgF8wd+PQ4rlch/56Vs/VZLDQ==}
+ peerDependencies:
+ graphology-types: '>=0.23.0'
+ dependencies:
+ graphology-types: 0.24.7
+ dev: false
+
+ /graphology@0.25.4(graphology-types@0.24.7):
+ resolution: {integrity: sha512-33g0Ol9nkWdD6ulw687viS8YJQBxqG5LWII6FI6nul0pq6iM2t5EKquOTFDbyTblRB3O9I+7KX4xI8u5ffekAQ==}
+ peerDependencies:
+ graphology-types: '>=0.24.0'
+ dependencies:
+ events: 3.3.0
+ graphology-types: 0.24.7
+ obliterator: 2.0.4
+ dev: false
+
/graphql@16.5.0:
resolution: {integrity: sha512-qbHgh8Ix+j/qY+a/ZcJnFQ+j8ezakqPiHwPiZhV/3PgGlgf96QMBB5/f2rkiC9sgLoy/xvT6TSiaf2nTHJh5iA==}
engines: {node: ^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0}
@@ -13941,6 +14011,12 @@ packages:
ufo: 1.3.1
dev: true
+ /mnemonist@0.39.6:
+ resolution: {integrity: sha512-A/0v5Z59y63US00cRSLiloEIw3t5G+MiKz4BhX21FI+YBJXBOGW0ohFxTxO08dsOYlzxo87T7vGfZKYp2bcAWA==}
+ dependencies:
+ obliterator: 2.0.4
+ dev: false
+
/module-definition@4.1.0:
resolution: {integrity: sha512-rHXi/DpMcD2qcKbPCTklDbX9lBKJrUSl971TW5l6nMpqKCIlzJqmQ8cfEF5M923h2OOLHPDVlh5pJxNyV+AJlw==}
engines: {node: '>=12'}
@@ -14673,6 +14749,10 @@ packages:
es-abstract: 1.20.4
dev: true
+ /obliterator@2.0.4:
+ resolution: {integrity: sha512-lgHwxlxV1qIg1Eap7LgIeoBWIMFibOjbrYPIPJZcI1mmGAI2m3lNYpK12Y+GBdPQ0U1hRwSord7GIaawz962qQ==}
+ dev: false
+
/omit.js@2.0.2:
resolution: {integrity: sha512-hJmu9D+bNB40YpL9jYebQl4lsTW6yEHRTroJzNLqQJYHm7c+NQnJGfZmIWh8S3q3KoaxV1aLhV6B3+0N0/kyJg==}
dev: true
@@ -15060,6 +15140,12 @@ packages:
- supports-color
dev: true
+ /pandemonium@2.4.1:
+ resolution: {integrity: sha512-wRqjisUyiUfXowgm7MFH2rwJzKIr20rca5FsHXCMNm1W5YPP1hCtrZfgmQ62kP7OZ7Xt+cR858aB28lu5NX55g==}
+ dependencies:
+ mnemonist: 0.39.6
+ dev: false
+
/parallel-transform@1.2.0:
resolution: {integrity: sha512-P2vSmIu38uIlvdcU7fDkyrxj33gTUy/ABO5ZUbGowxNCopBq/OoD42bP4UmMrJoPyk4Uqf0mu3mtWBhHCZD8yg==}
dependencies:
@@ -16809,6 +16895,16 @@ packages:
resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==}
dev: true
+ /sigma@3.0.0-beta.2(graphology-types@0.24.7):
+ resolution: {integrity: sha512-MfQTEJGsykg/Ont4ZhExeFd/qrInjb3dR0NLvQCncxxR0vW8ZFm06bKoVt69Ss4h5HX8b+50rOglyL6ShRND4w==}
+ dependencies:
+ '@yomguithereal/helpers': 1.1.1
+ events: 3.3.0
+ graphology-utils: 2.5.2(graphology-types@0.24.7)
+ transitivePeerDependencies:
+ - graphology-types
+ dev: false
+
/signal-exit@3.0.7:
resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==}