From 8b312ac5f233c13c533808918d6826c61344cee2 Mon Sep 17 00:00:00 2001 From: Moritz Jung Date: Fri, 26 Apr 2024 22:17:52 +0200 Subject: [PATCH 01/65] rusty breadcrumbs --- esbuild.config.mjs | 29 + package-lock.json | 4 +- src/graph/MyMultiGraph.ts | 5 + src/graph/builders/explicit/dataview_note.ts | 22 +- src/graph/builders/explicit/date_note.ts | 32 +- src/graph/builders/explicit/dendron_note.ts | 54 +- src/graph/builders/explicit/folder_note.ts | 23 +- .../builders/explicit/johnny_decimal_note.ts | 30 +- src/graph/builders/explicit/list_note.ts | 86 +- src/graph/builders/explicit/regex_note.ts | 33 +- src/graph/builders/explicit/tag_note.ts | 32 +- src/graph/builders/explicit/typed_link.ts | 40 +- src/graph/builders/implied/transitive.ts | 92 +- src/graph/builders/index.ts | 171 +-- src/interfaces/graph.ts | 32 +- src/main.ts | 218 +-- wasm/.appveyor.yml | 11 + wasm/.gitignore | 6 + wasm/.travis.yml | 69 + wasm/Cargo.toml | 32 + wasm/README.md | 19 + wasm/src/graph.rs | 1254 +++++++++++++++++ wasm/src/graph_construction.rs | 81 ++ wasm/src/graph_rules.rs | 39 + wasm/src/graph_update.rs | 168 +++ wasm/src/lib.rs | 20 + wasm/src/utils.rs | 122 ++ wasm/tests/common/mod.rs | 68 + wasm/tests/graph.rs | 221 +++ wasm/tests/web.rs | 13 + 30 files changed, 2621 insertions(+), 405 deletions(-) create mode 100644 wasm/.appveyor.yml create mode 100644 wasm/.gitignore create mode 100644 wasm/.travis.yml create mode 100644 wasm/Cargo.toml create mode 100644 wasm/README.md create mode 100644 wasm/src/graph.rs create mode 100644 wasm/src/graph_construction.rs create mode 100644 wasm/src/graph_rules.rs create mode 100644 wasm/src/graph_update.rs create mode 100644 wasm/src/lib.rs create mode 100644 wasm/src/utils.rs create mode 100644 wasm/tests/common/mod.rs create mode 100644 wasm/tests/graph.rs create mode 100644 wasm/tests/web.rs diff --git a/esbuild.config.mjs b/esbuild.config.mjs index 3954065a..a0fde384 100644 --- a/esbuild.config.mjs +++ b/esbuild.config.mjs @@ -3,6 +3,8 @@ import esbuild from "esbuild"; import esbuildSvelte from "esbuild-svelte"; import process from "process"; import sveltePreprocess from "svelte-preprocess"; +import path from 'node:path'; +import fs from 'node:fs'; const banner = `/* THIS IS A GENERATED/BUNDLED FILE BY ESBUILD @@ -10,6 +12,32 @@ if you want to view the source, please visit the github repository of this plugi */ `; +const wasmPlugin = { + name: 'wasm', + setup(build) { + // Resolve ".wasm" files to a path with a namespace + build.onResolve({ filter: /\.wasm$/ }, args => { + if (args.resolveDir === '') { + return; // Ignore unresolvable paths + } + return { + path: path.isAbsolute(args.path) ? args.path : path.join(args.resolveDir, args.path), + namespace: 'wasm-binary', + }; + }); + + // Virtual modules in the "wasm-binary" namespace contain the + // actual bytes of the WebAssembly file. This uses esbuild's + // built-in "binary" loader instead of manually embedding the + // binary data inside JavaScript code ourselves. + build.onLoad({ filter: /.*/, namespace: 'wasm-binary' }, async args => ({ + contents: await fs.promises.readFile(args.path), + loader: 'binary', + })); + }, +}; + + const prod = process.argv[2] === "production"; const context = await esbuild.context({ @@ -45,6 +73,7 @@ const context = await esbuild.context({ compilerOptions: { css: true }, preprocess: sveltePreprocess(), }), + wasmPlugin, ], }); diff --git a/package-lock.json b/package-lock.json index 29d39bef..de946918 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "breadcrumbs", - "version": "4.2.4-beta", + "version": "4.2.11-beta", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "breadcrumbs", - "version": "4.2.4-beta", + "version": "4.2.11-beta", "license": "MIT", "dependencies": { "graphology": "^0.25.4", diff --git a/src/graph/MyMultiGraph.ts b/src/graph/MyMultiGraph.ts index 8a3eefe6..9248ac27 100644 --- a/src/graph/MyMultiGraph.ts +++ b/src/graph/MyMultiGraph.ts @@ -18,6 +18,11 @@ export type BCNodeAttributes = { ignore_out_edges?: true; }; +export type BCNode = { + id: string; + attr: BCNodeAttributes; +} + export const EDGE_ATTRIBUTES = [ "field", "explicit", diff --git a/src/graph/builders/explicit/dataview_note.ts b/src/graph/builders/explicit/dataview_note.ts index e84aacd2..6049433a 100644 --- a/src/graph/builders/explicit/dataview_note.ts +++ b/src/graph/builders/explicit/dataview_note.ts @@ -3,7 +3,7 @@ import { META_ALIAS } from "src/const/metadata_fields"; import { dataview_plugin } from "src/external/dataview/index"; import type { IDataview } from "src/external/dataview/interfaces"; import type { - BreadcrumbsError, + EdgeBuilderResults, ExplicitEdgeBuilder, } from "src/interfaces/graph"; import { log } from "src/logger"; @@ -55,11 +55,10 @@ const get_dataview_note_info = ( }; export const _add_explicit_edges_dataview_note: ExplicitEdgeBuilder = ( - graph, plugin, all_files, ) => { - const errors: BreadcrumbsError[] = []; + const results: EdgeBuilderResults = { nodes: [], edges: [], errors: [] } all_files.obsidian?.forEach( ({ file: dataview_note_file, cache: dataview_note_cache }) => { @@ -72,7 +71,7 @@ export const _add_explicit_edges_dataview_note: ExplicitEdgeBuilder = ( ); if (!dataview_note_info.ok) { if (dataview_note_info.error) - errors.push(dataview_note_info.error); + results.errors.push(dataview_note_info.error); return; } else { new Notice( @@ -91,7 +90,7 @@ export const _add_explicit_edges_dataview_note: ExplicitEdgeBuilder = ( dataview_note_path, ); if (!dataview_note_info.ok) { - if (dataview_note_info.error) errors.push(dataview_note_info.error); + if (dataview_note_info.error) results.errors.push(dataview_note_info.error); return; } const { field, query } = dataview_note_info.data; @@ -106,7 +105,7 @@ export const _add_explicit_edges_dataview_note: ExplicitEdgeBuilder = ( error instanceof Error ? error.message : error, ); - return errors.push({ + return results.errors.push({ code: "invalid_field_value", path: dataview_note_path, message: `dataview-note-query is not a valid dataview query: '${query}'`, @@ -115,17 +114,18 @@ export const _add_explicit_edges_dataview_note: ExplicitEdgeBuilder = ( pages.forEach((page) => { // NOTE: I _believe_ we don't need to even safe_add_node, since dv will only return resolved notes - graph.safe_add_directed_edge( - dataview_note_page.file.path, - page.file.path, - { + results.edges.push({ + source_id: dataview_note_page.file.path, + target_id: page.file.path, + attr: { field, explicit: true, source: "dataview_note", }, + } ); }); }); - return { errors }; + return results }; diff --git a/src/graph/builders/explicit/date_note.ts b/src/graph/builders/explicit/date_note.ts index f7b557a1..6a6d6538 100644 --- a/src/graph/builders/explicit/date_note.ts +++ b/src/graph/builders/explicit/date_note.ts @@ -1,6 +1,6 @@ import { DateTime } from "luxon"; import type { - BreadcrumbsError, + EdgeBuilderResults, ExplicitEdgeBuilder, } from "src/interfaces/graph"; import { Paths } from "src/utils/paths"; @@ -8,25 +8,25 @@ import { Paths } from "src/utils/paths"; // TODO: Option to point up to month, (and for month to point up to year?) export const _add_explicit_edges_date_note: ExplicitEdgeBuilder = ( - graph, plugin, all_files, ) => { - const errors: BreadcrumbsError[] = []; + const results: EdgeBuilderResults = { nodes: [], edges: [], errors: [] } const date_note_settings = plugin.settings.explicit_edge_sources.date_note; - if (!date_note_settings.enabled) return { errors }; + if (!date_note_settings.enabled) { return results } else if ( !plugin.settings.edge_fields.find( (field) => field.label === date_note_settings.default_field, ) ) { - errors.push({ + results.errors.push({ code: "invalid_setting_value", path: "explicit_edge_sources.date_note.default_field", message: `The default Date Note field "${date_note_settings.default_field}" is not a valid Breadcrumbs Edge field`, }); - return { errors }; + + return results } const date_notes: { @@ -85,24 +85,28 @@ export const _add_explicit_edges_date_note: ExplicitEdgeBuilder = ( ? date_notes.at(i + 1)?.basename ?? basename_plus_one_day : basename_plus_one_day; - const target_path = Paths.build( + const target_id = Paths.build( date_note.folder, target_basename, date_note.ext, ); // NOTE: We have a full path, so we can go straight to the file without the given source_path - const target_file = plugin.app.vault.getFileByPath(target_path); + const target_file = plugin.app.vault.getFileByPath(target_id); if (!target_file) { - graph.safe_add_node(target_path, { resolved: false }); + results.nodes.push({ id: target_id, attr: { resolved: false } }); } - graph.safe_add_directed_edge(date_note.path, target_path, { - explicit: true, - source: "date_note", - field: date_note_settings.default_field, + results.edges.push({ + target_id, + source_id: date_note.path, + attr: { + explicit: true, + source: "date_note", + field: date_note_settings.default_field, + } }); }); - return { errors }; + return results }; diff --git a/src/graph/builders/explicit/dendron_note.ts b/src/graph/builders/explicit/dendron_note.ts index 16746f2a..1e3191c6 100644 --- a/src/graph/builders/explicit/dendron_note.ts +++ b/src/graph/builders/explicit/dendron_note.ts @@ -2,6 +2,7 @@ import { META_ALIAS } from "src/const/metadata_fields"; import type { BCGraph } from "src/graph/MyMultiGraph"; import type { BreadcrumbsError, + EdgeBuilderResults, ExplicitEdgeBuilder, } from "src/interfaces/graph"; import type BreadcrumbsPlugin from "src/main"; @@ -48,34 +49,33 @@ const get_dendron_note_info = ( */ const handle_dendron_note = ( plugin: BreadcrumbsPlugin, - graph: BCGraph, - source_path: string, + results: EdgeBuilderResults, + source_id: string, source_metadata: Record | undefined, - errors: BreadcrumbsError[], ) => { const { delimiter } = plugin.settings.explicit_edge_sources.dendron_note; // NOTE: There are easier ways to do alot of the below. // But the `file` type between obsidian and dataview doesn't have the common fields needed. // So we rebuild from `path` - const source_basename_splits = Paths.basename(source_path).split(delimiter); + const source_basename_splits = Paths.basename(source_id).split(delimiter); if (source_basename_splits.length === 1) return; const dendron_note_info = get_dendron_note_info( plugin, source_metadata, - source_path, + source_id, ); if (!dendron_note_info.ok) { if (dendron_note_info.error) { - errors.push(dendron_note_info.error); + results.errors.push(dendron_note_info.error); } return; } - const target_path = Paths.build( + const target_id = Paths.build( // Use the same folder as the source - source_path.split("/").slice(0, -1).join("/"), + source_id.split("/").slice(0, -1).join("/"), // Go one note up source_basename_splits.slice(0, -1).join(delimiter), "md", @@ -83,57 +83,53 @@ const handle_dendron_note = ( const { field } = dendron_note_info.data; // target_path is now a full path, so we can check for it directly, instead of getFirstLinkpathDest - const target_file = plugin.app.vault.getFileByPath(target_path); + const target_file = plugin.app.vault.getFileByPath(target_id); if (!target_file) { - graph.safe_add_node(target_path, { resolved: false }); + results.nodes.push({ id: target_id, attr: { resolved: false } }); // If !target_file, we can recursively call handle_dendron_note // To add the unresolved edges along the way handle_dendron_note( plugin, - graph, - target_path, + results, + target_id, // This is really quite elegant :) // The unresolved note has no BC-dendron field, by definition // Passing undefined would just use the settings.default field // But we can propagate the field from the resolved source note { [META_ALIAS["dendron-note-field"]]: field }, - errors, ); } - graph.safe_add_directed_edge(source_path, target_path, { - field, - explicit: true, - source: "dendron_note", + results.edges.push({ + source_id, + target_id, + attr: { + field, + explicit: true, + source: "dendron_note", + } }); }; export const _add_explicit_edges_dendron_note: ExplicitEdgeBuilder = ( - graph, plugin, all_files, ) => { - const errors: BreadcrumbsError[] = []; + const results: EdgeBuilderResults = { nodes: [], edges: [], errors: [] } if (!plugin.settings.explicit_edge_sources.dendron_note.enabled) { - return { errors }; + return results } all_files.obsidian?.forEach(({ file, cache }) => { - handle_dendron_note( - plugin, - graph, - file.path, - cache?.frontmatter, - errors, - ); + handle_dendron_note(plugin, results, file.path, cache?.frontmatter); }); all_files.dataview?.forEach((page) => { - handle_dendron_note(plugin, graph, page.file.path, page, errors); + handle_dendron_note(plugin, results, page.file.path, page); }); - return { errors }; + return results }; diff --git a/src/graph/builders/explicit/folder_note.ts b/src/graph/builders/explicit/folder_note.ts index a9791466..6ab4ecfc 100644 --- a/src/graph/builders/explicit/folder_note.ts +++ b/src/graph/builders/explicit/folder_note.ts @@ -1,6 +1,7 @@ import { META_ALIAS } from "src/const/metadata_fields"; import type { BreadcrumbsError, + EdgeBuilderResults, ExplicitEdgeBuilder, } from "src/interfaces/graph"; import type { Result } from "src/interfaces/result"; @@ -71,11 +72,10 @@ const iterate_folder_files = async ( }; export const _add_explicit_edges_folder_note: ExplicitEdgeBuilder = async ( - graph, plugin, all_files, ) => { - const errors: BreadcrumbsError[] = []; + const results: EdgeBuilderResults = { nodes: [], edges: [], errors: [] } const folder_notes: { file: { path: string; folder: string }; @@ -92,7 +92,7 @@ export const _add_explicit_edges_folder_note: ExplicitEdgeBuilder = async ( folder_note_file.path, ); if (!folder_note_info.ok) { - if (folder_note_info.error) errors.push(folder_note_info.error); + if (folder_note_info.error) results.errors.push(folder_note_info.error); return; } @@ -113,7 +113,7 @@ export const _add_explicit_edges_folder_note: ExplicitEdgeBuilder = async ( folder_note_page.file.path, ); if (!folder_note_info.ok) { - if (folder_note_info.error) errors.push(folder_note_info.error); + if (folder_note_info.error) results.errors.push(folder_note_info.error); return; } @@ -135,24 +135,25 @@ export const _add_explicit_edges_folder_note: ExplicitEdgeBuilder = async ( if ( !target_path.endsWith(".md") || target_path === folder_note.path - ) + ) { return; + } // We know path is resolved - graph.safe_add_directed_edge( - folder_note.path, - target_path, - { + results.edges.push({ + target_id: target_path, + source_id: folder_note.path, + attr: { explicit: true, field: data.field, source: "folder_note", }, - ); + }); }, data.recurse, ), ), ); - return { errors }; + return results }; diff --git a/src/graph/builders/explicit/johnny_decimal_note.ts b/src/graph/builders/explicit/johnny_decimal_note.ts index 4d442f12..e815cb3e 100644 --- a/src/graph/builders/explicit/johnny_decimal_note.ts +++ b/src/graph/builders/explicit/johnny_decimal_note.ts @@ -2,6 +2,7 @@ import { META_ALIAS } from "src/const/metadata_fields"; import type { BCGraph } from "src/graph/MyMultiGraph"; import type { BreadcrumbsError, + EdgeBuilderResults, ExplicitEdgeBuilder, } from "src/interfaces/graph"; import type BreadcrumbsPlugin from "src/main"; @@ -48,10 +49,9 @@ const get_johnny_decimal_note_info = ( */ const handle_johnny_decimal_note = ( plugin: BreadcrumbsPlugin, - graph: BCGraph, + results: EdgeBuilderResults, source_note: JohnnyDecimalNote, notes: JohnnyDecimalNote[], - errors: BreadcrumbsError[], ) => { const johnny_decimal_note_info = get_johnny_decimal_note_info( plugin, @@ -60,7 +60,7 @@ const handle_johnny_decimal_note = ( ); if (!johnny_decimal_note_info.ok) { if (johnny_decimal_note_info.error) { - errors.push(johnny_decimal_note_info.error); + results.errors.push(johnny_decimal_note_info.error); } return; } @@ -85,15 +85,19 @@ const handle_johnny_decimal_note = ( // NOTE: I don't think this can ever happen... if target_note, then target_file must exist if (!target_file) { - graph.safe_add_node(target_note.path, { resolved: false }); + results.nodes.push({ id: target_note.path, attr: { resolved: false } }); } const { field } = johnny_decimal_note_info.data; - graph.safe_add_directed_edge(source_note.path, target_note.path, { - field, - explicit: true, - source: "johnny_decimal_note", + results.edges.push({ + source_id: source_note.path, + target_id: target_note.path, + attr: { + field, + explicit: true, + source: "johnny_decimal_note", + } }); }; @@ -105,14 +109,13 @@ type JohnnyDecimalNote = { }; export const _add_explicit_edges_johnny_decimal_note: ExplicitEdgeBuilder = ( - graph, plugin, all_files, ) => { - const errors: BreadcrumbsError[] = []; + const results: EdgeBuilderResults = { nodes: [], edges: [], errors: [] } if (!plugin.settings.explicit_edge_sources.johnny_decimal_note.enabled) { - return { errors }; + return results } const { delimiter } = @@ -155,12 +158,11 @@ export const _add_explicit_edges_johnny_decimal_note: ExplicitEdgeBuilder = ( johnny_decimal_notes.forEach((note) => { handle_johnny_decimal_note( plugin, - graph, + results, note, johnny_decimal_notes, - errors, ); }); - return { errors }; + return results }; diff --git a/src/graph/builders/explicit/list_note.ts b/src/graph/builders/explicit/list_note.ts index 29f2f8ee..37734a64 100644 --- a/src/graph/builders/explicit/list_note.ts +++ b/src/graph/builders/explicit/list_note.ts @@ -3,7 +3,7 @@ import { META_ALIAS } from "src/const/metadata_fields"; import type { IDataview } from "src/external/dataview/interfaces"; import type { BCGraph } from "src/graph/MyMultiGraph"; import type { - BreadcrumbsError, + EdgeBuilderResults, ExplicitEdgeBuilder, } from "src/interfaces/graph"; import type BreadcrumbsPlugin from "src/main"; @@ -103,17 +103,17 @@ const resolve_field_override = ( /** If a few conditions are met, add an edge from the current list item to the _next_ one on the same level */ const handle_neighbour_list_item = ({ - graph, plugin, - source_path, + results, + source_id, list_note_page, list_note_info, source_list_item_i, }: { - graph: BCGraph; - source_path: string; + source_id: string; plugin: BreadcrumbsPlugin; source_list_item_i: number; + results: EdgeBuilderResults; list_note_page: IDataview.Page; list_note_info: Extract< ReturnType, @@ -160,30 +160,33 @@ const handle_neighbour_list_item = ({ const neighbour_link = neighbour_list_item.outlinks.at(0); if (!neighbour_link) return; - const [path, file] = resolve_relative_target_path( + const [target_id, file] = resolve_relative_target_path( plugin.app, neighbour_link.path, list_note_page.file.path, ); if (!file) { - graph.safe_add_node(path, { resolved: false }); + results.nodes.push({ id: target_id, attr: { resolved: false } }); } // NOTE: Currently no support for field overrides for neighbour-fields - graph.safe_add_directed_edge(source_path, path, { - explicit: true, - source: "list_note", - field: list_note_info.data.neighbour_field, + results.edges.push({ + source_id, + target_id, + attr: { + explicit: true, + source: "list_note", + field: list_note_info.data.neighbour_field, + } }); }; export const _add_explicit_edges_list_note: ExplicitEdgeBuilder = ( - graph, plugin, all_files, ) => { - const errors: BreadcrumbsError[] = []; + const results: EdgeBuilderResults = { nodes: [], edges: [], errors: [] } all_files.obsidian?.forEach( ({ file: list_note_file, cache: list_note_cache }) => { @@ -195,7 +198,7 @@ export const _add_explicit_edges_list_note: ExplicitEdgeBuilder = ( list_note_file.path, ); if (!list_note_info.ok) { - if (list_note_info.error) errors.push(list_note_info.error); + if (list_note_info.error) results.errors.push(list_note_info.error); return; } else { new Notice( @@ -218,7 +221,7 @@ export const _add_explicit_edges_list_note: ExplicitEdgeBuilder = ( list_note_page.file.path, ); if (!list_note_info.ok) { - if (list_note_info.error) errors.push(list_note_info.error); + if (list_note_info.error) results.errors.push(list_note_info.error); return; } @@ -239,7 +242,7 @@ export const _add_explicit_edges_list_note: ExplicitEdgeBuilder = ( // The node wouldn't have been added in the simple_loop if it wasn't resolved. if (!source_file) { - graph.safe_add_node(source_path, { resolved: false }); + results.nodes.push({ id: source_path, attr: { resolved: false } }); } // Then, add the edge from the list_note itself, to the top-level list_items (if it's not excluded) @@ -257,21 +260,23 @@ export const _add_explicit_edges_list_note: ExplicitEdgeBuilder = ( if (!source_override_field.ok) { if (source_override_field.error) { - errors.push(source_override_field.error); + results.errors.push(source_override_field.error); } return; } - graph.safe_add_directed_edge( - list_note_page.file.path, - source_path, + results.edges.push( { - explicit: true, - source: "list_note", - field: - source_override_field.data?.field ?? - list_note_info.data.field, - }, + source_id: list_note_page.file.path, + target_id: source_path, + attr: { + explicit: true, + source: "list_note", + field: + source_override_field.data?.field ?? + list_note_info.data.field, + }, + } ); } @@ -279,14 +284,15 @@ export const _add_explicit_edges_list_note: ExplicitEdgeBuilder = ( // to prevent multiple levels of if statement nesting if (list_note_info.data.neighbour_field) { handle_neighbour_list_item({ - graph, plugin, - source_path, - list_note_info, + results, list_note_page, + list_note_info, source_list_item_i, + source_id: source_path, }); } + source_list_item.children.forEach((target_list_item) => { const target_link = target_list_item.outlinks.at(0); if (!target_link) return; @@ -299,7 +305,7 @@ export const _add_explicit_edges_list_note: ExplicitEdgeBuilder = ( if (!target_override_field.ok) { if (target_override_field.error) { - errors.push(target_override_field.error); + results.errors.push(target_override_field.error); } return; } @@ -316,20 +322,24 @@ export const _add_explicit_edges_list_note: ExplicitEdgeBuilder = ( // But then I'd need to break up the iteration to first gather all sources, then handle the targets // This way we can guarentee the target exists if (!target_file) { - graph.safe_add_node(target_path, { resolved: false }); + results.nodes.push({ id: target_path, attr: { resolved: false } }); } - graph.safe_add_directed_edge(source_path, target_path, { - explicit: true, - source: "list_note", - field: - target_override_field.data?.field ?? - list_note_info.data.field, + results.edges.push({ + source_id: source_path, + target_id: target_path, + attr: { + explicit: true, + source: "list_note", + field: + target_override_field.data?.field ?? + list_note_info.data.field, + } }); }); }, ); }); - return { errors }; + return results }; diff --git a/src/graph/builders/explicit/regex_note.ts b/src/graph/builders/explicit/regex_note.ts index 36e79b58..4c46013d 100644 --- a/src/graph/builders/explicit/regex_note.ts +++ b/src/graph/builders/explicit/regex_note.ts @@ -1,6 +1,6 @@ import { META_ALIAS } from "src/const/metadata_fields"; import type { - BreadcrumbsError, + EdgeBuilderResults, ExplicitEdgeBuilder, } from "src/interfaces/graph"; import { log } from "src/logger"; @@ -74,11 +74,10 @@ const get_regex_note_info = ( }; export const _add_explicit_edges_regex_note: ExplicitEdgeBuilder = ( - graph, plugin, all_files, ) => { - const errors: BreadcrumbsError[] = []; + const results: EdgeBuilderResults = { nodes: [], edges: [], errors: [] } const regex_note_files: { path: string; @@ -93,7 +92,7 @@ export const _add_explicit_edges_regex_note: ExplicitEdgeBuilder = ( all_files.obsidian?.forEach(({ file, cache }) => { const info = get_regex_note_info(plugin, cache?.frontmatter, file.path); if (!info.ok) { - if (info.error) errors.push(info.error); + if (info.error) results.errors.push(info.error); return; } @@ -104,7 +103,7 @@ export const _add_explicit_edges_regex_note: ExplicitEdgeBuilder = ( const { file } = page; const info = get_regex_note_info(plugin, page, file.path); if (!info.ok) { - if (info.error) errors.push(info.error); + if (info.error) results.errors.push(info.error); return; } @@ -112,23 +111,27 @@ export const _add_explicit_edges_regex_note: ExplicitEdgeBuilder = ( }); // Return early before bringing all nodes into memory - if (!regex_note_files) return { errors }; + if (!regex_note_files) return results - const nodes = graph.mapNodes((id) => id); + const nodes = all_files.obsidian?.map(note => note.file.path) + ?? all_files.dataview?.map(note => note.file.path) + ?? [] // Won't happen, but makes TS happy regex_note_files.forEach((regex_note) => { nodes .filter((node) => regex_note.info.regex.test(node)) - .forEach((target_path) => { - // NOTE: We don't need to safe_add_nodes because the list of possible targets is already from the graph - - graph.safe_add_directed_edge(regex_note.path, target_path, { - explicit: true, - source: "regex_note", - field: regex_note.info.field, + .forEach((target_id) => { + results.edges.push({ + target_id, + source_id: regex_note.path, + attr: { + explicit: true, + source: "regex_note", + field: regex_note.info.field, + } }); }); }); - return { errors }; + return results }; diff --git a/src/graph/builders/explicit/tag_note.ts b/src/graph/builders/explicit/tag_note.ts index e6ec8fd1..02c1ff85 100644 --- a/src/graph/builders/explicit/tag_note.ts +++ b/src/graph/builders/explicit/tag_note.ts @@ -1,6 +1,6 @@ import { META_ALIAS } from "src/const/metadata_fields"; import type { - BreadcrumbsError, + EdgeBuilderResults, ExplicitEdgeBuilder, } from "src/interfaces/graph"; import { log } from "src/logger"; @@ -18,6 +18,7 @@ const get_tag_note_info = ( let raw_tag = metadata[META_ALIAS["tag-note-tag"]]; if (!raw_tag) { raw_tag = metadata["BC-tag-note"]; + if (raw_tag) { log.warn( `'BC-tag-note' is deprecated in favor of ${META_ALIAS["tag-note-tag"]}`, @@ -62,11 +63,10 @@ const get_tag_note_info = ( }; export const _add_explicit_edges_tag_note: ExplicitEdgeBuilder = ( - graph, plugin, all_files, ) => { - const errors: BreadcrumbsError[] = []; + const results: EdgeBuilderResults = { nodes: [], edges: [], errors: [], } // More efficient than quadratic looping over all_files, // We gather the tag_notes, and the tags the each note has in one go @@ -101,7 +101,7 @@ export const _add_explicit_edges_tag_note: ExplicitEdgeBuilder = ( tag_note_file.path, ); if (!tag_note_info.ok) { - if (tag_note_info.error) errors.push(tag_note_info.error); + if (tag_note_info.error) results.errors.push(tag_note_info.error); return; } @@ -132,7 +132,7 @@ export const _add_explicit_edges_tag_note: ExplicitEdgeBuilder = ( tag_note_file.path, ); if (!tag_note_info.ok) { - if (tag_note_info.error) errors.push(tag_note_info.error); + if (tag_note_info.error) results.errors.push(tag_note_info.error); return; } const { tag, field, exact } = tag_note_info.data; @@ -153,21 +153,25 @@ export const _add_explicit_edges_tag_note: ExplicitEdgeBuilder = ( const target_paths = tag_note.exact ? tag_paths_map.get(tag_note.tag) : all_tags - .filter((tag) => tag.startsWith(tag_note.tag)) - // We know that the tag_note.tag is in the tag_paths_map, so this is safe - .flatMap((tag) => tag_paths_map.get(tag)!); + .filter((tag) => tag.startsWith(tag_note.tag)) + // We know that the tag_note.tag is in the tag_paths_map, so this is safe + .flatMap((tag) => tag_paths_map.get(tag)!); // Adding these edges is comparatively simple. // We know the target_path is resolved, since it only gets added to the map // if it's a resolved note with a tag in it target_paths?.forEach((target_path) => { - graph.safe_add_directed_edge(tag_note.source_path, target_path, { - explicit: true, - source: "tag_note", - field: tag_note.field, - }); + results.edges.push({ + source_id: tag_note.source_path, + target_id: target_path, + attr: { + explicit: true, + source: "tag_note", + field: tag_note.field, + } + }) }); }); - return { errors }; + return results }; diff --git a/src/graph/builders/explicit/typed_link.ts b/src/graph/builders/explicit/typed_link.ts index bcf0a021..f103c922 100644 --- a/src/graph/builders/explicit/typed_link.ts +++ b/src/graph/builders/explicit/typed_link.ts @@ -1,5 +1,5 @@ import type { - BreadcrumbsError, + EdgeBuilderResults, ExplicitEdgeBuilder, } from "src/interfaces/graph"; import { ensure_is_array } from "src/utils/arrays"; @@ -8,11 +8,10 @@ import { resolve_relative_target_path } from "src/utils/obsidian"; const MARKDOWN_LINK_REGEX = /\[(.+?)\]\((.+?)\)/; export const _add_explicit_edges_typed_link: ExplicitEdgeBuilder = ( - graph, plugin, all_files, ) => { - const errors: BreadcrumbsError[] = []; + const results: EdgeBuilderResults = { nodes: [], edges: [], errors: [] } const field_labels = new Set( plugin.settings.edge_fields.map((f) => f.label), @@ -29,7 +28,7 @@ export const _add_explicit_edges_typed_link: ExplicitEdgeBuilder = ( const field = target_link.key.split(".")[0]; if (!field_labels.has(field)) return; - const [target_path, target_file] = resolve_relative_target_path( + const [target_id, target_file] = resolve_relative_target_path( plugin.app, target_link.link, source_file.path, @@ -37,13 +36,17 @@ export const _add_explicit_edges_typed_link: ExplicitEdgeBuilder = ( if (!target_file) { // Unresolved nodes don't have aliases - graph.safe_add_node(target_path, { resolved: false }); + results.nodes.push({ id: target_id, attr: { resolved: false } }); } - graph.safe_add_directed_edge(source_file.path, target_path, { - field, - explicit: true, - source: "typed_link", + results.edges.push({ + target_id, + source_id: source_file.path, + attr: { + field, + explicit: true, + source: "typed_link", + } }); }); }, @@ -84,14 +87,14 @@ export const _add_explicit_edges_typed_link: ExplicitEdgeBuilder = ( // @ts-expect-error: instanceof didn't work here? target_link?.isLuxonDateTime ) { - errors.push({ + results.errors.push({ path: source_file.path, code: "invalid_field_value", message: `Invalid value for field '${field}': '${target_link}'. Dataview DateTime values are not supported, since they don't preserve the original date string.`, }); } else { // It's a BC field, with a definitely invalid value, cause it's not a link - errors.push({ + results.errors.push({ path: source_file.path, code: "invalid_field_value", message: `Invalid value for field '${field}': '${target_link}'. Expected wikilink or markdown link.`, @@ -110,22 +113,23 @@ export const _add_explicit_edges_typed_link: ExplicitEdgeBuilder = ( if (!target_file) { // It's an unresolved link, so we add a node for it // But still do it safely, as a previous file may point to the same unresolved node - graph.safe_add_node(target_path, { resolved: false }); + results.nodes.push({ id: target_path, attr: { resolved: false } }); } // If the file exists, we should have already added a node for it in the simple loop over all markdown files - graph.safe_add_directed_edge( - source_file.path, - target_path, - { + results.edges.push({ + source_id: source_file.path, + target_id: target_path, + attr: { field, explicit: true, source: "typed_link", - }, + } + }, ); }); }); }); - return { errors }; + return results }; diff --git a/src/graph/builders/implied/transitive.ts b/src/graph/builders/implied/transitive.ts index 753cfe3c..a1a7e19e 100644 --- a/src/graph/builders/implied/transitive.ts +++ b/src/graph/builders/implied/transitive.ts @@ -1,58 +1,58 @@ import type { BCGraph } from "src/graph/MyMultiGraph"; import { Traverse } from "src/graph/traverse"; -import type { ImpliedEdgeBuilderResults } from "src/interfaces/graph"; +import type { EdgeBuilderResults } from "src/interfaces/graph"; import type { BreadcrumbsSettings } from "src/interfaces/settings"; import type BreadcrumbsPlugin from "src/main"; import { get_transitive_rule_name } from "src/utils/transitive_rules"; -export const _add_implied_edges_transitive = ( - graph: BCGraph, - plugin: BreadcrumbsPlugin, - rule: BreadcrumbsSettings["implied_relations"]["transitive"][number], - round: number, -) => { - const results: ImpliedEdgeBuilderResults = { edges: [], errors: [] }; +// export const _add_implied_edges_transitive = ( +// graph: BCGraph, +// plugin: BreadcrumbsPlugin, +// rule: BreadcrumbsSettings["implied_relations"]["transitive"][number], +// round: number, +// ) => { +// const results: EdgeBuilderResults = { edges: [], errors: [] }; - if (rule.rounds < round) { - return results; - } else if ( - !plugin.settings.edge_fields.find((f) => f.label === rule.close_field) - ) { - results.errors.push({ - code: "invalid_setting_value", - path: "implied_relations.transitive[].close_field", - message: `close_field is not a valid BC field: '${rule.close_field}'`, - }); +// if (rule.rounds < round) { +// return results; +// } else if ( +// !plugin.settings.edge_fields.find((f) => f.label === rule.close_field) +// ) { +// results.errors.push({ +// code: "invalid_setting_value", +// path: "implied_relations.transitive[].close_field", +// message: `close_field is not a valid BC field: '${rule.close_field}'`, +// }); - return results; - } +// return results; +// } - const implied_kind = - `transitive:${get_transitive_rule_name(rule)}` as const; +// const implied_kind = +// `transitive:${get_transitive_rule_name(rule)}` as const; - graph.forEachNode((start_node) => { - Traverse.get_transitive_chain_target_ids( - graph, - start_node, - rule.chain, - (item) => item.edge.target_id !== start_node, - ).forEach((end_node) => { - const [source_id, target_id] = rule.close_reversed - ? [end_node, start_node] - : [start_node, end_node]; +// graph.forEachNode((start_node) => { +// Traverse.get_transitive_chain_target_ids( +// graph, +// start_node, +// rule.chain, +// (item) => item.edge.target_id !== start_node, +// ).forEach((end_node) => { +// const [source_id, target_id] = rule.close_reversed +// ? [end_node, start_node] +// : [start_node, end_node]; - results.edges.push({ - source_id, - target_id, - attr: { - round, - implied_kind, - explicit: false, - field: rule.close_field, - }, - }); - }); - }); +// results.edges.push({ +// source_id, +// target_id, +// attr: { +// round, +// implied_kind, +// explicit: false, +// field: rule.close_field, +// }, +// }); +// }); +// }); - return results; -}; +// return results; +// }; diff --git a/src/graph/builders/index.ts b/src/graph/builders/index.ts index 14bfdfbf..3cbcc540 100644 --- a/src/graph/builders/index.ts +++ b/src/graph/builders/index.ts @@ -7,141 +7,146 @@ import { Timer } from "src/utils/timer"; import { BCGraph, type BCNodeAttributes } from "../MyMultiGraph"; import { add_explicit_edges } from "./explicit"; import { get_all_files, type AllFiles } from "./explicit/files"; -import { _add_implied_edges_transitive } from "./implied/transitive"; +// import { _add_implied_edges_transitive } from "./implied/transitive"; +import { GraphConstructionNodeData } from "wasm/pkg/breadcrumbs_graph_wasm"; + +const get_initial_nodes = (all_files: AllFiles) => { + const nodes: GraphConstructionNodeData[] = [] -const add_initial_nodes = (graph: BCGraph, all_files: AllFiles) => { if (all_files.obsidian) { all_files.obsidian.forEach(({ file, cache }) => { - const node_attr: BCNodeAttributes = { + const attr: BCNodeAttributes = { resolved: true, }; const aliases = cache?.frontmatter?.aliases as unknown; if (Array.isArray(aliases) && aliases.length > 0) { - node_attr.aliases = aliases; + attr.aliases = aliases; } if (cache?.frontmatter?.[META_ALIAS["ignore-in-edges"]]) { - node_attr.ignore_in_edges = true; + attr.ignore_in_edges = true; } if (cache?.frontmatter?.[META_ALIAS["ignore-out-edges"]]) { - node_attr.ignore_out_edges = true; + attr.ignore_out_edges = true; } - graph.addNode(file.path, node_attr); + nodes.push(new GraphConstructionNodeData(file.path, attr.aliases ?? [], true, attr.ignore_in_edges ?? false, attr.ignore_out_edges ?? false)) }); } else { all_files.dataview.forEach((page) => { - const node_attr: BCNodeAttributes = { + const attr: BCNodeAttributes = { resolved: true, }; const aliases = page.file.aliases.values; if (Array.isArray(aliases) && aliases.length > 0) { - node_attr.aliases = aliases; + attr.aliases = aliases; } if (page[META_ALIAS["ignore-in-edges"]]) { - node_attr.ignore_in_edges = true; + attr.ignore_in_edges = true; } if (page[META_ALIAS["ignore-out-edges"]]) { - node_attr.ignore_out_edges = true; + attr.ignore_out_edges = true; } - graph.addNode(page.file.path, node_attr); + nodes.push(new GraphConstructionNodeData(page.file.path, attr.aliases ?? [], true, attr.ignore_in_edges ?? false, attr.ignore_out_edges ?? false)) }); } + + return nodes }; export const rebuild_graph = async (plugin: BreadcrumbsPlugin) => { const timer = new Timer(); - const timer2 = new Timer(); - - // Make a new graph, instead of mutating the old one - const graph = new BCGraph(); // Get once, send to all builders const all_files = get_all_files(plugin.app); // Add initial nodes - add_initial_nodes(graph, all_files); + const nodes = get_initial_nodes(all_files); - log.debug(timer.elapsedMessage("Adding initial nodes")); + log.debug(timer.elapsedMessage("get_initial_nodes")); timer.reset(); // Explicit edges const explicit_edge_results = await Promise.all( EXPLICIT_EDGE_SOURCES.map(async (source) => { - const result = await add_explicit_edges[source]( - graph, + const results = await add_explicit_edges[source]( plugin, all_files, ); - return { source, errors: result.errors }; + return { source, results }; }), ); - log.debug(timer.elapsedMessage("Adding initial edges")); - timer.reset(); - - const max_implied_relationship_rounds = Math.max( - ...plugin.settings.implied_relations.transitive.map( - (imp) => imp.rounds, - ), - ); - - const implied_edge_results: { transitive: BreadcrumbsError[] } = { - transitive: [], - }; - - // Track which fields get added, clearing each round - // This lets us check if a transitive rule even needs to be considered - const added_fields = new Set(); - - // Add all the fields from the initial edges - for (const edge of graph.edgeEntries()) { - added_fields.add(edge.attributes.field); - } - - for (let round = 1; round <= max_implied_relationship_rounds; round++) { - const edges: EdgeToAdd[] = []; - - plugin.settings.implied_relations.transitive.forEach((rule) => { - // If none of the fields added in the previous round are in this rule, skip it - if (!rule.chain.some((attr) => added_fields.has(attr.field!))) { - return; - } - - const result = _add_implied_edges_transitive( - graph, - plugin, - rule, - round, - ); - - edges.push(...result.edges); - implied_edge_results.transitive.push(...result.errors); - }); - - // We don't need the previous fields anymore - added_fields.clear(); - - // PERF: Break if no edges were added. We've reached a fixed point - if (edges.length === 0) break; - else { - edges.forEach((edge) => { - graph.safe_add_directed_edge( - edge.source_id, - edge.target_id, - edge.attr, - ) && added_fields.add(edge.attr.field); - }); - } - } - - log.debug(timer.elapsedMessage("Adding implied edges")); - log.debug(timer2.elapsedMessage("Total Graph building")); - - return { graph, explicit_edge_results, implied_edge_results }; + log.debug(timer.elapsedMessage("Collecting edges and nodes")); + + // TODO + plugin.graph.build_graph(nodes, edges); + + // log.debug(timer.elapsedMessage("Adding initial edges")); + // timer.reset(); + + // const max_implied_relationship_rounds = Math.max( + // ...plugin.settings.implied_relations.transitive.map( + // (imp) => imp.rounds, + // ), + // ); + + // const implied_edge_results: { transitive: BreadcrumbsError[] } = { + // transitive: [], + // }; + + // // Track which fields get added, clearing each round + // // This lets us check if a transitive rule even needs to be considered + // const added_fields = new Set(); + + // // Add all the fields from the initial edges + // for (const edge of graph.edgeEntries()) { + // added_fields.add(edge.attributes.field); + // } + + // for (let round = 1; round <= max_implied_relationship_rounds; round++) { + // const edges: EdgeToAdd[] = []; + + // plugin.settings.implied_relations.transitive.forEach((rule) => { + // // If none of the fields added in the previous round are in this rule, skip it + // if (!rule.chain.some((attr) => added_fields.has(attr.field!))) { + // return; + // } + + // const result = _add_implied_edges_transitive( + // graph, + // plugin, + // rule, + // round, + // ); + + // edges.push(...result.edges); + // implied_edge_results.transitive.push(...result.errors); + // }); + + // // We don't need the previous fields anymore + // added_fields.clear(); + + // // PERF: Break if no edges were added. We've reached a fixed point + // if (edges.length === 0) break; + // else { + // edges.forEach((edge) => { + // graph.safe_add_directed_edge( + // edge.source_id, + // edge.target_id, + // edge.attr, + // ) && added_fields.add(edge.attr.field); + // }); + // } + // } + + // log.debug(timer.elapsedMessage("Adding implied edges")); + // log.debug(timer2.elapsedMessage("Total Graph building")); + + return { explicit_edge_results }; }; diff --git a/src/interfaces/graph.ts b/src/interfaces/graph.ts index 663a9340..d46980e1 100644 --- a/src/interfaces/graph.ts +++ b/src/interfaces/graph.ts @@ -1,4 +1,4 @@ -import type { BCEdgeAttributes, BCGraph } from "src/graph/MyMultiGraph"; +import type { BCEdgeAttributes, BCNode } from "src/graph/MyMultiGraph"; import type { AllFiles } from "src/graph/builders/explicit/files"; import type BreadcrumbsPlugin from "src/main"; import type { MaybePromise } from "."; @@ -7,25 +7,14 @@ export type BreadcrumbsError = { // TODO: Differentiate between invalid edge-field and invalid metadata-field values // BUT: Some errors might be a metadata field with an invalid edge-field value code: - | "deprecated_field" - | "invalid_field_value" - | "invalid_setting_value" - | "invalid_yaml"; + | "deprecated_field" + | "invalid_field_value" + | "invalid_setting_value" + | "invalid_yaml"; message: string; path: string; }; -// NOTE: A completely different approach is to do it on a single node level -// This way, we could rebuild the edges for a particular node as needed -/** "Extension" system. Takes in current state of plugin & graph, and adds to the graph */ -export type ExplicitEdgeBuilder = ( - graph: BCGraph, - plugin: BreadcrumbsPlugin, - all_files: AllFiles, -) => MaybePromise<{ - errors: BreadcrumbsError[]; -}>; - /** The values passed into safe_add_edge */ export type EdgeToAdd = { source_id: string; @@ -33,7 +22,16 @@ export type EdgeToAdd = { attr: BCEdgeAttributes; }; -export type ImpliedEdgeBuilderResults = { +export type EdgeBuilderResults = { + nodes: BCNode[] edges: EdgeToAdd[]; errors: BreadcrumbsError[]; }; + +// NOTE: A completely different approach is to do it on a single node level +// This way, we could rebuild the edges for a particular node as needed +/** "Extension" system. Takes in current state of plugin & graph, and adds to the graph */ +export type ExplicitEdgeBuilder = ( + plugin: BreadcrumbsPlugin, + all_files: AllFiles, +) => MaybePromise; diff --git a/src/main.ts b/src/main.ts index c8b0c564..71f636d6 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,4 +1,4 @@ -import { Notice, Plugin, TFile, WorkspaceLeaf } from "obsidian"; +import { Events, Notice, Plugin, TFile, WorkspaceLeaf } from "obsidian"; import { Codeblocks } from "src/codeblocks"; import { DEFAULT_SETTINGS } from "src/const/settings"; import { VIEW_IDS } from "src/const/views"; @@ -26,11 +26,28 @@ import { deep_merge_objects } from "./utils/objects"; import { Timer } from "./utils/timer"; import { redraw_page_views } from "./views/page"; import { TreeView } from "./views/tree"; +import wasmbin from '../wasm/pkg/breadcrumbs_graph_wasm_bg.wasm'; +import init, { + type InitInput, + type NoteGraph, + TransitiveGraphRule, + create_graph, + GraphConstructionNodeData, + GraphConstructionEdgeData, + BatchGraphUpdate, + RemoveNoteGraphUpdate, + AddNoteGraphUpdate, +} from '../wasm/pkg'; + +export enum BCEvent { + GRAPH_UPDATE = "graph-update", +} export default class BreadcrumbsPlugin extends Plugin { settings!: BreadcrumbsSettings; - graph = new BCGraph(); + graph!: NoteGraph; api!: BCAPI; + events!: Events; async onload() { // Settings @@ -42,6 +59,19 @@ export default class BreadcrumbsPlugin extends Plugin { log.info("loading Breadcrumbs plugin"); log.debug("settings >", this.settings); + // Init event bus + this.events = new Events(); + + // Init wasm + await init(wasmbin); + this.graph = create_graph(); + this.graph.set_update_callback(() => { + // funny micro task so that the rust update function finishes before we access the graph again for the visualization + // this is needed because otherwise we get an "recursive use of an object detected which would lead to unsafe aliasing in rust" error + // see https://github.com/rustwasm/wasm-bindgen/issues/1578 + queueMicrotask(() => this.events.trigger(BCEvent.GRAPH_UPDATE)); + }); + /// Migrations this.settings = migrate_old_settings(this.settings); await this.saveSettings(); @@ -143,59 +173,60 @@ export default class BreadcrumbsPlugin extends Plugin { }), ); - /// Vault - this.registerEvent( - this.app.vault.on("create", (file) => { - log.debug("on:create >", file.path); - - if (file instanceof TFile) { - // This isn't perfect, but it stops any "node doesn't exist" errors - // The user will have to refresh to add any relevant edges - this.graph.upsert_node(file.path, { resolved: true }); - - // NOTE: No need to this.refresh. The event triggers a layout-change anyway - } - }), - ); - - this.registerEvent( - this.app.vault.on("rename", (file, old_path) => { - log.debug("on:rename >", old_path, "->", file.path); - - if (file instanceof TFile) { - const res = this.graph.safe_rename_node( - old_path, - file.path, - ); - - if (!res.ok) { - log.error("safe_rename_node >", res.error.message); - } - - // NOTE: No need to this.refresh. The event triggers a layout-change anyway - } - }), - ); - - this.registerEvent( - this.app.vault.on("delete", (file) => { - log.debug("on:delete >", file.path); - - if (file instanceof TFile) { - // NOTE: Instead of dropping it, we mark it as unresolved. - // There are pros and cons to both, but unresolving it is less intense. - // There may still be a typed link:: to that file, so it shouldn't drop off the graph. - // So it's not perfect. Rebuilding the graph is the only way to be sure. - this.graph.setNodeAttribute( - file.path, - "resolved", - false, - ); - - // NOTE: No need to this.refresh. The event triggers a layout-change anyway - } - }), - ); + // TODO + // /// Vault + // this.registerEvent( + // this.app.vault.on("create", (file) => { + // log.debug("on:create >", file.path); + + // if (file instanceof TFile) { + // // This isn't perfect, but it stops any "node doesn't exist" errors + // // The user will have to refresh to add any relevant edges + // this.graph.upsert_node(file.path, { resolved: true }); + + // // NOTE: No need to this.refresh. The event triggers a layout-change anyway + // } + // }), + // ); + + // this.registerEvent( + // this.app.vault.on("rename", (file, old_path) => { + // log.debug("on:rename >", old_path, "->", file.path); + + // if (file instanceof TFile) { + // const res = this.graph.safe_rename_node( + // old_path, + // file.path, + // ); + + // if (!res.ok) { + // log.error("safe_rename_node >", res.error.message); + // } + + // // NOTE: No need to this.refresh. The event triggers a layout-change anyway + // } + // }), + // ); + + // this.registerEvent( + // this.app.vault.on("delete", (file) => { + // log.debug("on:delete >", file.path); + + // if (file instanceof TFile) { + // // NOTE: Instead of dropping it, we mark it as unresolved. + // // There are pros and cons to both, but unresolving it is less intense. + // // There may still be a typed link:: to that file, so it shouldn't drop off the graph. + // // So it's not perfect. Rebuilding the graph is the only way to be sure. + // this.graph.setNodeAttribute( + // file.path, + // "resolved", + // false, + // ); + + // // NOTE: No need to this.refresh. The event triggers a layout-change anyway + // } + // }), + // ); // Views this.registerView( @@ -248,24 +279,25 @@ export default class BreadcrumbsPlugin extends Plugin { }, }); - this.addCommand({ - id: "breadcrumbs:graph-stats", - name: "Show/Copy graph stats", - callback: async () => { - const stats = get_graph_stats(this.graph, { - groups: this.settings.edge_field_groups, - }); - log.feat("Graph stats >", stats); - - await navigator.clipboard.writeText( - JSON.stringify(stats, null, 2), - ); - - new Notice( - "Graph stats printed to console and copied to clipboard", - ); - }, - }); + // TODO + // this.addCommand({ + // id: "breadcrumbs:graph-stats", + // name: "Show/Copy graph stats", + // callback: async () => { + // const stats = get_graph_stats(this.graph, { + // groups: this.settings.edge_field_groups, + // }); + // log.feat("Graph stats >", stats); + + // await navigator.clipboard.writeText( + // JSON.stringify(stats, null, 2), + // ); + + // new Notice( + // "Graph stats printed to console and copied to clipboard", + // ); + // }, + // }); this.addCommand({ id: "breadcrumbs:freeze-implied-edges-to-note", @@ -318,7 +350,7 @@ export default class BreadcrumbsPlugin extends Plugin { log.debug("loaded Breadcrumbs plugin"); } - onunload() {} + onunload() { } async loadSettings() { this.settings = deep_merge_objects( @@ -352,30 +384,30 @@ export default class BreadcrumbsPlugin extends Plugin { : null; const rebuild_results = await rebuild_graph(this); - this.graph = rebuild_results.graph; + // this.graph = rebuild_results.graph; const explicit_edge_errors = rebuild_results.explicit_edge_results - .filter((result) => result.errors.length) + .filter(({ results }) => results.errors.length) .reduce( - (acc, { source, errors }) => { - acc[source] = errors; + (acc, { source, results }) => { + acc[source] = results.errors; return acc; }, {} as Record, ); - const implied_edge_results = Object.fromEntries( - Object.entries(rebuild_results.implied_edge_results) - .filter(([_, errors]) => errors.length) - .map(([implied_kind, errors]) => [implied_kind, errors]), - ); + // const implied_edge_results = Object.fromEntries( + // Object.entries(rebuild_results.implied_edge_results) + // .filter(([_, errors]) => errors.length) + // .map(([implied_kind, errors]) => [implied_kind, errors]), + // ); if (Object.keys(explicit_edge_errors).length) { log.warn("explicit_edge_errors >", explicit_edge_errors); } - if (Object.keys(implied_edge_results).length) { - log.warn("implied_edge_results >", implied_edge_results); - } + // if (Object.keys(implied_edge_results).length) { + // log.warn("implied_edge_results >", implied_edge_results); + // } notice?.setMessage( [ @@ -390,14 +422,14 @@ export default class BreadcrumbsPlugin extends Plugin { `- ${source}: ${errors.length} errors`, ), - implied_edge_results.length - ? "\nImplied edge errors (see console for details):" - : null, + // implied_edge_results.length + // ? "\nImplied edge errors (see console for details):" + // : null, - ...Object.entries(implied_edge_results).map( - ([implied_kind, errors]) => - `- ${implied_kind}: ${errors.length} errors`, - ), + // ...Object.entries(implied_edge_results).map( + // ([implied_kind, errors]) => + // `- ${implied_kind}: ${errors.length} errors`, + // ), ] .filter(Boolean) .join("\n"), diff --git a/wasm/.appveyor.yml b/wasm/.appveyor.yml new file mode 100644 index 00000000..50910bd6 --- /dev/null +++ b/wasm/.appveyor.yml @@ -0,0 +1,11 @@ +install: + - appveyor-retry appveyor DownloadFile https://win.rustup.rs/ -FileName rustup-init.exe + - if not defined RUSTFLAGS rustup-init.exe -y --default-host x86_64-pc-windows-msvc --default-toolchain nightly + - set PATH=%PATH%;C:\Users\appveyor\.cargo\bin + - rustc -V + - cargo -V + +build: false + +test_script: + - cargo test --locked diff --git a/wasm/.gitignore b/wasm/.gitignore new file mode 100644 index 00000000..4e301317 --- /dev/null +++ b/wasm/.gitignore @@ -0,0 +1,6 @@ +/target +**/*.rs.bk +Cargo.lock +bin/ +pkg/ +wasm-pack.log diff --git a/wasm/.travis.yml b/wasm/.travis.yml new file mode 100644 index 00000000..7a913256 --- /dev/null +++ b/wasm/.travis.yml @@ -0,0 +1,69 @@ +language: rust +sudo: false + +cache: cargo + +matrix: + include: + + # Builds with wasm-pack. + - rust: beta + env: RUST_BACKTRACE=1 + addons: + firefox: latest + chrome: stable + before_script: + - (test -x $HOME/.cargo/bin/cargo-install-update || cargo install cargo-update) + - (test -x $HOME/.cargo/bin/cargo-generate || cargo install --vers "^0.2" cargo-generate) + - cargo install-update -a + - curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh -s -- -f + script: + - cargo generate --git . --name testing + # Having a broken Cargo.toml (in that it has curlies in fields) anywhere + # in any of our parent dirs is problematic. + - mv Cargo.toml Cargo.toml.tmpl + - cd testing + - wasm-pack build + - wasm-pack test --chrome --firefox --headless + + # Builds on nightly. + - rust: nightly + env: RUST_BACKTRACE=1 + before_script: + - (test -x $HOME/.cargo/bin/cargo-install-update || cargo install cargo-update) + - (test -x $HOME/.cargo/bin/cargo-generate || cargo install --vers "^0.2" cargo-generate) + - cargo install-update -a + - rustup target add wasm32-unknown-unknown + script: + - cargo generate --git . --name testing + - mv Cargo.toml Cargo.toml.tmpl + - cd testing + - cargo check + - cargo check --target wasm32-unknown-unknown + - cargo check --no-default-features + - cargo check --target wasm32-unknown-unknown --no-default-features + - cargo check --no-default-features --features console_error_panic_hook + - cargo check --target wasm32-unknown-unknown --no-default-features --features console_error_panic_hook + - cargo check --no-default-features --features "console_error_panic_hook wee_alloc" + - cargo check --target wasm32-unknown-unknown --no-default-features --features "console_error_panic_hook wee_alloc" + + # Builds on beta. + - rust: beta + env: RUST_BACKTRACE=1 + before_script: + - (test -x $HOME/.cargo/bin/cargo-install-update || cargo install cargo-update) + - (test -x $HOME/.cargo/bin/cargo-generate || cargo install --vers "^0.2" cargo-generate) + - cargo install-update -a + - rustup target add wasm32-unknown-unknown + script: + - cargo generate --git . --name testing + - mv Cargo.toml Cargo.toml.tmpl + - cd testing + - cargo check + - cargo check --target wasm32-unknown-unknown + - cargo check --no-default-features + - cargo check --target wasm32-unknown-unknown --no-default-features + - cargo check --no-default-features --features console_error_panic_hook + - cargo check --target wasm32-unknown-unknown --no-default-features --features console_error_panic_hook + # Note: no enabling the `wee_alloc` feature here because it requires + # nightly for now. diff --git a/wasm/Cargo.toml b/wasm/Cargo.toml new file mode 100644 index 00000000..9ecfccd6 --- /dev/null +++ b/wasm/Cargo.toml @@ -0,0 +1,32 @@ +[package] +name = "breadcrumbs_graph_wasm" +version = "0.1.0" +authors = ["Moritz Jung ", "SkepticMystic"] +edition = "2018" + +[lib] +crate-type = ["cdylib", "rlib"] + +[features] +default = ["console_error_panic_hook"] + +[dependencies] +wasm-bindgen = "0.2.84" + +# The `console_error_panic_hook` crate provides better debugging of panics by +# logging them with `console.error`. This is great for development, but requires +# all the `std::fmt` and `std::panicking` infrastructure, so isn't great for +# code size when deploying. +console_error_panic_hook = { version = "0.1.7", optional = true } +petgraph = "0.6.4" +web-time = "1.1.0" +js-sys = "0.3.69" +itertools = "0.12.1" + +[dev-dependencies] +wasm-bindgen-test = "0.3.34" + +[profile.release] +# Tell `rustc` to optimize for small code size. +opt-level = 3 +lto = true diff --git a/wasm/README.md b/wasm/README.md new file mode 100644 index 00000000..62ceebae --- /dev/null +++ b/wasm/README.md @@ -0,0 +1,19 @@ +## Build + +Packages will be downloaded on the first build. + +```bash +wasm-pack build --target web +``` + +## Format + +```bash +cargo fmt +``` + +## Test + +```bash +wasm-pack test --node +``` \ No newline at end of file diff --git a/wasm/src/graph.rs b/wasm/src/graph.rs new file mode 100644 index 00000000..ad3a59d4 --- /dev/null +++ b/wasm/src/graph.rs @@ -0,0 +1,1254 @@ +use std::{ + collections::{HashMap, HashSet}, + fmt::Debug, +}; + +use itertools::{EitherOrBoth, Itertools}; +use petgraph::{ + stable_graph::{EdgeIndex, EdgeReference, Edges, NodeIndex, StableGraph}, + visit::{EdgeRef, IntoEdgeReferences, IntoNodeReferences, NodeRef}, + Directed, +}; +use wasm_bindgen::prelude::*; +use web_time::Instant; + +use crate::{ + graph_construction::{GraphConstructionEdgeData, GraphConstructionNodeData}, + graph_rules::TransitiveGraphRule, + graph_update::BatchGraphUpdate, + utils::{ + self, BreadthFirstTraversalDataStructure, DepthFirstTraversalDataStructure, + GraphTraversalDataStructure, NoteGraphError, Result, + }, +}; + +#[wasm_bindgen] +#[derive(Clone, Debug, PartialEq)] +pub struct EdgeData { + #[wasm_bindgen(skip)] + pub edge_type: String, + #[wasm_bindgen(skip)] + pub implied: bool, + #[wasm_bindgen(skip)] + pub round: u8, +} + +#[wasm_bindgen] +impl EdgeData { + #[wasm_bindgen(constructor)] + pub fn new(edge_type: String, implied: bool, round: u8) -> EdgeData { + EdgeData { + edge_type, + implied, + round, + } + } + + #[wasm_bindgen(getter)] + pub fn edge_type(&self) -> String { + self.edge_type.clone() + } + + #[wasm_bindgen(getter)] + pub fn implied(&self) -> bool { + self.implied + } + + #[wasm_bindgen(getter)] + pub fn round(&self) -> u8 { + self.round + } +} + +// impl PartialEq for EdgeData { +// fn eq(&self, other: &Self) -> bool { +// self.edge_type == other.edge_type +// } +// } + +#[wasm_bindgen] +#[derive(Clone, Debug, PartialEq)] +pub struct NodeData { + #[wasm_bindgen(skip)] + pub path: String, + #[wasm_bindgen(skip)] + pub aliases: Vec, + #[wasm_bindgen(skip)] + pub resolved: bool, + #[wasm_bindgen(skip)] + pub ignore_in_edges: bool, + #[wasm_bindgen(skip)] + pub ignore_out_edges: bool, +} + +#[wasm_bindgen] +impl NodeData { + #[wasm_bindgen(constructor)] + pub fn new( + path: String, + aliases: Vec, + resolved: bool, + ignore_in_edges: bool, + ignore_out_edges: bool, + ) -> NodeData { + NodeData { + path, + aliases, + resolved, + ignore_in_edges, + ignore_out_edges, + } + } + + #[wasm_bindgen(getter)] + pub fn path(&self) -> String { + self.path.clone() + } + + #[wasm_bindgen(getter)] + pub fn resolved(&self) -> bool { + self.resolved + } +} + +impl NodeData { + pub fn from_construction_data(data: &GraphConstructionNodeData) -> NodeData { + NodeData { + path: data.path.clone(), + aliases: data.aliases.clone(), + resolved: data.resolved, + ignore_in_edges: data.ignore_in_edges, + ignore_out_edges: data.ignore_out_edges, + } + } + + pub fn new_unresolved(path: String) -> NodeData { + NodeData { + path, + aliases: Vec::new(), + resolved: false, + ignore_in_edges: false, + ignore_out_edges: false, + } + } +} + +// impl PartialEq for NodeData { +// fn eq(&self, other: &Self) -> bool { +// self.path == other.path +// } +// } + +#[wasm_bindgen] +pub struct MermaidGraphData { + mermaid: String, + traversal_time: u64, + total_time: u64, +} + +#[wasm_bindgen] +impl MermaidGraphData { + #[wasm_bindgen(getter)] + pub fn mermaid(&self) -> String { + self.mermaid.clone() + } + + #[wasm_bindgen(getter)] + pub fn traversal_time(&self) -> u64 { + self.traversal_time + } + + #[wasm_bindgen(getter)] + pub fn total_time(&self) -> u64 { + self.total_time + } +} + +impl MermaidGraphData { + pub fn new(mermaid: String, traversal_time: u64, total_time: u64) -> MermaidGraphData { + MermaidGraphData { + mermaid, + traversal_time, + total_time, + } + } +} + +#[wasm_bindgen] +#[derive(Clone, Debug)] +pub struct RecTraversalData { + node: NodeData, + edge_type: String, + depth: u32, + children: Vec, +} + +#[wasm_bindgen] +impl RecTraversalData { + #[wasm_bindgen(getter)] + pub fn node(&self) -> NodeData { + self.node.clone() + } + + #[wasm_bindgen(getter)] + pub fn edge_type(&self) -> String { + self.edge_type.clone() + } + + #[wasm_bindgen(getter)] + pub fn depth(&self) -> u32 { + self.depth + } + + #[wasm_bindgen(getter)] + pub fn children(&self) -> Vec { + self.children.clone() + } + + #[wasm_bindgen(js_name = toString)] + pub fn to_string(&self) -> String { + format!("{:?}", self) + } +} + +#[wasm_bindgen] +#[derive(Clone)] +pub struct NoteGraph { + #[wasm_bindgen(skip)] + pub graph: StableGraph, + #[wasm_bindgen(skip)] + pub transitive_rules: Vec, + #[wasm_bindgen(skip)] + pub edge_types: HashSet, + #[wasm_bindgen(skip)] + pub node_hash: HashMap>, + update_callback: Option, +} + +#[wasm_bindgen] +impl NoteGraph { + pub fn new() -> NoteGraph { + NoteGraph { + graph: StableGraph::::default(), + transitive_rules: Vec::new(), + edge_types: HashSet::new(), + node_hash: HashMap::new(), + update_callback: None, + } + } + + pub fn set_transitive_rules(&mut self, rules: Vec) { + self.transitive_rules = rules; + } + + pub fn set_update_callback(&mut self, callback: js_sys::Function) { + self.update_callback = Some(callback); + } + + pub fn notify_update(&self) { + match &self.update_callback { + Some(callback) => match callback.call0(&JsValue::NULL) { + Ok(_) => {} + Err(e) => utils::log(format!("Error calling function: {:?}", e)), + }, + None => {} + } + } + + pub fn build_graph( + &mut self, + nodes: Vec, + edges: Vec, + ) { + let now = Instant::now(); + + self.graph = StableGraph::::default(); + self.edge_types = HashSet::new(); + + // self.graph.reserve_exact_nodes(nodes.len()); + + self.node_hash = HashMap::new(); + + for info_node in nodes.as_slice() { + if self.node_hash.contains_key(&info_node.path) { + utils::log(format!("Node already exists: {}", info_node.path)); + continue; + } + + self.node_hash.insert( + info_node.path.clone(), + self.graph + .add_node(NodeData::from_construction_data(info_node)), + ); + } + + for edge in edges { + self.int_safe_add_edge(&edge.from, &edge.to, &edge.edge_type); + } + + let elapsed = now.elapsed(); + utils::log(format!("Building initial graph took {:.2?}", elapsed)); + + self.int_build_implied_edges(); + + self.notify_update(); + } + + pub fn apply_update(&mut self, update: BatchGraphUpdate) -> Result<()> { + let now = Instant::now(); + self.int_remove_implied_edges(); + + // self.log(); + update.apply(self)?; + // self.log(); + + self.int_rebuild_edge_type_tracker(); + self.int_build_implied_edges(); + utils::log(format!("Applying update took {:.2?}", now.elapsed())); + + self.notify_update(); + + Ok(()) + } + + pub fn iterate_nodes(&self, f: &js_sys::Function) { + let this = JsValue::NULL; + + self.graph.node_references().into_iter().for_each(|node| { + match f.call1(&this, &node.weight().clone().into()) { + Ok(_) => {} + Err(e) => utils::log(format!("Error calling function: {:?}", e)), + } + }); + } + + pub fn iterate_edges(&self, f: &js_sys::Function) { + let this = JsValue::NULL; + + self.graph.edge_references().into_iter().for_each(|edge| { + match f.call1(&this, &edge.weight().clone().into()) { + Ok(_) => {} + Err(e) => utils::log(format!("Error calling function: {:?}", e)), + } + }); + } + + pub fn edge_types(&self) -> Vec { + self.edge_types.iter().cloned().collect() + } + + // pub fn subgraph(&self, edge_types: Vec) -> Result { + // let mut subgraph = self.clone(); + + // for edge in subgraph.graph.edge_indices() { + // let edge_data = subgraph.int_get_edge_weight(edge)?; + + // if !edge_types.contains(&edge_data.edge_type) { + // subgraph.graph.remove_edge(edge); + // } + // } + + // Ok(subgraph) + // } + + pub fn generate_mermaid_graph( + &self, + entry_nodes: Vec, + edge_types: Vec, + max_depth: u32, + ) -> Result { + let now = Instant::now(); + + let mut entry_node_indices: Vec> = Vec::new(); + for node in entry_nodes { + let node_index = self + .int_get_node_index(&node) + .ok_or(NoteGraphError::new("Node not found"))?; + entry_node_indices.push(node_index); + } + + let (node_map, edge_map) = self.int_traverse_breadth_first( + entry_node_indices, + Some(&edge_types), + max_depth, + |_, depth| depth, + |edge| edge, + ); + + let traversal_elapsed = now.elapsed(); + + let mut result = String::new(); + result.push_str("flowchart LR\n"); + + // accumulate edges by direction, so that we can collapse them in the next step + let mut accumulated_edges: HashMap< + (NodeIndex, NodeIndex), + (NodeIndex, NodeIndex, Vec, Vec), + > = HashMap::new(); + + // TODO: this sucks, maybe use a tuple of node indices instead of strings + for (_, edge_ref) in edge_map { + let forward_dir = (edge_ref.source(), edge_ref.target()); + + let entry1 = accumulated_edges.get_mut(&forward_dir); + match entry1 { + Some((_, _, forward, _)) => { + forward.push(edge_ref.weight().clone()); + } + None => { + let backward_dir = (edge_ref.target(), edge_ref.source()); + + let entry2 = accumulated_edges.get_mut(&backward_dir); + match entry2 { + Some((_, _, _, backward)) => { + backward.push(edge_ref.weight().clone()); + } + None => { + accumulated_edges.insert( + forward_dir, + ( + edge_ref.source(), + edge_ref.target(), + vec![edge_ref.weight().clone()], + Vec::new(), + ), + ); + } + } + } + } + } + + let mut unresolved_nodes = HashSet::new(); + + // add nodes to the graph + for element in node_map.iter() { + let weight = self.int_get_node_weight(element.0.clone())?; + result.push_str(&format!( + " {}[\"{} ({})\"]\n", + element.0.index(), + weight.path, + element.1 + )); + if !weight.resolved { + unresolved_nodes.insert(element.0.index()); + } + } + + // collapse edge data and add them to the graph + for (from, to, forward, backward) in accumulated_edges.values() { + let mut label = String::new(); + + let same_elements = forward + .iter() + .zip(backward.iter()) + .all(|(a, b)| a.edge_type == b.edge_type); + let all_implied = forward + .iter() + .zip_longest(backward.iter()) + .all(|pair| match pair { + EitherOrBoth::Both(a, b) => a.implied && b.implied, + EitherOrBoth::Left(a) => a.implied, + EitherOrBoth::Right(b) => b.implied, + }); + + let arrow_type: String; + if backward.is_empty() { + if all_implied { + arrow_type = String::from("-.->"); + } else { + arrow_type = String::from("-->"); + } + } else { + if all_implied { + arrow_type = String::from("<-.->"); + } else { + arrow_type = String::from("<-->"); + } + } + + if same_elements { + label.push_str( + forward + .iter() + .map(|edge| edge.edge_type.clone()) + .collect::>() + .join(", ") + .as_str(), + ); + } else { + label.push_str( + forward + .iter() + .map(|edge| edge.edge_type.clone()) + .collect::>() + .join(", ") + .as_str(), + ); + // forward can never be empty + if !backward.is_empty() { + label.push_str(" / "); + } + label.push_str( + backward + .iter() + .map(|edge| edge.edge_type.clone()) + .collect::>() + .join(", ") + .as_str(), + ); + } + + result.push_str(&format!( + " {} {}|{}| {}\n", + from.index(), + arrow_type, + label, + to.index() + )); + } + + if !unresolved_nodes.is_empty() { + result.push_str(&format!( + "class {} ng-node-unresolved", + unresolved_nodes + .iter() + .map(|index| format!("{}", index)) + .join(",") + )); + } + + let total_elapsed = now.elapsed(); + + Ok(MermaidGraphData::new( + result, + traversal_elapsed.as_micros() as u64, + total_elapsed.as_micros() as u64, + )) + } + + pub fn rec_traverse( + &self, + entry_node: String, + edge_types: Vec, + max_depth: u32, + ) -> Result { + let now = Instant::now(); + let mut node_count = 1; + + // let node_index = self.get_node_index(node.clone()).unwrap(); + + let start_node = self + .int_get_node_index(&entry_node) + .ok_or(NoteGraphError::new("Node not found"))?; + + let result = self.int_rec_traverse( + start_node, + &edge_types, + String::new(), + 0, + max_depth, + &mut node_count, + ); + + let total_elapsed = now.elapsed(); + utils::log(format!( + "Total tree took {:.2?} ({} nodes)", + total_elapsed, node_count + )); + + result + } + + pub fn log(&self) { + utils::log(format!("{:#?}", self.graph)); + } +} + +/// Internal methods, not exposed to the wasm interface. +/// All of these methods are prefixed with `int_`. +impl NoteGraph { + /// Builds the implied edges based on the transitive rules. + pub fn int_build_implied_edges(&mut self) { + let now = Instant::now(); + + let max_rounds = self + .transitive_rules + .iter() + .map(|rule| rule.rounds) + .max() + .unwrap_or(0); + // utils::log(format!("Max rounds: {}", max_rounds)); + // utils::log(format!("Rules count: {}", self.transitive_rules.len())); + + // rules look like + // [A, B, C] -> D + + // TODO: maybe we can keep track of edge types that were added in the last round and only check a rule that has any of those edge types on the left side + // we would need to also check back edges though + // a rule like [A, B] -> (C with back edge D) would do nothing if applied multiple times, since the edges on the left side were not modified + + let mut edge_type_tracker: HashSet = self.edge_types.clone(); + + for i in 1..(max_rounds + 1) { + if edge_type_tracker.is_empty() { + break; + } + + let mut edges_to_add: Vec<(NodeIndex, NodeIndex, String)> = Vec::new(); + + for rule in self.transitive_rules.iter() { + // if there is any edge type that the graph doesn't have, we can skip the rule + if rule + .path + .iter() + .any(|edge_type| !self.edge_types.contains(edge_type)) + { + // utils::log(format!("Skipping rule: {}", rule.edge_type)); + continue; + } + + // if all edge types of a rule didn't see any changes in the last round, we can skip the rule + if rule + .path + .iter() + .all(|edge_type| !edge_type_tracker.contains(edge_type)) + { + // utils::log(format!("Skipping rule: {}", rule.edge_type)); + continue; + } + + for start_node in self.graph.node_indices() { + // let path = rule.path.clone(); + let mut current_nodes = vec![start_node]; + + for edge_type in rule.path.iter() { + let mut next_nodes = Vec::new(); + + for current_node in current_nodes { + for edge in self.graph.edges(current_node) { + if *edge.weight().edge_type == *edge_type { + next_nodes.push(edge.target()); + } + } + } + + current_nodes = next_nodes; + } + + for end_node in current_nodes { + // if the rule can't loop and the start and end node are the same, we skip the edge + if !rule.can_loop && start_node == end_node { + continue; + } + + if rule.close_reversed { + edges_to_add.push((end_node, start_node, rule.edge_type.clone())); + } else { + edges_to_add.push((start_node, end_node, rule.edge_type.clone())); + } + } + } + } + + // if edges_to_add.is_empty() { + // break; + // } + + // utils::log(format!("New edge count: {}", edges_to_add.len())); + + let mut current_edge_type_tracker: HashSet = HashSet::new(); + + let now2 = Instant::now(); + utils::log(format!("Adding {} Edges ", edges_to_add.len())); + + for (from, to, edge_type) in edges_to_add { + self.int_add_edge( + from, + to, + EdgeData::new(edge_type, true, i), + &mut Some(&mut current_edge_type_tracker), + ); + } + + let elapsed2 = now2.elapsed(); + utils::log(format!("Adding Implied Edges took {:.2?}", elapsed2)); + + edge_type_tracker = current_edge_type_tracker; + } + + let elapsed = now.elapsed(); + utils::log(format!("Building Implied Edges took {:.2?}", elapsed)); + } + + pub fn int_rebuild_edge_type_tracker(&mut self) { + self.edge_types = HashSet::new(); + + for edge in self.graph.edge_references() { + self.edge_types.insert(edge.weight().edge_type.clone()); + } + } + + /// Returns the node index for a specific node weight. + pub fn int_get_node_index(&self, node: &String) -> Option> { + self.node_hash.get(node).map(|index| index.clone()) + } + + /// Adds an edge type to the global edge type tracker and an optional local edge type tracker. + pub fn int_add_to_edge_type_tracker( + &mut self, + edge_type: &String, + edge_type_tracker: &mut Option<&mut HashSet>, + add_to_global: bool, + ) { + if add_to_global { + self.edge_types.insert(edge_type.clone()); + } + + match edge_type_tracker { + Some(inner) => { + inner.insert(edge_type.clone()); + } + None => {} + } + } + + /// Recursively traverses the graph using DFS and builds a tree structure. + /// + /// Will return an error if the node weight for any node along the traversal is not found. + pub fn int_rec_traverse( + &self, + node: NodeIndex, + edge_types: &Vec, + edge_type: String, + depth: u32, + max_depth: u32, + node_count: &mut usize, + ) -> Result { + let mut new_children = Vec::new(); + + if depth < max_depth { + for edge in self.graph.edges(node) { + let target = edge.target(); + let edge_type = &edge.weight().edge_type; + + if edge_types.contains(edge_type) { + new_children.push(self.int_rec_traverse( + target, + edge_types, + edge_type.clone(), + depth + 1, + max_depth, + node_count, + )?) + } + } + } + + *node_count += new_children.len(); + + Ok(RecTraversalData { + node: self.int_get_node_weight(node)?.clone(), + edge_type, + depth, + children: new_children, + }) + } + + /// Returns the node weight for a specific node index. + /// + /// Will return an error if the node is not found. + pub fn int_get_node_weight(&self, node: NodeIndex) -> Result<&NodeData> { + self.graph + .node_weight(node) + .ok_or(NoteGraphError::new("Node not found")) + } + + pub fn int_has_incoming_edges(&self, node: NodeIndex) -> bool { + self.graph + .edges_directed(node, petgraph::Direction::Incoming) + .count() + > 0 + } + + pub fn int_has_outgoing_edges(&self, node: NodeIndex) -> bool { + self.graph + .edges_directed(node, petgraph::Direction::Outgoing) + .count() + > 0 + } + + pub fn int_iter_incoming_edges( + &self, + node: NodeIndex, + ) -> Edges<'_, EdgeData, Directed, u32> { + self.graph + .edges_directed(node, petgraph::Direction::Incoming) + } + + pub fn int_iter_outgoing_edges( + &self, + node: NodeIndex, + ) -> Edges<'_, EdgeData, Directed, u32> { + self.graph + .edges_directed(node, petgraph::Direction::Outgoing) + } + + pub fn int_remove_outgoing_edges(&mut self, node: NodeIndex) { + let mut edges_to_remove: Vec> = Vec::new(); + + for edge in self.graph.edges(node) { + edges_to_remove.push(edge.id()); + } + + for edge in edges_to_remove { + self.graph.remove_edge(edge); + } + } + + pub fn int_set_node_resolved(&mut self, node: NodeIndex, resolved: bool) -> Result<()> { + let node = self.graph.node_weight_mut(node); + match node { + Some(node) => { + node.resolved = resolved; + + Ok(()) + } + None => Err(NoteGraphError::new("Node not found")), + } + } + + pub fn int_node_count(&self) -> usize { + self.graph.node_count() + } + + pub fn int_edge_count(&self) -> usize { + self.graph.edge_count() + } + + /// Returns the edge weight for a specific edge index. + /// + /// Will return an error if the edge is not found. + pub fn int_get_edge_weight(&self, edge: EdgeIndex) -> Result<&EdgeData> { + self.graph + .edge_weight(edge) + .ok_or(NoteGraphError::new("Edge not found")) + } + + /// Traverses the tree in a depth first manner and calls the provided callbacks for each node and edge. + /// The depth metric **might not** accurately represent the intuitive understanding of depth on a graph. + /// A list of tuples of node indices and the result of the node callback and a list of tuples of edge indices and the result of the edge callback are returned. + /// These lists are ordered by the order in which the nodes and edges were visited. + /// Each node and edge is only visited once. + /// At the depth limit, edges are only added if the target node is already in the depth map. + pub fn int_traverse_depth_first<'a, T, S>( + &'a self, + entry_nodes: Vec>, + edge_types: Option<&Vec>, + max_depth: u32, + node_callback: fn(NodeIndex, u32) -> T, + edge_callback: fn(EdgeReference<'a, EdgeData, u32>) -> S, + ) -> (Vec<(NodeIndex, T)>, Vec<(EdgeIndex, S)>) { + let mut data_structure = DepthFirstTraversalDataStructure::new(); + + self.int_traverse_generic( + &mut data_structure, + entry_nodes, + edge_types, + max_depth, + node_callback, + edge_callback, + ) + } + + /// Traverses the tree in a breadth first manner and calls the provided callbacks for each node and edge. + /// The depth metric accurately represent the intuitive understanding of depth on a graph. + /// A list of tuples of node indices and the result of the node callback and a list of tuples of edge indices and the result of the edge callback are returned. + /// These lists are ordered by the order in which the nodes and edges were visited. + /// Each node and edge is only visited once. + /// At the depth limit, edges are only added if the target node is already in the depth map. + pub fn int_traverse_breadth_first<'a, T, S>( + &'a self, + entry_nodes: Vec>, + edge_types: Option<&Vec>, + max_depth: u32, + node_callback: fn(NodeIndex, u32) -> T, + edge_callback: fn(EdgeReference<'a, EdgeData, u32>) -> S, + ) -> (Vec<(NodeIndex, T)>, Vec<(EdgeIndex, S)>) { + let mut data_structure = BreadthFirstTraversalDataStructure::new(); + + self.int_traverse_generic( + &mut data_structure, + entry_nodes, + edge_types, + max_depth, + node_callback, + edge_callback, + ) + } + + pub fn int_traverse_generic<'a, T, S>( + &'a self, + traversal_data_structure: &mut impl GraphTraversalDataStructure<(NodeIndex, u32)>, + entry_nodes: Vec>, + edge_types: Option<&Vec>, + max_depth: u32, + node_callback: fn(NodeIndex, u32) -> T, + edge_callback: fn(EdgeReference<'a, EdgeData, u32>) -> S, + ) -> (Vec<(NodeIndex, T)>, Vec<(EdgeIndex, S)>) { + let mut node_list: Vec<(NodeIndex, T)> = Vec::new(); + let mut edge_list: Vec<(EdgeIndex, S)> = Vec::new(); + let mut visited_nodes: HashSet> = HashSet::new(); + + for node in entry_nodes { + node_list.push((node, node_callback(node, 0))); + visited_nodes.insert(node); + traversal_data_structure.push((node, 0)); + } + + while !traversal_data_structure.is_empty() { + let (current_node, current_depth) = traversal_data_structure.pop().unwrap(); + let at_depth_limit = current_depth >= max_depth; + + for edge in self.graph.edges(current_node) { + let target = edge.target(); + let edge_data = edge.weight(); + + if self.int_edge_matches_edge_filter(edge_data, edge_types) { + let already_visited = visited_nodes.contains(&target); + + // we only add the edge if we are not at the depth limit or if we are at the depth limit and the target node is already in the depth map + // this captures all the outgoing edges from the nodes at the depth limit to nodes already present in the depth map + if !at_depth_limit || (at_depth_limit && already_visited) { + edge_list.push((edge.id(), edge_callback(edge))); + } + + // we only insert the new node when we are not at the depth limit and the node is not already in the depth map + if !at_depth_limit && !already_visited { + node_list.push((target, node_callback(target, current_depth + 1))); + visited_nodes.insert(target); + traversal_data_structure.push((target, current_depth + 1)); + } + } + } + } + + (node_list, edge_list) + } + + pub fn int_edge_matches_edge_filter( + &self, + edge: &EdgeData, + edge_types: Option<&Vec>, + ) -> bool { + match edge_types { + Some(types) => types.contains(&edge.edge_type), + None => true, + } + } + + pub fn int_remove_implied_edges(&mut self) { + let now = Instant::now(); + + // let mut edges_to_remove: Vec> = Vec::new(); + + self.graph.retain_edges(|frozen_graph, edge| { + let weight = frozen_graph.edge_weight(edge).unwrap(); + + !weight.implied + }); + + // for edge in self.graph.edge_references() { + // let edge_data = edge.weight(); + + // if edge_data.implied { + // edges_to_remove.push(edge.id()); + // } + // } + + // utils::log(format!("Removing {} of {} Implied Edges", edges_to_remove.len(), self.graph.edge_count())); + + // for edge in edges_to_remove { + // self.graph.remove_edge(edge); + // } + + utils::log(format!("{} edges remain", self.graph.edge_count())); + + let elapsed = now.elapsed(); + utils::log(format!("Removing Implied Edges took {:.2?}", elapsed)); + } + + fn int_add_node(&mut self, node: &GraphConstructionNodeData) -> Result<()> { + if self.node_hash.contains_key(&node.path) { + return Err(NoteGraphError::new("Node already exists")); + } + + let node_index = self.graph.add_node(NodeData::from_construction_data(node)); + + self.node_hash.insert(node.path.clone(), node_index); + + Ok(()) + } + + fn int_remove_node(&mut self, node: &String) -> Result<()> { + let node_index = self + .int_get_node_index(node) + .ok_or(NoteGraphError::new("Node not found"))?; + + self.graph.remove_node(node_index); + self.node_hash.remove(node); + + Ok(()) + } + /// Adds an edge to the graph. will also add the back edge if necessary. + /// This will not add already existing edges. + /// + /// Will add the added edge types to the global edge type tracker and an optional local edge type tracker. + fn int_add_edge( + &mut self, + from: NodeIndex, + to: NodeIndex, + edge_data: EdgeData, + edge_type_tracker: &mut Option<&mut HashSet>, + ) { + if self.int_has_edge(from, to, &edge_data.edge_type) { + return; + } + + self.int_add_to_edge_type_tracker(&edge_data.edge_type, edge_type_tracker, true); + + self.graph.add_edge(from, to, edge_data); + } + + fn int_remove_edge( + &mut self, + from: NodeIndex, + to: NodeIndex, + edge_type: &String, + ) -> Result<()> { + let edge = self + .graph + .edges(from) + .into_iter() + .find(|e| e.target() == to && *e.weight().edge_type == *edge_type); + + match edge { + Some(edge) => { + self.graph.remove_edge(edge.id()); + Ok(()) + } + None => Err(NoteGraphError::new("Edge not found")), + } + } + + pub fn int_get_edge( + &self, + from: NodeIndex, + to: NodeIndex, + edge_type: &String, + ) -> Option> { + self.graph + .edges(from) + .into_iter() + .find(|e| e.target() == to && *e.weight().edge_type == *edge_type) + } + + pub fn int_get_edge_by_name( + &self, + from: &String, + to: &String, + edge_type: &String, + ) -> Option> { + let from_index = self.int_get_node_index(from)?; + let to_index = self.int_get_node_index(to)?; + + self.int_get_edge(from_index, to_index, edge_type) + } + + /// Checks if an edge exists between two nodes with a specific edge type. + pub fn int_has_edge( + &self, + from: NodeIndex, + to: NodeIndex, + edge_type: &String, + ) -> bool { + self.graph + .edges(from) + .into_iter() + .any(|e| e.target() == to && *e.weight().edge_type == *edge_type) + } + + pub fn int_has_edge_by_name(&self, from: &String, to: &String, edge_type: &String) -> bool { + let from_index = self.int_get_node_index(from); + let to_index = self.int_get_node_index(to); + + match (from_index, to_index) { + (Some(from_index), Some(to_index)) => { + self.int_has_edge(from_index, to_index, edge_type) + } + _ => false, + } + } + + pub fn int_safe_add_node( + &mut self, + construction_data: &GraphConstructionNodeData, + ) -> Result<()> { + // we check if the node already exists in the graph + // if it does, we assert that it is not resolved + match self.int_get_node_index(&construction_data.path) { + Some(node_index) => { + if self.int_get_node_weight(node_index)?.resolved() { + return Err(NoteGraphError::new( + "There already exists a resolved node with the same name.", + )); + } + + // TODO: also update the other things like aliases + self.int_set_node_resolved(node_index, true)?; + } + None => { + self.int_add_node(construction_data)?; + } + } + + Ok(()) + } + + pub fn int_safe_remove_node(&mut self, node: &String) -> Result<()> { + match self.int_get_node_index(node) { + Some(index) => { + if !self.int_get_node_weight(index)?.resolved() { + return Err(NoteGraphError::new("Cannot remove an unresolved node")); + } + + self.int_set_node_resolved(index, false)?; + + let edges_to_remove: Vec> = self + .int_iter_outgoing_edges(index) + .map(|edge| edge.id()) + .collect(); + + for edge in edges_to_remove { + self.int_safe_delete_edge_ref(edge)?; + } + } + None => { + return Err(NoteGraphError::new("Node not found")); + } + } + + Ok(()) + } + + pub fn int_safe_rename_node(&mut self, old_name: &String, new_name: &String) -> Result<()> { + let node_index = self + .int_get_node_index(old_name) + .ok_or(NoteGraphError::new("Old node not found"))?; + + self.graph.node_weight_mut(node_index).unwrap().path = new_name.clone(); + self.node_hash.remove(old_name); + self.node_hash.insert(new_name.clone(), node_index); + + Ok(()) + } + + fn int_safe_delete_edge_ref(&mut self, edge: EdgeIndex) -> Result<()> { + let (_, target) = self + .graph + .edge_endpoints(edge) + .ok_or(NoteGraphError::new("Edge not found"))?; + let target_data = self.int_get_node_weight(target)?.clone(); + let target_resolved = target_data.resolved(); + + self.graph.remove_edge(edge); + + if !target_resolved && self.int_iter_incoming_edges(target).count() == 0 { + self.int_remove_node(&target_data.path)?; + } + + Ok(()) + } + + pub fn int_safe_delete_edge( + &mut self, + from: &String, + to: &String, + edge_type: &String, + ) -> Result<()> { + match (self.int_get_node_index(from), self.int_get_node_index(to)) { + (Some(from_index), Some(to_index)) => { + match self.int_get_edge(from_index, to_index, edge_type) { + Some(edge) => self.int_safe_delete_edge_ref(edge.id()), + None => Err(NoteGraphError::new("Edge not found")), + } + } + _ => Err(NoteGraphError::new("Node not found")), + } + } + + pub fn int_safe_add_edge(&mut self, from_path: &String, to_path: &String, edge_type: &String) { + let from = self.node_hash.get(from_path); + let to = self.node_hash.get(to_path); + + let from_index: NodeIndex; + let to_index: NodeIndex; + let mut add_from_to_hash = false; + let mut add_to_to_hash = false; + + if from.is_none() { + from_index = self + .graph + .add_node(NodeData::new_unresolved(from_path.clone())); + add_from_to_hash = true; + } else { + from_index = from.unwrap().clone(); + } + + if to.is_none() { + if from_path == to_path { + to_index = from_index; + } else { + to_index = self + .graph + .add_node(NodeData::new_unresolved(to_path.clone())); + add_to_to_hash = true; + } + } else { + to_index = to.unwrap().clone(); + } + + if add_from_to_hash { + self.node_hash.insert(from_path.clone(), from_index); + } + + if add_to_to_hash { + self.node_hash.insert(from_path.clone(), to_index); + } + + self.int_add_edge( + from_index, + to_index, + EdgeData::new(edge_type.clone(), false, 0), + &mut None, + ); + } + + pub fn assert_correct_trackers(&self) { + let mut edge_types: HashSet = HashSet::new(); + + for edge in self.graph.edge_references() { + edge_types.insert(edge.weight().edge_type.clone()); + } + + assert_eq!(edge_types, self.edge_types); + + let mut node_hash: HashMap> = HashMap::new(); + + for node_ref in self.graph.node_references() { + node_hash.insert(node_ref.weight().path.clone(), node_ref.id()); + } + + assert_eq!(node_hash, self.node_hash); + } +} diff --git a/wasm/src/graph_construction.rs b/wasm/src/graph_construction.rs new file mode 100644 index 00000000..2d00af83 --- /dev/null +++ b/wasm/src/graph_construction.rs @@ -0,0 +1,81 @@ +use wasm_bindgen::prelude::*; + +#[wasm_bindgen] +#[derive(Clone, Debug)] +pub struct GraphConstructionNodeData { + #[wasm_bindgen(skip)] + pub path: String, + #[wasm_bindgen(skip)] + pub aliases: Vec, + #[wasm_bindgen(skip)] + pub resolved: bool, + #[wasm_bindgen(skip)] + pub ignore_in_edges: bool, + #[wasm_bindgen(skip)] + pub ignore_out_edges: bool, +} + +#[wasm_bindgen] +impl GraphConstructionNodeData { + #[wasm_bindgen(constructor)] + pub fn new( + path: String, + aliases: Vec, + resolved: bool, + ignore_in_edges: bool, + ignore_out_edges: bool, + ) -> GraphConstructionNodeData { + GraphConstructionNodeData { + path, + aliases, + resolved, + ignore_in_edges, + ignore_out_edges, + } + } + + #[wasm_bindgen(js_name = toString)] + pub fn to_string(&self) -> String { + format!("{:?}", self) + } +} + +impl GraphConstructionNodeData { + pub fn new_unresolved(path: String) -> GraphConstructionNodeData { + GraphConstructionNodeData { + path, + aliases: Vec::new(), + resolved: false, + ignore_in_edges: false, + ignore_out_edges: false, + } + } +} + +#[wasm_bindgen] +#[derive(Clone, Debug)] +pub struct GraphConstructionEdgeData { + #[wasm_bindgen(skip)] + pub from: String, + #[wasm_bindgen(skip)] + pub to: String, + #[wasm_bindgen(skip)] + pub edge_type: String, +} + +#[wasm_bindgen] +impl GraphConstructionEdgeData { + #[wasm_bindgen(constructor)] + pub fn new(from: String, to: String, edge_type: String) -> GraphConstructionEdgeData { + GraphConstructionEdgeData { + from, + to, + edge_type, + } + } + + #[wasm_bindgen(js_name = toString)] + pub fn to_string(&self) -> String { + format!("{:?}", self) + } +} diff --git a/wasm/src/graph_rules.rs b/wasm/src/graph_rules.rs new file mode 100644 index 00000000..727ec5aa --- /dev/null +++ b/wasm/src/graph_rules.rs @@ -0,0 +1,39 @@ +use wasm_bindgen::prelude::*; + +#[wasm_bindgen] +#[derive(Clone)] +pub struct TransitiveGraphRule { + // the path by edge type + #[wasm_bindgen(skip)] + pub path: Vec, + // the edge type to add + #[wasm_bindgen(skip)] + pub edge_type: String, + // the edge type to add + #[wasm_bindgen(skip)] + pub rounds: u8, + #[wasm_bindgen(skip)] + pub can_loop: bool, + #[wasm_bindgen(skip)] + pub close_reversed: bool, +} + +#[wasm_bindgen] +impl TransitiveGraphRule { + #[wasm_bindgen(constructor)] + pub fn new( + path: Vec, + edge_type: String, + rounds: u8, + can_loop: bool, + close_reversed: bool, + ) -> TransitiveGraphRule { + TransitiveGraphRule { + path, + edge_type, + rounds, + can_loop, + close_reversed, + } + } +} diff --git a/wasm/src/graph_update.rs b/wasm/src/graph_update.rs new file mode 100644 index 00000000..f3b9dac6 --- /dev/null +++ b/wasm/src/graph_update.rs @@ -0,0 +1,168 @@ +use crate::{graph::NoteGraph, graph_construction::GraphConstructionNodeData, utils::Result}; +use wasm_bindgen::prelude::*; + +pub trait GraphUpdate { + fn apply(&self, graph: &mut NoteGraph) -> Result<()>; +} + +#[wasm_bindgen] +pub struct BatchGraphUpdate { + updates: Vec>, +} + +#[wasm_bindgen] +impl BatchGraphUpdate { + #[wasm_bindgen(constructor)] + pub fn new() -> Self { + Self { + updates: Vec::new(), + } + } +} + +impl BatchGraphUpdate { + pub fn add_update(&mut self, update: Box) { + self.updates.push(update); + } + + pub fn apply(&self, graph: &mut NoteGraph) -> Result<()> { + for update in &self.updates { + update.apply(graph)?; + } + + Ok(()) + } +} + +#[wasm_bindgen] +#[derive(Clone)] +pub struct AddNoteGraphUpdate { + data: GraphConstructionNodeData, +} + +#[wasm_bindgen] +impl AddNoteGraphUpdate { + #[wasm_bindgen(constructor)] + pub fn new(data: GraphConstructionNodeData) -> Self { + Self { data } + } + + pub fn add_to_batch(&self, batch: &mut BatchGraphUpdate) { + batch.add_update(Box::new(self.clone())); + } +} + +impl GraphUpdate for AddNoteGraphUpdate { + fn apply(&self, graph: &mut NoteGraph) -> Result<()> { + graph.int_safe_add_node(&self.data) + } +} + +#[wasm_bindgen] +#[derive(Clone)] +pub struct RemoveNoteGraphUpdate { + data: String, +} + +#[wasm_bindgen] +impl RemoveNoteGraphUpdate { + #[wasm_bindgen(constructor)] + pub fn new(data: String) -> Self { + Self { data } + } + + pub fn add_to_batch(&self, batch: &mut BatchGraphUpdate) { + batch.add_update(Box::new(self.clone())); + } +} + +impl GraphUpdate for RemoveNoteGraphUpdate { + fn apply(&self, graph: &mut NoteGraph) -> Result<()> { + graph.int_safe_remove_node(&self.data) + } +} + +#[wasm_bindgen] +#[derive(Clone)] +pub struct RenameNoteGraphUpdate { + old_name: String, + new_name: String, +} + +#[wasm_bindgen] +impl RenameNoteGraphUpdate { + #[wasm_bindgen(constructor)] + pub fn new(old_name: String, new_name: String) -> Self { + Self { old_name, new_name } + } + + pub fn add_to_batch(&self, batch: &mut BatchGraphUpdate) { + batch.add_update(Box::new(self.clone())); + } +} + +impl GraphUpdate for RenameNoteGraphUpdate { + fn apply(&self, graph: &mut NoteGraph) -> Result<()> { + graph.int_safe_rename_node(&self.old_name, &self.new_name) + } +} + +#[wasm_bindgen] +#[derive(Clone)] +pub struct AddEdgeGraphUpdate { + from: String, + to: String, + edge_type: String, +} + +#[wasm_bindgen] +impl AddEdgeGraphUpdate { + #[wasm_bindgen(constructor)] + pub fn new(from: String, to: String, edge_type: String) -> Self { + Self { + from, + to, + edge_type, + } + } + + pub fn add_to_batch(&self, batch: &mut BatchGraphUpdate) { + batch.add_update(Box::new(self.clone())); + } +} + +impl GraphUpdate for AddEdgeGraphUpdate { + fn apply(&self, graph: &mut NoteGraph) -> Result<()> { + Ok(graph.int_safe_add_edge(&self.from, &self.to, &self.edge_type)) + } +} + +#[wasm_bindgen] +#[derive(Clone)] +pub struct RemoveEdgeGraphUpdate { + from: String, + to: String, + edge_type: String, +} + +#[wasm_bindgen] +impl RemoveEdgeGraphUpdate { + #[wasm_bindgen(constructor)] + pub fn new(from: String, to: String, edge_type: String) -> Self { + Self { + from, + to, + edge_type, + } + } + + pub fn add_to_batch(&self, batch: &mut BatchGraphUpdate) { + batch.add_update(Box::new(self.clone())); + } +} + +impl GraphUpdate for RemoveEdgeGraphUpdate { + fn apply(&self, graph: &mut NoteGraph) -> Result<()> { + graph.int_safe_delete_edge(&self.from, &self.to, &self.edge_type) + } +} diff --git a/wasm/src/lib.rs b/wasm/src/lib.rs new file mode 100644 index 00000000..768dcf07 --- /dev/null +++ b/wasm/src/lib.rs @@ -0,0 +1,20 @@ +pub mod graph; +pub mod graph_construction; +pub mod graph_rules; +pub mod graph_update; +pub mod utils; + +use wasm_bindgen::prelude::*; + +const DEBUG: bool = false; + +#[wasm_bindgen] +pub fn create_graph() -> graph::NoteGraph { + // utils::log_str("Hello, from WASM!"); + + console_error_panic_hook::set_once(); + + let graph = graph::NoteGraph::new(); + + return graph; +} diff --git a/wasm/src/utils.rs b/wasm/src/utils.rs new file mode 100644 index 00000000..5393b142 --- /dev/null +++ b/wasm/src/utils.rs @@ -0,0 +1,122 @@ +use petgraph::visit::{EdgeRef, IntoEdgeReferences, IntoNodeReferences}; +use std::{collections::VecDeque, fmt}; +use wasm_bindgen::prelude::*; + +#[wasm_bindgen] +extern "C" { + fn alert(s: &str); + // Use `js_namespace` here to bind `console.log(..)` instead of just + // `log(..)` + #[wasm_bindgen(js_namespace = console, js_name = log)] + pub fn log_str(s: &str); + #[wasm_bindgen(js_namespace = console)] + pub fn log(s: String); +} + +pub type Result = std::result::Result; + +#[wasm_bindgen] +#[derive(Clone, Debug)] +pub struct NoteGraphError { + message: String, +} + +#[wasm_bindgen] +impl NoteGraphError { + #[wasm_bindgen(constructor)] + pub fn new(message: &str) -> NoteGraphError { + NoteGraphError { + message: message.to_string(), + } + } + + #[wasm_bindgen(getter)] + pub fn message(&self) -> String { + self.message.clone() + } + + #[wasm_bindgen(js_name = toString)] + pub fn to_string(&self) -> String { + format!("{:?}", self) + } +} + +impl fmt::Display for NoteGraphError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self.message) + } +} + +pub struct DepthFirstTraversalDataStructure { + stack: Vec, +} + +pub struct BreadthFirstTraversalDataStructure { + queue: VecDeque, +} + +impl GraphTraversalDataStructure for DepthFirstTraversalDataStructure { + fn new() -> Self { + DepthFirstTraversalDataStructure { stack: Vec::new() } + } + + fn push(&mut self, value: T) { + self.stack.push(value); + } + + fn pop(&mut self) -> Option { + self.stack.pop() + } + + fn is_empty(&self) -> bool { + self.stack.is_empty() + } +} + +impl GraphTraversalDataStructure for BreadthFirstTraversalDataStructure { + fn new() -> Self { + BreadthFirstTraversalDataStructure { + queue: VecDeque::new(), + } + } + + fn push(&mut self, value: T) { + self.queue.push_back(value); + } + + fn pop(&mut self) -> Option { + self.queue.pop_front() + } + + fn is_empty(&self) -> bool { + self.queue.is_empty() + } +} + +pub trait GraphTraversalDataStructure { + fn new() -> Self; + fn push(&mut self, value: T); + fn pop(&mut self) -> Option; + fn is_empty(&self) -> bool; +} + +pub fn graph_eq( + a: &petgraph::stable_graph::StableGraph, + b: &petgraph::stable_graph::StableGraph, +) -> bool +where + N: PartialEq, + E: PartialEq, + Ty: petgraph::EdgeType, + Ix: petgraph::graph::IndexType + PartialEq, +{ + let a_ns = a.node_references().map(|n| n.1); + let b_ns = b.node_references().map(|n| n.1); + let a_es = a + .edge_references() + .map(|e| (e.source(), e.target(), e.weight())); + let b_es = b + .edge_references() + .map(|e| (e.source(), e.target(), e.weight())); + a_ns.eq(b_ns) && a_es.eq(b_es) +} diff --git a/wasm/tests/common/mod.rs b/wasm/tests/common/mod.rs new file mode 100644 index 00000000..082653e4 --- /dev/null +++ b/wasm/tests/common/mod.rs @@ -0,0 +1,68 @@ +use breadcrumbs_graph_wasm::{ + graph::NoteGraph, + graph_construction::{GraphConstructionEdgeData, GraphConstructionNodeData}, +}; + +/// Generate a tree of nodes with explicit down edges for testing purposes. +pub fn tdata_generate_tree( + depth: u32, + branches: u32, +) -> ( + Vec, + Vec, +) { + let root = GraphConstructionNodeData::new("root".to_string(), vec![], true, false, false); + let mut edges = vec![]; + + let mut stack = vec![]; + + for i in 0..branches { + stack.push(i.to_string()); + edges.push(GraphConstructionEdgeData::new( + "root".to_string(), + i.to_string(), + "down".to_string(), + )); + } + + let mut nodes = vec![root]; + + while !stack.is_empty() { + let current = stack.pop().unwrap(); + + if current.len() < depth as usize { + for i in 0..branches { + let next = format!("{}{}", current, i); + edges.push(GraphConstructionEdgeData::new( + current.clone(), + next.clone(), + "down".to_string(), + )); + stack.push(next); + } + } + + nodes.push(GraphConstructionNodeData::new( + current, + vec![], + true, + false, + false, + )); + } + + (nodes, edges) +} + +pub fn tdata_to_graph( + data: ( + Vec, + Vec, + ), +) -> NoteGraph { + let mut graph = NoteGraph::new(); + + graph.build_graph(data.0, data.1); + + graph +} diff --git a/wasm/tests/graph.rs b/wasm/tests/graph.rs new file mode 100644 index 00000000..24be74b9 --- /dev/null +++ b/wasm/tests/graph.rs @@ -0,0 +1,221 @@ +// #![cfg(target_arch = "wasm32")] + +extern crate wasm_bindgen_test; +use breadcrumbs_graph_wasm::{ + graph::NoteGraph, + graph_construction::GraphConstructionNodeData, + graph_rules::TransitiveGraphRule, + graph_update::{ + AddEdgeGraphUpdate, AddNoteGraphUpdate, BatchGraphUpdate, RemoveNoteGraphUpdate, + }, + utils::graph_eq, +}; +use wasm_bindgen_test::*; +mod common; + +// wasm_bindgen_test_configure!(run_in_browser); + +#[wasm_bindgen_test] +fn test_graph_builds() { + let data = common::tdata_generate_tree(3, 2); + let graph = common::tdata_to_graph(data); + + assert_eq!(graph.int_node_count(), 15); + assert_eq!(graph.int_edge_count(), 14); + graph.assert_correct_trackers(); +} + +#[wasm_bindgen_test] +fn test_implied_edge_rules_reverse_direction() { + let data = common::tdata_generate_tree(3, 2); + let mut graph = NoteGraph::new(); + + graph.set_transitive_rules(vec![ + TransitiveGraphRule::new(vec!["down".to_string()], "up".to_string(), 5, false, true), + TransitiveGraphRule::new(vec!["up".to_string()], "down".to_string(), 5, false, true), + ]); + + graph.build_graph(data.0, data.1); + + assert_eq!(graph.int_edge_count(), 28); + + let up_edge_1 = graph + .int_get_edge_by_name(&"0".to_string(), &"root".to_string(), &"up".to_string()) + .unwrap(); + let up_edge_2 = graph + .int_get_edge_by_name(&"1".to_string(), &"root".to_string(), &"up".to_string()) + .unwrap(); + + assert_eq!(up_edge_1.weight().implied, true); + assert_eq!(up_edge_2.weight().implied, true); + graph.assert_correct_trackers(); +} + +#[wasm_bindgen_test] +fn test_implied_edge_rules_sibling() { + let data = common::tdata_generate_tree(3, 2); + let mut graph = NoteGraph::new(); + + graph.set_transitive_rules(vec![ + TransitiveGraphRule::new(vec!["down".to_string()], "up".to_string(), 5, false, true), + TransitiveGraphRule::new(vec!["up".to_string()], "down".to_string(), 5, false, true), + TransitiveGraphRule::new( + vec!["up".to_string(), "down".to_string()], + "same".to_string(), + 5, + false, + false, + ), + ]); + + graph.build_graph(data.0, data.1); + // same edges between siblings exist + let same_edge_1 = graph + .int_get_edge_by_name(&"0".to_string(), &"1".to_string(), &"same".to_string()) + .unwrap(); + let same_edge_2 = graph + .int_get_edge_by_name(&"1".to_string(), &"0".to_string(), &"same".to_string()) + .unwrap(); + // same edges to self do not exist + let same_edge_3 = + graph.int_get_edge_by_name(&"0".to_string(), &"0".to_string(), &"same".to_string()); + let same_edge_4 = + graph.int_get_edge_by_name(&"1".to_string(), &"1".to_string(), &"same".to_string()); + + assert_eq!(same_edge_1.weight().implied, true); + assert_eq!(same_edge_2.weight().implied, true); + assert_eq!(same_edge_3.is_none(), true); + assert_eq!(same_edge_4.is_none(), true); + graph.assert_correct_trackers(); +} + +#[wasm_bindgen_test] +fn test_implied_edge_rules_sibling_can_loop() { + let data = common::tdata_generate_tree(3, 2); + let mut graph = NoteGraph::new(); + + graph.set_transitive_rules(vec![ + TransitiveGraphRule::new(vec!["down".to_string()], "up".to_string(), 5, false, true), + TransitiveGraphRule::new(vec!["up".to_string()], "down".to_string(), 5, false, true), + TransitiveGraphRule::new( + vec!["up".to_string(), "down".to_string()], + "same".to_string(), + 5, + true, + false, + ), + ]); + + graph.build_graph(data.0, data.1); + // same edges between siblings exist + let same_edge_1 = graph + .int_get_edge_by_name(&"0".to_string(), &"1".to_string(), &"same".to_string()) + .unwrap(); + let same_edge_2 = graph + .int_get_edge_by_name(&"1".to_string(), &"0".to_string(), &"same".to_string()) + .unwrap(); + // same edges to self do not exist + let same_edge_3 = graph + .int_get_edge_by_name(&"0".to_string(), &"0".to_string(), &"same".to_string()) + .unwrap(); + let same_edge_4 = graph + .int_get_edge_by_name(&"1".to_string(), &"1".to_string(), &"same".to_string()) + .unwrap(); + + assert_eq!(same_edge_1.weight().implied, true); + assert_eq!(same_edge_2.weight().implied, true); + assert_eq!(same_edge_3.weight().implied, true); + assert_eq!(same_edge_4.weight().implied, true); + graph.assert_correct_trackers(); +} + +#[wasm_bindgen_test] +fn test_empty_update_does_nothing() { + let data = common::tdata_generate_tree(2, 2); + let mut graph_1 = common::tdata_to_graph(data.clone()); + let graph_2 = common::tdata_to_graph(data); + + let batch = BatchGraphUpdate::new(); + graph_1.apply_update(batch).unwrap(); + + assert!(graph_eq(&graph_1.graph, &graph_2.graph)); + graph_1.assert_correct_trackers(); +} + +#[wasm_bindgen_test] +fn test_remove_update() { + let data = common::tdata_generate_tree(2, 2); + let mut graph = common::tdata_to_graph(data); + + let mut batch = BatchGraphUpdate::new(); + RemoveNoteGraphUpdate::new("0".to_string()).add_to_batch(&mut batch); + graph.apply_update(batch).unwrap(); + + let node_0 = graph.int_get_node_index(&"0".to_string()).unwrap(); + let node_0_weight = graph.int_get_node_weight(node_0).unwrap(); + + assert_eq!(node_0_weight.resolved, false); + assert_eq!(graph.int_has_outgoing_edges(node_0), false); + assert!(graph.int_has_edge_by_name(&"root".to_string(), &"0".to_string(), &"down".to_string())); + + graph.assert_correct_trackers(); +} + +#[wasm_bindgen_test] +fn test_remove_add_update() { + let data = common::tdata_generate_tree(2, 2); + let mut graph_1 = common::tdata_to_graph(data.clone()); + let graph_2 = common::tdata_to_graph(data); + + let mut batch = BatchGraphUpdate::new(); + RemoveNoteGraphUpdate::new("0".to_string()).add_to_batch(&mut batch); + AddNoteGraphUpdate::new(GraphConstructionNodeData::new( + "0".to_string(), + vec![], + true, + false, + false, + )) + .add_to_batch(&mut batch); + AddEdgeGraphUpdate::new("0".to_string(), "00".to_string(), "down".to_string()) + .add_to_batch(&mut batch); + AddEdgeGraphUpdate::new("0".to_string(), "01".to_string(), "down".to_string()) + .add_to_batch(&mut batch); + + graph_1.apply_update(batch).unwrap(); + + assert!(graph_eq(&graph_1.graph, &graph_2.graph)); + + graph_1.assert_correct_trackers(); +} + +#[wasm_bindgen_test] +fn test_remove_add_separate_updates() { + let data = common::tdata_generate_tree(2, 2); + let mut graph_1 = common::tdata_to_graph(data.clone()); + let graph_2 = common::tdata_to_graph(data); + + let mut batch_1 = BatchGraphUpdate::new(); + RemoveNoteGraphUpdate::new("0".to_string()).add_to_batch(&mut batch_1); + + graph_1.apply_update(batch_1).unwrap(); + + let mut batch_2 = BatchGraphUpdate::new(); + AddNoteGraphUpdate::new(GraphConstructionNodeData::new( + "0".to_string(), + vec![], + true, + false, + false, + )) + .add_to_batch(&mut batch_2); + AddEdgeGraphUpdate::new("0".to_string(), "00".to_string(), "down".to_string()) + .add_to_batch(&mut batch_2); + AddEdgeGraphUpdate::new("0".to_string(), "01".to_string(), "down".to_string()) + .add_to_batch(&mut batch_2); + graph_1.apply_update(batch_2).unwrap(); + + assert!(graph_eq(&graph_1.graph, &graph_2.graph)); + + graph_1.assert_correct_trackers(); +} diff --git a/wasm/tests/web.rs b/wasm/tests/web.rs new file mode 100644 index 00000000..de5c1daf --- /dev/null +++ b/wasm/tests/web.rs @@ -0,0 +1,13 @@ +//! Test suite for the Web and headless browsers. + +#![cfg(target_arch = "wasm32")] + +extern crate wasm_bindgen_test; +use wasm_bindgen_test::*; + +wasm_bindgen_test_configure!(run_in_browser); + +#[wasm_bindgen_test] +fn pass() { + assert_eq!(1 + 1, 2); +} From 8d01fe0be1705dd832c359d737202bb8a54d7f0f Mon Sep 17 00:00:00 2001 From: Moritz Jung Date: Sat, 27 Apr 2024 21:33:41 +0200 Subject: [PATCH 02/65] working graph building; mermaid --- .../codeblocks/CodeblockMermaid.svelte | 175 +++++++++--------- src/graph/builders/index.ts | 22 ++- 2 files changed, 112 insertions(+), 85 deletions(-) diff --git a/src/components/codeblocks/CodeblockMermaid.svelte b/src/components/codeblocks/CodeblockMermaid.svelte index 0f0e0386..73de529d 100644 --- a/src/components/codeblocks/CodeblockMermaid.svelte +++ b/src/components/codeblocks/CodeblockMermaid.svelte @@ -27,90 +27,99 @@ export let errors: BreadcrumbsError[]; export let file_path: string; - const sort = get_edge_sorter( - // @ts-expect-error: ts(2345) - options.sort, - plugin.graph, - ); - - let traversal_items: TraversalStackItem[] = []; - let distances: Map = new Map(); - - // if the file_path is an empty string, so the code block is not rendered inside note, we fall back to the active file store - $: source_path = file_path - ? file_path - : $active_file_store - ? $active_file_store.path - : ""; - - // this is an exposed function that we can call from the outside to update the codeblock - export const update = () => { - traversal_items = get_traversal_items(); - distances = Distance.from_traversal_items(traversal_items); - }; + let mermaid: string = ""; - const base_traversal = (attr: EdgeAttrFilters) => - Traverse.gather_items(plugin.graph, source_path, (item) => - has_edge_attrs(item.edge, { - ...attr, - $or_target_ids: options["dataview-from-paths"], - }), - ); - - const edge_field_labels = - options.fields ?? plugin.settings.edge_fields.map((f) => f.label); - - const get_traversal_items = () => { - if (source_path && plugin.graph.hasNode(source_path)) { - return options["merge-fields"] - ? base_traversal({ $or_fields: options.fields }) - : edge_field_labels.flatMap((field) => - base_traversal({ field }), - ); - } else { - return []; - } + export const update = () => { + // TODO pass all the options and then implement them all the options on the rust side + mermaid = plugin.graph.generate_mermaid_graph([file_path], options.fields ?? [], options.depth[1] ?? 100).mermaid; }; - onMount(update); - - $: edges = traversal_items - .filter((item) => - is_between( - distances.get(item.edge.target_id) ?? 0, - options.depth[0] + 1, - options.depth[1], - ), - ) - .map((item) => item.edge) - .sort(sort); - - $: mermaid = Mermaid.from_edges(edges, { - kind: "graph", - click: { method: "class" }, - active_node_id: source_path, - renderer: options["mermaid-renderer"], - direction: options["mermaid-direction"], - show_attributes: options["show-attributes"], - - get_node_label: (node_id, _attr) => { - const file = plugin.app.vault.getFileByPath(node_id); - - return file - ? plugin.app.fileManager - .generateMarkdownLink(file, source_path) - .slice(2, -2) - : Paths.drop_ext( - Links.resolve_to_absolute_path( - plugin.app, - node_id, - source_path, - ), - ); - }, - }); - - $: log.debug(mermaid); + update(); + + // const sort = get_edge_sorter( + // // @ts-expect-error: ts(2345) + // options.sort, + // plugin.graph, + // ); + + // let traversal_items: TraversalStackItem[] = []; + // let distances: Map = new Map(); + + // // if the file_path is an empty string, so the code block is not rendered inside note, we fall back to the active file store + // $: source_path = file_path + // ? file_path + // : $active_file_store + // ? $active_file_store.path + // : ""; + + // // this is an exposed function that we can call from the outside to update the codeblock + // export const update = () => { + // traversal_items = get_traversal_items(); + // distances = Distance.from_traversal_items(traversal_items); + // }; + + // const base_traversal = (attr: EdgeAttrFilters) => + // Traverse.gather_items(plugin.graph, source_path, (item) => + // has_edge_attrs(item.edge, { + // ...attr, + // $or_target_ids: options["dataview-from-paths"], + // }), + // ); + + // const edge_field_labels = + // options.fields ?? plugin.settings.edge_fields.map((f) => f.label); + + // const get_traversal_items = () => { + // if (source_path && plugin.graph.hasNode(source_path)) { + // return options["merge-fields"] + // ? base_traversal({ $or_fields: options.fields }) + // : edge_field_labels.flatMap((field) => + // base_traversal({ field }), + // ); + // } else { + // return []; + // } + // }; + + // onMount(update); + + // $: edges = traversal_items + // .filter((item) => + // is_between( + // distances.get(item.edge.target_id) ?? 0, + // options.depth[0] + 1, + // options.depth[1], + // ), + // ) + // .map((item) => item.edge) + // .sort(sort); + + // $: mermaid = Mermaid.from_edges(edges, { + // kind: "graph", + // click: { method: "class" }, + // active_node_id: source_path, + // renderer: options["mermaid-renderer"], + // direction: options["mermaid-direction"], + // show_attributes: options["show-attributes"], + + // get_node_label: (node_id, _attr) => { + // const file = plugin.app.vault.getFileByPath(node_id); + + // return file + // ? plugin.app.fileManager + // .generateMarkdownLink(file, source_path) + // .slice(2, -2) + // : Paths.drop_ext( + // Links.resolve_to_absolute_path( + // plugin.app, + // node_id, + // source_path, + // ), + // ); + // }, + // }); + + // $: log.debug(mermaid);
@@ -122,7 +131,7 @@ {/if} - {#if traversal_items.length} + {#if mermaid}
- +
{:else}

No paths found.

diff --git a/src/graph/builders/index.ts b/src/graph/builders/index.ts index 3cbcc540..87f9d410 100644 --- a/src/graph/builders/index.ts +++ b/src/graph/builders/index.ts @@ -4,11 +4,11 @@ import type { BreadcrumbsError, EdgeToAdd } from "src/interfaces/graph"; import { log } from "src/logger"; import type BreadcrumbsPlugin from "src/main"; import { Timer } from "src/utils/timer"; -import { BCGraph, type BCNodeAttributes } from "../MyMultiGraph"; +import { BCGraph, type BCNode, type BCNodeAttributes } from "../MyMultiGraph"; import { add_explicit_edges } from "./explicit"; import { get_all_files, type AllFiles } from "./explicit/files"; // import { _add_implied_edges_transitive } from "./implied/transitive"; -import { GraphConstructionNodeData } from "wasm/pkg/breadcrumbs_graph_wasm"; +import { GraphConstructionEdgeData, GraphConstructionNodeData } from "wasm/pkg/breadcrumbs_graph_wasm"; const get_initial_nodes = (all_files: AllFiles) => { const nodes: GraphConstructionNodeData[] = [] @@ -58,6 +58,17 @@ const get_initial_nodes = (all_files: AllFiles) => { return nodes }; +// TODO: these functions should not be needed. The explicit edge builders should return the WASM data directly +const construction_data_from_node = (node: BCNode): GraphConstructionNodeData => { + const attr = node.attr; + return new GraphConstructionNodeData(node.id, attr.aliases ?? [], attr.resolved, attr.ignore_in_edges ?? false, attr.ignore_out_edges ?? false); +} + +// TODO: these functions should not be needed. The explicit edge builders should return the WASM data directly +const construction_data_from_edge = (edge: EdgeToAdd): GraphConstructionEdgeData => { + return new GraphConstructionEdgeData(edge.source_id, edge.target_id, edge.attr.field); +} + export const rebuild_graph = async (plugin: BreadcrumbsPlugin) => { const timer = new Timer(); @@ -82,6 +93,13 @@ export const rebuild_graph = async (plugin: BreadcrumbsPlugin) => { }), ); + const edges: GraphConstructionEdgeData[] = []; + + for (const { source, results } of explicit_edge_results) { + nodes.push(...results.nodes.map(construction_data_from_node)); + edges.push(...results.edges.map(construction_data_from_edge)); + } + log.debug(timer.elapsedMessage("Collecting edges and nodes")); // TODO From f822607e07ea45b47cbe57a98d3826f2b7ce3f05 Mon Sep 17 00:00:00 2001 From: Moritz Jung Date: Sat, 27 Apr 2024 22:14:12 +0200 Subject: [PATCH 03/65] minor changes --- src/graph/MyMultiGraph.ts | 352 +++++++++--------- src/graph/builders/explicit/dendron_note.ts | 2 +- .../builders/explicit/johnny_decimal_note.ts | 2 +- src/graph/builders/explicit/list_note.ts | 2 +- src/graph/builders/implied/transitive.ts | 2 +- src/graph/builders/index.ts | 2 +- wasm/src/graph.rs | 15 + 7 files changed, 196 insertions(+), 181 deletions(-) diff --git a/src/graph/MyMultiGraph.ts b/src/graph/MyMultiGraph.ts index 9248ac27..ff4cf5f4 100644 --- a/src/graph/MyMultiGraph.ts +++ b/src/graph/MyMultiGraph.ts @@ -62,179 +62,179 @@ export type BCEdge = { undirected: boolean; }; -export class BCGraph extends MultiGraph { - constructor( - /** Generally only for testing purposes, to quickly init a graph */ - input?: { - nodes?: { id: string; attr: BCNodeAttributes }[]; - edges?: { - source_id: string; - target_id: string; - attr: BCEdgeAttributes; - }[]; - }, - ) { - super(); - - if (input) { - input.nodes?.forEach(({ id, attr }) => - this.safe_add_node(id, attr), - ); - input.edges?.forEach((edge) => { - this.safe_add_node(edge.source_id, { resolved: true }); - this.safe_add_node(edge.target_id, { resolved: true }); - - this.safe_add_directed_edge( - edge.source_id, - edge.target_id, - edge.attr, - ); - }); - } - } - - safe_add_node(id: string, attr: BCNodeAttributes) { - try { - this.addNode(id, attr); - return true; - } catch (error) { - return false; - } - } - - /** Upsert a node by it's id (path). If it exists, patch attr, else addNode */ - upsert_node(id: string, attr: BCNodeAttributes) { - if (this.hasNode(id)) { - Object.keys(attr).forEach((key) => { - this.setNodeAttribute( - id, - key as keyof BCNodeAttributes, - attr[key as keyof BCNodeAttributes], - ); - }); - } else { - this.addNode(id, attr); - } - } - - safe_rename_node(old_id: string, new_id: string) { - const exists = { - old: this.hasNode(old_id), - new: this.hasNode(new_id), - }; - - if (!exists.old) { - return fail({ exists, message: "old_id doesn't exist" }); - } else if (exists.new) { - return fail({ exists, message: "new_id already exists" }); - } else { - // Add the new node - this.addNode(new_id, this.getNodeAttributes(old_id)); - - // WARN: For edge sources that depend on the name of the note (e.g. Dendron), - // This naive renaming won't _just_ work. - // The idea I mentioned about GraphBuilders being on a node-level would address this - // You could just rerun the builders for the new_id node - // But for now, the name-specific edges can just be filtered out here - // Freeze the old node - const old_edges = { - in: this.get_in_edges(old_id), - out: this.get_out_edges(old_id), - }; - - // Drop the old node (conveniently, this also drops the old edges) - this.dropNode(old_id); - - // Point the old edges at the new node - old_edges.in.forEach((old_in_edge) => { - // Might be a self-loop (source:self_is_sibling, for example) - is_self_loop(old_in_edge) - ? this.safe_add_directed_edge( - new_id, - new_id, - old_in_edge.attr, - ) - : this.safe_add_directed_edge( - old_in_edge.source_id, - new_id, - old_in_edge.attr, - ); - }); - - old_edges.out.forEach((old_out_edge) => { - // Only add the self-loop once. - // If it's there, it would have appeared in both the old_in and old_out edges - !is_self_loop(old_out_edge) && - this.safe_add_directed_edge( - new_id, - old_out_edge.target_id, - old_out_edge.attr, - ); - }); - } - - return succ({ exists }); - } - - /** Uniquely identify an edge based on its: - * - source_id - * - target_id - * - field - */ - make_edge_id = ( - source_id: string, - target_id: string, - attr: BCEdgeAttributes, - ) => `${source_id}|${attr.field}|${target_id}`; - // NOTE: These fields shouldn't actually dedupe an edge... I think what the user would consider an edge to be the same - // even if it was added for different reasons, but still to and from the same nodes, using the same field. - // Consider the commands/freeze-crumbs/index.md note as an example. If these fields were included, the implied relations would still show - // even tho there are now frozen real relations serving the exact same purpose. - // |${attr.explicit ? "explicit|" + attr.source : "implied|" + attr.implied_kind} - - /** Return true if the edge was added. - * Won't be added if it already exists (based on it's {@link this.make_edge_id}), - * or if it's target_node has ingore_in_edges */ - safe_add_directed_edge = ( - source_id: string, - target_id: string, - attr: BCEdgeAttributes, - ) => { - if (this.getNodeAttribute(target_id, "ignore_in_edges")) { - log.debug( - `ignore-in-edge > ${source_id} -${attr.field}-> ${target_id}`, - ); - return false; - } else if (this.getNodeAttribute(source_id, "ignore_out_edges")) { - log.debug( - `ignore-out-edge > ${source_id} -${attr.field}-> ${target_id}`, - ); - return false; - } - - const edge_id = this.make_edge_id(source_id, target_id, attr); - - if (!this.hasDirectedEdge(edge_id)) { - this.addDirectedEdgeWithKey(edge_id, source_id, target_id, attr); - return true; - } else { - return false; - } - }; - - /** safely returns [] if node_id and !hasNode(node_id) */ - get_in_edges = (node_id?: string) => - node_id - ? this.hasNode(node_id) - ? this.mapInEdges(node_id, objectify_edge) - : [] - : this.mapInEdges(objectify_edge); - - /** safely returns [] if node_id and !hasNode(node_id) */ - get_out_edges = (node_id?: string) => - node_id - ? this.hasNode(node_id) - ? this.mapOutEdges(node_id, objectify_edge) - : [] - : this.mapOutEdges(objectify_edge); -} +// export class BCGraph extends MultiGraph { +// constructor( +// /** Generally only for testing purposes, to quickly init a graph */ +// input?: { +// nodes?: { id: string; attr: BCNodeAttributes }[]; +// edges?: { +// source_id: string; +// target_id: string; +// attr: BCEdgeAttributes; +// }[]; +// }, +// ) { +// super(); + +// if (input) { +// input.nodes?.forEach(({ id, attr }) => +// this.safe_add_node(id, attr), +// ); +// input.edges?.forEach((edge) => { +// this.safe_add_node(edge.source_id, { resolved: true }); +// this.safe_add_node(edge.target_id, { resolved: true }); + +// this.safe_add_directed_edge( +// edge.source_id, +// edge.target_id, +// edge.attr, +// ); +// }); +// } +// } + +// safe_add_node(id: string, attr: BCNodeAttributes) { +// try { +// this.addNode(id, attr); +// return true; +// } catch (error) { +// return false; +// } +// } + +// /** Upsert a node by it's id (path). If it exists, patch attr, else addNode */ +// upsert_node(id: string, attr: BCNodeAttributes) { +// if (this.hasNode(id)) { +// Object.keys(attr).forEach((key) => { +// this.setNodeAttribute( +// id, +// key as keyof BCNodeAttributes, +// attr[key as keyof BCNodeAttributes], +// ); +// }); +// } else { +// this.addNode(id, attr); +// } +// } + +// safe_rename_node(old_id: string, new_id: string) { +// const exists = { +// old: this.hasNode(old_id), +// new: this.hasNode(new_id), +// }; + +// if (!exists.old) { +// return fail({ exists, message: "old_id doesn't exist" }); +// } else if (exists.new) { +// return fail({ exists, message: "new_id already exists" }); +// } else { +// // Add the new node +// this.addNode(new_id, this.getNodeAttributes(old_id)); + +// // WARN: For edge sources that depend on the name of the note (e.g. Dendron), +// // This naive renaming won't _just_ work. +// // The idea I mentioned about GraphBuilders being on a node-level would address this +// // You could just rerun the builders for the new_id node +// // But for now, the name-specific edges can just be filtered out here +// // Freeze the old node +// const old_edges = { +// in: this.get_in_edges(old_id), +// out: this.get_out_edges(old_id), +// }; + +// // Drop the old node (conveniently, this also drops the old edges) +// this.dropNode(old_id); + +// // Point the old edges at the new node +// old_edges.in.forEach((old_in_edge) => { +// // Might be a self-loop (source:self_is_sibling, for example) +// is_self_loop(old_in_edge) +// ? this.safe_add_directed_edge( +// new_id, +// new_id, +// old_in_edge.attr, +// ) +// : this.safe_add_directed_edge( +// old_in_edge.source_id, +// new_id, +// old_in_edge.attr, +// ); +// }); + +// old_edges.out.forEach((old_out_edge) => { +// // Only add the self-loop once. +// // If it's there, it would have appeared in both the old_in and old_out edges +// !is_self_loop(old_out_edge) && +// this.safe_add_directed_edge( +// new_id, +// old_out_edge.target_id, +// old_out_edge.attr, +// ); +// }); +// } + +// return succ({ exists }); +// } + +// /** Uniquely identify an edge based on its: +// * - source_id +// * - target_id +// * - field +// */ +// make_edge_id = ( +// source_id: string, +// target_id: string, +// attr: BCEdgeAttributes, +// ) => `${source_id}|${attr.field}|${target_id}`; +// // NOTE: These fields shouldn't actually dedupe an edge... I think what the user would consider an edge to be the same +// // even if it was added for different reasons, but still to and from the same nodes, using the same field. +// // Consider the commands/freeze-crumbs/index.md note as an example. If these fields were included, the implied relations would still show +// // even tho there are now frozen real relations serving the exact same purpose. +// // |${attr.explicit ? "explicit|" + attr.source : "implied|" + attr.implied_kind} + +// /** Return true if the edge was added. +// * Won't be added if it already exists (based on it's {@link this.make_edge_id}), +// * or if it's target_node has ingore_in_edges */ +// safe_add_directed_edge = ( +// source_id: string, +// target_id: string, +// attr: BCEdgeAttributes, +// ) => { +// if (this.getNodeAttribute(target_id, "ignore_in_edges")) { +// log.debug( +// `ignore-in-edge > ${source_id} -${attr.field}-> ${target_id}`, +// ); +// return false; +// } else if (this.getNodeAttribute(source_id, "ignore_out_edges")) { +// log.debug( +// `ignore-out-edge > ${source_id} -${attr.field}-> ${target_id}`, +// ); +// return false; +// } + +// const edge_id = this.make_edge_id(source_id, target_id, attr); + +// if (!this.hasDirectedEdge(edge_id)) { +// this.addDirectedEdgeWithKey(edge_id, source_id, target_id, attr); +// return true; +// } else { +// return false; +// } +// }; + +// /** safely returns [] if node_id and !hasNode(node_id) */ +// get_in_edges = (node_id?: string) => +// node_id +// ? this.hasNode(node_id) +// ? this.mapInEdges(node_id, objectify_edge) +// : [] +// : this.mapInEdges(objectify_edge); + +// /** safely returns [] if node_id and !hasNode(node_id) */ +// get_out_edges = (node_id?: string) => +// node_id +// ? this.hasNode(node_id) +// ? this.mapOutEdges(node_id, objectify_edge) +// : [] +// : this.mapOutEdges(objectify_edge); +// } diff --git a/src/graph/builders/explicit/dendron_note.ts b/src/graph/builders/explicit/dendron_note.ts index 1e3191c6..5048c3e8 100644 --- a/src/graph/builders/explicit/dendron_note.ts +++ b/src/graph/builders/explicit/dendron_note.ts @@ -1,5 +1,5 @@ import { META_ALIAS } from "src/const/metadata_fields"; -import type { BCGraph } from "src/graph/MyMultiGraph"; +// import type { BCGraph } from "src/graph/MyMultiGraph"; import type { BreadcrumbsError, EdgeBuilderResults, diff --git a/src/graph/builders/explicit/johnny_decimal_note.ts b/src/graph/builders/explicit/johnny_decimal_note.ts index e815cb3e..77c11710 100644 --- a/src/graph/builders/explicit/johnny_decimal_note.ts +++ b/src/graph/builders/explicit/johnny_decimal_note.ts @@ -1,5 +1,5 @@ import { META_ALIAS } from "src/const/metadata_fields"; -import type { BCGraph } from "src/graph/MyMultiGraph"; +// import type { BCGraph } from "src/graph/MyMultiGraph"; import type { BreadcrumbsError, EdgeBuilderResults, diff --git a/src/graph/builders/explicit/list_note.ts b/src/graph/builders/explicit/list_note.ts index 37734a64..bcd39f4f 100644 --- a/src/graph/builders/explicit/list_note.ts +++ b/src/graph/builders/explicit/list_note.ts @@ -1,7 +1,7 @@ import { Notice } from "obsidian"; import { META_ALIAS } from "src/const/metadata_fields"; import type { IDataview } from "src/external/dataview/interfaces"; -import type { BCGraph } from "src/graph/MyMultiGraph"; +// import type { BCGraph } from "src/graph/MyMultiGraph"; import type { EdgeBuilderResults, ExplicitEdgeBuilder, diff --git a/src/graph/builders/implied/transitive.ts b/src/graph/builders/implied/transitive.ts index a1a7e19e..14d75fb2 100644 --- a/src/graph/builders/implied/transitive.ts +++ b/src/graph/builders/implied/transitive.ts @@ -1,4 +1,4 @@ -import type { BCGraph } from "src/graph/MyMultiGraph"; +// import type { BCGraph } from "src/graph/MyMultiGraph"; import { Traverse } from "src/graph/traverse"; import type { EdgeBuilderResults } from "src/interfaces/graph"; import type { BreadcrumbsSettings } from "src/interfaces/settings"; diff --git a/src/graph/builders/index.ts b/src/graph/builders/index.ts index 87f9d410..648510e8 100644 --- a/src/graph/builders/index.ts +++ b/src/graph/builders/index.ts @@ -4,7 +4,7 @@ import type { BreadcrumbsError, EdgeToAdd } from "src/interfaces/graph"; import { log } from "src/logger"; import type BreadcrumbsPlugin from "src/main"; import { Timer } from "src/utils/timer"; -import { BCGraph, type BCNode, type BCNodeAttributes } from "../MyMultiGraph"; +import { type BCNode, type BCNodeAttributes } from "../MyMultiGraph"; import { add_explicit_edges } from "./explicit"; import { get_all_files, type AllFiles } from "./explicit/files"; // import { _add_implied_edges_transitive } from "./implied/transitive"; diff --git a/wasm/src/graph.rs b/wasm/src/graph.rs index ad3a59d4..9ed8dc74 100644 --- a/wasm/src/graph.rs +++ b/wasm/src/graph.rs @@ -105,10 +105,25 @@ impl NodeData { self.path.clone() } + #[wasm_bindgen(getter)] + pub fn aliases(&self) -> Vec { + self.aliases.clone() + } + #[wasm_bindgen(getter)] pub fn resolved(&self) -> bool { self.resolved } + + #[wasm_bindgen(getter)] + pub fn ignore_in_edges(&self) -> bool { + self.ignore_in_edges + } + + #[wasm_bindgen(getter)] + pub fn ignore_out_edges(&self) -> bool { + self.ignore_out_edges + } } impl NodeData { From de02fb481355fe965472a44dda2901ef73a0a972 Mon Sep 17 00:00:00 2001 From: Moritz Jung Date: Sun, 28 Apr 2024 17:03:57 +0200 Subject: [PATCH 04/65] progress on tree view --- src/commands/list_index/index.ts | 28 +++-- src/components/EdgeLink.svelte | 14 ++- src/components/NestedEdgeList.svelte | 13 ++- .../codeblocks/CodeblockMermaid.svelte | 6 +- .../codeblocks/CodeblockTree.svelte | 107 ++++++++++-------- src/graph/utils.ts | 15 +-- wasm/src/graph.rs | 54 +++++---- 7 files changed, 136 insertions(+), 101 deletions(-) diff --git a/src/commands/list_index/index.ts b/src/commands/list_index/index.ts index 85219197..c67261db 100644 --- a/src/commands/list_index/index.ts +++ b/src/commands/list_index/index.ts @@ -1,5 +1,5 @@ import type { EdgeSortId } from "src/const/graph"; -import type { BCGraph, EdgeAttribute } from "src/graph/MyMultiGraph"; +import type { EdgeAttribute } from "src/graph/MyMultiGraph"; import { Traverse, type EdgeTree } from "src/graph/traverse"; import { get_edge_sorter, @@ -11,6 +11,7 @@ import type { ShowNodeOptions } from "src/interfaces/settings"; import { Links } from "src/utils/links"; import { untyped_pick } from "src/utils/objects"; import { url_search_params } from "src/utils/url"; +import type { NoteGraph, RecTraversalData } from "wasm/pkg/breadcrumbs_graph_wasm"; export namespace ListIndex { export type Options = { @@ -44,7 +45,7 @@ export namespace ListIndex { }; export const edge_tree_to_list_index = ( - tree: EdgeTree[], + tree: RecTraversalData[], options: Pick< Options, "link_kind" | "indent" | "show_node_options" | "show_attributes" @@ -53,21 +54,23 @@ export namespace ListIndex { let index = ""; const real_indent = options.indent.replace(/\\t/g, "\t"); - tree.forEach(({ children, depth, edge }) => { - const display = stringify_node(edge.target_id, edge.target_attr, { + tree.forEach(({ children, depth, edge, node }) => { + const display = stringify_node(node, { show_node_options: options.show_node_options, }); - const link = Links.ify(edge.target_id, display, { + const link = Links.ify(node.path, display, { link_kind: options.link_kind, }); - const attr = options.show_attributes.length - ? ` (${url_search_params( - untyped_pick(edge.attr, options.show_attributes), - { trim_lone_param: true }, - )})` - : ""; + // TODO: show_attributes + // const attr = options.show_attributes.length + // ? ` (${url_search_params( + // untyped_pick(edge.attr, options.show_attributes), + // { trim_lone_param: true }, + // )})` + // : ""; + const attr = ""; index += real_indent.repeat(depth) + `- ${link}${attr}\n`; @@ -77,8 +80,9 @@ export namespace ListIndex { return index; }; + // TODO export const build = ( - graph: BCGraph, + graph: NoteGraph, start_node: string, options: Options, ) => diff --git a/src/components/EdgeLink.svelte b/src/components/EdgeLink.svelte index a320b7b0..c811f4cf 100644 --- a/src/components/EdgeLink.svelte +++ b/src/components/EdgeLink.svelte @@ -4,15 +4,17 @@ import { stringify_node } from "src/graph/utils"; import type { ShowNodeOptions } from "src/interfaces/settings"; import BreadcrumbsPlugin from "src/main"; + import type { EdgeData, NodeData } from "wasm/pkg/breadcrumbs_graph_wasm"; - export let edge: Pick; + export let edge: EdgeData; + export let target_node: NodeData; export let plugin: BreadcrumbsPlugin; export let show_node_options: ShowNodeOptions; export let cls = ""; const { dendron_note } = plugin.settings.explicit_edge_sources; - const display = stringify_node(edge.target_id, edge.target_attr, { + const display = stringify_node(target_node, { show_node_options, trim_basename_delimiter: dendron_note.enabled && dendron_note.display_trimmed @@ -24,9 +26,9 @@ diff --git a/src/components/NestedEdgeList.svelte b/src/components/NestedEdgeList.svelte index 3c153d01..312a1eac 100644 --- a/src/components/NestedEdgeList.svelte +++ b/src/components/NestedEdgeList.svelte @@ -9,10 +9,11 @@ import EdgeLink from "./EdgeLink.svelte"; import ChevronOpener from "./button/ChevronOpener.svelte"; import TreeItemFlair from "./obsidian/TreeItemFlair.svelte"; + import type { RecTraversalData } from "wasm/pkg/breadcrumbs_graph_wasm"; export let plugin: BreadcrumbsPlugin; - export let tree: EdgeTree[]; + export let tree: RecTraversalData[]; export let open_signal: boolean | null; export let show_node_options: ShowNodeOptions; @@ -30,8 +31,8 @@ open_signal = null; } - -{#each tree.sort((a, b) => sort(a.edge, b.edge)) as item, i} + +{#each tree as item, i}
{#if item.children.length} @@ -44,19 +45,21 @@
- {#if show_attributes?.length} + + {#if item.children.length} diff --git a/src/components/codeblocks/CodeblockMermaid.svelte b/src/components/codeblocks/CodeblockMermaid.svelte index 73de529d..85e57429 100644 --- a/src/components/codeblocks/CodeblockMermaid.svelte +++ b/src/components/codeblocks/CodeblockMermaid.svelte @@ -34,8 +34,10 @@ mermaid = plugin.graph.generate_mermaid_graph([file_path], options.fields ?? [], options.depth[1] ?? 100).mermaid; }; - update(); - + onMount(() => { + update(); + }); + // const sort = get_edge_sorter( // // @ts-expect-error: ts(2345) // options.sort, diff --git a/src/components/codeblocks/CodeblockTree.svelte b/src/components/codeblocks/CodeblockTree.svelte index f2bb8f81..c647b113 100644 --- a/src/components/codeblocks/CodeblockTree.svelte +++ b/src/components/codeblocks/CodeblockTree.svelte @@ -14,6 +14,7 @@ import NestedEdgeList from "../NestedEdgeList.svelte"; import CopyToClipboardButton from "../button/CopyToClipboardButton.svelte"; import CodeblockErrors from "./CodeblockErrors.svelte"; + import type { RecTraversalData } from "wasm/pkg/breadcrumbs_graph_wasm"; export let plugin: BreadcrumbsPlugin; export let options: ICodeblock["Options"]; @@ -27,59 +28,71 @@ ); const { show_node_options } = plugin.settings.views.codeblocks; - let tree: EdgeTree[] = []; + let tree: RecTraversalData[] = []; - // if the file_path is an empty string, so the code block is not rendered inside note, we fall back to the active file store - $: source_path = file_path - ? file_path - : $active_file_store - ? $active_file_store.path - : ""; - - // this is an exposed function that we can call from the outside to update the codeblock export const update = () => { - tree = get_tree(); + tree = plugin.graph.rec_traverse( + file_path, + options.fields ?? [], + options.depth[1] ?? 100, + ); }; - const base_traversal = (attr: EdgeAttrFilters) => - Traverse.build_tree( - plugin.graph, - source_path, - { max_depth: options.depth[1] }, - (e) => - has_edge_attrs(e, { - ...attr, - $or_target_ids: options["dataview-from-paths"], - }), - ); + onMount(() => { + update(); + }); - const edge_field_labels = - options.fields ?? plugin.settings.edge_fields.map((f) => f.label); - - const get_tree = () => { - if (source_path && plugin.graph.hasNode(source_path)) { - const traversal = options["merge-fields"] - ? base_traversal({ $or_fields: options.fields }) - : edge_field_labels.flatMap((field) => - base_traversal({ field }), - ); - - // NOTE: The flattening is done here so that: - // - We can use NestedEdgeList for both modes - // - ListIndex builds from an EdgeTree[] as well - return options.flat - ? Traverse.flatten_tree(traversal).map((item) => ({ - depth: 0, - children: [], - edge: item.edge, - })) - : traversal; - } else { - return []; - } - }; + // // if the file_path is an empty string, so the code block is not rendered inside note, we fall back to the active file store + // $: source_path = file_path + // ? file_path + // : $active_file_store + // ? $active_file_store.path + // : ""; + + // // this is an exposed function that we can call from the outside to update the codeblock + // export const update = () => { + // tree = get_tree(); + // }; + + // const base_traversal = (attr: EdgeAttrFilters) => + // Traverse.build_tree( + // plugin.graph, + // source_path, + // { max_depth: options.depth[1] }, + // (e) => + // has_edge_attrs(e, { + // ...attr, + // $or_target_ids: options["dataview-from-paths"], + // }), + // ); + + // const edge_field_labels = + // options.fields ?? plugin.settings.edge_fields.map((f) => f.label); + + // const get_tree = () => { + // if (source_path && plugin.graph.hasNode(source_path)) { + // const traversal = options["merge-fields"] + // ? base_traversal({ $or_fields: options.fields }) + // : edge_field_labels.flatMap((field) => + // base_traversal({ field }), + // ); + + // // NOTE: The flattening is done here so that: + // // - We can use NestedEdgeList for both modes + // // - ListIndex builds from an EdgeTree[] as well + // return options.flat + // ? Traverse.flatten_tree(traversal).map((item) => ({ + // depth: 0, + // children: [], + // edge: item.edge, + // })) + // : traversal; + // } else { + // return []; + // } + // }; - onMount(update); + // onMount(update);
diff --git a/src/graph/utils.ts b/src/graph/utils.ts index ccbcea38..b82fb3ee 100644 --- a/src/graph/utils.ts +++ b/src/graph/utils.ts @@ -7,33 +7,34 @@ import { Paths } from "src/utils/paths"; import type { BCEdge, BCEdgeAttributes, - BCGraph, BCNodeAttributes, } from "./MyMultiGraph"; +import type { NodeData } from "wasm/pkg/breadcrumbs_graph_wasm"; export const is_self_loop = (edge: Pick) => edge.source_id === edge.target_id; export const stringify_node = ( - node_id: string, - node_attr: BCNodeAttributes, + node: NodeData, options?: { show_node_options?: ShowNodeOptions; trim_basename_delimiter?: string; }, ) => { - if (options?.show_node_options?.alias && node_attr.aliases?.length) { - return node_attr.aliases.at(0)!; + if (options?.show_node_options?.alias && node.aliases?.length) { + return node.aliases.at(0)!; } else if (options?.trim_basename_delimiter) { - return Paths.drop_ext(node_id) + return Paths.drop_ext(node.path) .split("/") .pop()! .split(options.trim_basename_delimiter) .last()!; } else { - return Paths.show(node_id, options?.show_node_options); + return Paths.show(node.path, options?.show_node_options); } }; + +// UNUSED export const stringify_edge = ( edge: BCEdge, options?: { diff --git a/wasm/src/graph.rs b/wasm/src/graph.rs index 9ed8dc74..7617c181 100644 --- a/wasm/src/graph.rs +++ b/wasm/src/graph.rs @@ -192,9 +192,13 @@ impl MermaidGraphData { #[wasm_bindgen] #[derive(Clone, Debug)] pub struct RecTraversalData { + // the node that was traversed node: NodeData, - edge_type: String, + // the edge that was traversed to get to the node + edge: EdgeData, + // the depth of the node in the traversal depth: u32, + // the children of the node children: Vec, } @@ -206,8 +210,8 @@ impl RecTraversalData { } #[wasm_bindgen(getter)] - pub fn edge_type(&self) -> String { - self.edge_type.clone() + pub fn edge(&self) -> EdgeData { + self.edge.clone() } #[wasm_bindgen(getter)] @@ -289,7 +293,7 @@ impl NoteGraph { utils::log(format!("Node already exists: {}", info_node.path)); continue; } - + self.node_hash.insert( info_node.path.clone(), self.graph @@ -547,24 +551,30 @@ impl NoteGraph { entry_node: String, edge_types: Vec, max_depth: u32, - ) -> Result { + ) -> Result> { let now = Instant::now(); - let mut node_count = 1; - - // let node_index = self.get_node_index(node.clone()).unwrap(); let start_node = self .int_get_node_index(&entry_node) .ok_or(NoteGraphError::new("Node not found"))?; - let result = self.int_rec_traverse( - start_node, - &edge_types, - String::new(), - 0, - max_depth, - &mut node_count, - ); + let mut node_count = 1; + let mut result = Vec::new(); + + for edge in self.graph.edges(start_node) { + let edge_weight = edge.weight(); + + result.push(self.int_rec_traverse( + start_node, + edge_weight.clone(), + &edge_types, + 0, + max_depth, + &mut node_count, + )?); + + node_count += 1; + } let total_elapsed = now.elapsed(); utils::log(format!( @@ -572,7 +582,7 @@ impl NoteGraph { total_elapsed, node_count )); - result + Ok(result) } pub fn log(&self) { @@ -734,8 +744,8 @@ impl NoteGraph { pub fn int_rec_traverse( &self, node: NodeIndex, + edge: EdgeData, edge_types: &Vec, - edge_type: String, depth: u32, max_depth: u32, node_count: &mut usize, @@ -745,13 +755,13 @@ impl NoteGraph { if depth < max_depth { for edge in self.graph.edges(node) { let target = edge.target(); - let edge_type = &edge.weight().edge_type; + let edge_weight = edge.weight(); - if edge_types.contains(edge_type) { + if edge_types.contains(&edge_weight.edge_type) { new_children.push(self.int_rec_traverse( target, + edge_weight.clone(), edge_types, - edge_type.clone(), depth + 1, max_depth, node_count, @@ -764,7 +774,7 @@ impl NoteGraph { Ok(RecTraversalData { node: self.int_get_node_weight(node)?.clone(), - edge_type, + edge, depth, children: new_children, }) From 563bde62230fd74a12d9c1520fb4066ec7f9ab58 Mon Sep 17 00:00:00 2001 From: Moritz Jung Date: Mon, 29 Apr 2024 14:40:38 +0200 Subject: [PATCH 05/65] edge sorting --- src/components/EdgeLink.svelte | 8 +- src/components/NestedEdgeList.svelte | 15 +- .../codeblocks/CodeblockTree.svelte | 3 +- src/graph/builders/index.ts | 2 +- src/graph/traverse.ts | 3 +- src/graph/utils.ts | 104 ++++---- wasm/src/graph.rs | 240 ++++++++++++++---- wasm/src/graph_construction.rs | 18 +- wasm/src/graph_rules.rs | 4 + wasm/src/graph_update.rs | 19 +- wasm/tests/common/mod.rs | 2 + wasm/tests/graph.rs | 94 +++++-- 12 files changed, 365 insertions(+), 147 deletions(-) diff --git a/src/components/EdgeLink.svelte b/src/components/EdgeLink.svelte index c811f4cf..eabee132 100644 --- a/src/components/EdgeLink.svelte +++ b/src/components/EdgeLink.svelte @@ -4,14 +4,14 @@ import { stringify_node } from "src/graph/utils"; import type { ShowNodeOptions } from "src/interfaces/settings"; import BreadcrumbsPlugin from "src/main"; - import type { EdgeData, NodeData } from "wasm/pkg/breadcrumbs_graph_wasm"; + import type { EdgeData, EdgeStruct, NodeData } from "wasm/pkg/breadcrumbs_graph_wasm"; - export let edge: EdgeData; - export let target_node: NodeData; + export let edge: EdgeStruct; export let plugin: BreadcrumbsPlugin; export let show_node_options: ShowNodeOptions; export let cls = ""; + let target_node = edge.target; const { dendron_note } = plugin.settings.explicit_edge_sources; const display = stringify_node(target_node, { @@ -28,7 +28,7 @@ {display} path={target_node.path} resolved={target_node.resolved} - cls="{cls} BC-edge {edge.implied + cls="{cls} BC-edge {!edge.implied ? 'BC-edge-explicit' : `BC-edge-implied BC-edge-implied-${ 'transitive' /*edge.attr.implied_kind */}`}" /> diff --git a/src/components/NestedEdgeList.svelte b/src/components/NestedEdgeList.svelte index 312a1eac..fb9f2d30 100644 --- a/src/components/NestedEdgeList.svelte +++ b/src/components/NestedEdgeList.svelte @@ -31,8 +31,8 @@ open_signal = null; } - -{#each tree as item, i} + +{#each tree.sort((a, b) => sort(a.edge, b.edge)) as item, i}
{#if item.children.length} @@ -45,21 +45,16 @@
- - + {/if} {#if item.children.length} diff --git a/src/components/codeblocks/CodeblockTree.svelte b/src/components/codeblocks/CodeblockTree.svelte index c647b113..0a92b5b4 100644 --- a/src/components/codeblocks/CodeblockTree.svelte +++ b/src/components/codeblocks/CodeblockTree.svelte @@ -24,7 +24,6 @@ const sort = get_edge_sorter( // @ts-expect-error: ts(2345) options.sort, - plugin.graph, ); const { show_node_options } = plugin.settings.views.codeblocks; @@ -42,6 +41,8 @@ update(); }); + // TODO reimplement all this logic + // // if the file_path is an empty string, so the code block is not rendered inside note, we fall back to the active file store // $: source_path = file_path // ? file_path diff --git a/src/graph/builders/index.ts b/src/graph/builders/index.ts index 648510e8..9e5ed996 100644 --- a/src/graph/builders/index.ts +++ b/src/graph/builders/index.ts @@ -66,7 +66,7 @@ const construction_data_from_node = (node: BCNode): GraphConstructionNodeData => // TODO: these functions should not be needed. The explicit edge builders should return the WASM data directly const construction_data_from_edge = (edge: EdgeToAdd): GraphConstructionEdgeData => { - return new GraphConstructionEdgeData(edge.source_id, edge.target_id, edge.attr.field); + return new GraphConstructionEdgeData(edge.source_id, edge.target_id, edge.attr.field, edge.attr.explicit ? edge.attr.source : edge.attr.implied_kind); } export const rebuild_graph = async (plugin: BreadcrumbsPlugin) => { diff --git a/src/graph/traverse.ts b/src/graph/traverse.ts index 515f6d9e..024d3312 100644 --- a/src/graph/traverse.ts +++ b/src/graph/traverse.ts @@ -1,3 +1,4 @@ +import type { RecTraversalData } from "wasm/pkg/breadcrumbs_graph_wasm"; import { BCGraph, type BCEdge, type BCEdgeAttributes } from "./MyMultiGraph"; import { has_edge_attrs, type EdgeSorter } from "./utils"; @@ -120,7 +121,7 @@ const tree_to_all_paths = (tree: EdgeTree[]): BCEdge[][] => { /** Sort a nested list of paths on a per-depth level. * Mutates the input. */ -const sort_edge_tree = (tree: EdgeTree[], sorter: EdgeSorter) => { +const sort_edge_tree = (tree: RecTraversalData[], sorter: EdgeSorter) => { tree.forEach((nested_path) => { nested_path.children = sort_edge_tree(nested_path.children, sorter); }); diff --git a/src/graph/utils.ts b/src/graph/utils.ts index b82fb3ee..eaa1913c 100644 --- a/src/graph/utils.ts +++ b/src/graph/utils.ts @@ -9,7 +9,7 @@ import type { BCEdgeAttributes, BCNodeAttributes, } from "./MyMultiGraph"; -import type { NodeData } from "wasm/pkg/breadcrumbs_graph_wasm"; +import type { EdgeStruct, NodeData } from "wasm/pkg/breadcrumbs_graph_wasm"; export const is_self_loop = (edge: Pick) => edge.source_id === edge.target_id; @@ -53,15 +53,16 @@ export const stringify_edge = ( return list.join(" "); }; -export type EdgeSorter = (a: BCEdge, b: BCEdge) => number; +// TODO: the sorting should probably happen in the WASM code +export type EdgeSorter = (a: EdgeStruct, b: EdgeStruct) => number; const sorters = { - path: (order) => (a, b) => a.target_id.localeCompare(b.target_id) * order, + path: (order) => (a, b) => a.target.path.localeCompare(b.target.path) * order, basename: (order) => (a, b) => { const [a_field, b_field] = [ - Paths.drop_folder(a.target_id), - Paths.drop_folder(b.target_id), + Paths.drop_folder(a.target.path), + Paths.drop_folder(b.target.path), ]; return a_field.localeCompare(b_field) * order; @@ -69,8 +70,8 @@ const sorters = { field: (order) => (a, b) => { const [a_field, b_field] = [ - a.attr.field ?? "null", - b.attr.field ?? "null", + a.edge_type ?? "null", + b.edge_type ?? "null", ]; return a_field.localeCompare(b_field) * order; @@ -79,8 +80,7 @@ const sorters = { export const get_edge_sorter: ( sort: EdgeSortId, - graph: BCGraph, -) => EdgeSorter = (sort, graph) => { +) => EdgeSorter = (sort) => { switch (sort.field) { case "path": { return sorters.path(sort.order); @@ -96,20 +96,11 @@ export const get_edge_sorter: ( case "explicit": { return (a, b) => { - if (a.attr.explicit === true && b.attr.explicit === true) { - return ( - a.attr.source.localeCompare(b.attr.source) * sort.order - ); - } else if ( - a.attr.explicit === false && - b.attr.explicit === false - ) { - return ( - a.attr.implied_kind.localeCompare(b.attr.implied_kind) * - sort.order - ); + + if (a.implied === b.implied) { + return a.edge_source.localeCompare(b.edge_source) * sort.order; } else { - return a.attr.explicit ? sort.order : -sort.order; + return a.implied ? -sort.order : sort.order; } }; } @@ -125,41 +116,42 @@ export const get_edge_sorter: ( } switch (sort.field.split(":")[0]) { + // TODO // BREAKING: Deprecate in favour of neighbour-field - case "neighbour": - case "neighbour-field": { - const field = sort.field.split(":", 2).at(1); - const cache: Record = {}; - - return (a, b) => { - const [a_neighbour, b_neighbour] = [ - (cache[a.target_id] ??= graph - .get_out_edges(a.target_id) - .filter((e) => has_edge_attrs(e, { field })) - .at(0)), - - (cache[b.target_id] ??= graph - .get_out_edges(b.target_id) - .filter((e) => has_edge_attrs(e, { field })) - .at(0)), - ]; - - if (!a_neighbour || !b_neighbour) { - // NOTE: This puts the node with no neighbours last - // Which makes sense, I think. It simulates a traversal, where the node with no neighbours is the end of the path - return a_neighbour - ? -sort.order - : b_neighbour - ? sort.order - : 0; - } else { - return sorters.path(sort.order)( - a_neighbour, - b_neighbour, - ); - } - }; - } + // case "neighbour": + // case "neighbour-field": { + // const field = sort.field.split(":", 2).at(1); + // const cache: Record = {}; + + // return (a, b) => { + // const [a_neighbour, b_neighbour] = [ + // (cache[a.target_id] ??= graph + // .get_out_edges(a.target_id) + // .filter((e) => has_edge_attrs(e, { field })) + // .at(0)), + + // (cache[b.target_id] ??= graph + // .get_out_edges(b.target_id) + // .filter((e) => has_edge_attrs(e, { field })) + // .at(0)), + // ]; + + // if (!a_neighbour || !b_neighbour) { + // // NOTE: This puts the node with no neighbours last + // // Which makes sense, I think. It simulates a traversal, where the node with no neighbours is the end of the path + // return a_neighbour + // ? -sort.order + // : b_neighbour + // ? sort.order + // : 0; + // } else { + // return sorters.path(sort.order)( + // a_neighbour, + // b_neighbour, + // ); + // } + // }; + // } default: { return (_a, _b) => sort.order; diff --git a/wasm/src/graph.rs b/wasm/src/graph.rs index 7617c181..e95d1455 100644 --- a/wasm/src/graph.rs +++ b/wasm/src/graph.rs @@ -28,6 +28,8 @@ pub struct EdgeData { #[wasm_bindgen(skip)] pub edge_type: String, #[wasm_bindgen(skip)] + pub edge_source: String, + #[wasm_bindgen(skip)] pub implied: bool, #[wasm_bindgen(skip)] pub round: u8, @@ -36,9 +38,10 @@ pub struct EdgeData { #[wasm_bindgen] impl EdgeData { #[wasm_bindgen(constructor)] - pub fn new(edge_type: String, implied: bool, round: u8) -> EdgeData { + pub fn new(edge_type: String, edge_source: String, implied: bool, round: u8) -> EdgeData { EdgeData { edge_type, + edge_source, implied, round, } @@ -49,6 +52,11 @@ impl EdgeData { self.edge_type.clone() } + #[wasm_bindgen(getter)] + pub fn edge_source(&self) -> String { + self.edge_source.clone() + } + #[wasm_bindgen(getter)] pub fn implied(&self) -> bool { self.implied @@ -148,6 +156,108 @@ impl NodeData { } } +#[wasm_bindgen] +#[derive(Clone, Debug)] +pub struct EdgeStruct { + #[wasm_bindgen(skip)] + pub source: NodeData, + #[wasm_bindgen(skip)] + pub target: NodeData, + + #[wasm_bindgen(skip)] + pub edge_type: String, + #[wasm_bindgen(skip)] + pub edge_source: String, + #[wasm_bindgen(skip)] + pub implied: bool, + #[wasm_bindgen(skip)] + pub round: u8, +} + +#[wasm_bindgen] +impl EdgeStruct { + #[wasm_bindgen(constructor)] + pub fn new(source: NodeData, target: NodeData, edge: EdgeData) -> EdgeStruct { + EdgeStruct { + source, + target, + edge_type: edge.edge_type, + edge_source: edge.edge_source, + implied: edge.implied, + round: edge.round, + } + } + + #[wasm_bindgen(getter)] + pub fn source(&self) -> NodeData { + self.source.clone() + } + + #[wasm_bindgen(getter)] + pub fn target(&self) -> NodeData { + self.target.clone() + } + + #[wasm_bindgen(getter)] + pub fn edge_type(&self) -> String { + self.edge_type.clone() + } + + #[wasm_bindgen(getter)] + pub fn edge_source(&self) -> String { + self.edge_source.clone() + } + + #[wasm_bindgen(getter)] + pub fn implied(&self) -> bool { + self.implied + } + + #[wasm_bindgen(getter)] + pub fn round(&self) -> u8 { + self.round + } + + pub fn get_attribute_label(&self, attributes: Vec) -> String { + let mut result = vec![]; + + // the mapping that exist on the JS side are as follows + // "field" | "explicit" | "source" | "implied_kind" | "round" + + // TODO: maybe change the attribute options so that the JS side better matches the data + + for attribute in attributes { + let data = match attribute.as_str() { + "field" => Some(("field", self.edge_type.clone())), + "explicit" => Some(("explicit", (!self.implied).to_string())), + "source" => { + if !self.implied { + Some(("source", self.edge_source.clone())) + } else { + None + } + } + "implied_kind" => { + if self.implied { + Some(("implied_kind", self.edge_source.clone())) + } else { + None + } + } + "round" => Some(("round", self.round.to_string())), + _ => None, + }; + + match data { + Some(data) => result.push(format!("{}={}", data.0, data.1)), + None => {} + } + } + + result.join(" ") + } +} + // impl PartialEq for NodeData { // fn eq(&self, other: &Self) -> bool { // self.path == other.path @@ -192,10 +302,8 @@ impl MermaidGraphData { #[wasm_bindgen] #[derive(Clone, Debug)] pub struct RecTraversalData { - // the node that was traversed - node: NodeData, - // the edge that was traversed to get to the node - edge: EdgeData, + // the edge struct that was traversed + edge: EdgeStruct, // the depth of the node in the traversal depth: u32, // the children of the node @@ -204,13 +312,17 @@ pub struct RecTraversalData { #[wasm_bindgen] impl RecTraversalData { - #[wasm_bindgen(getter)] - pub fn node(&self) -> NodeData { - self.node.clone() + #[wasm_bindgen(constructor)] + pub fn new(edge: EdgeStruct, depth: u32, children: Vec) -> RecTraversalData { + RecTraversalData { + edge, + depth, + children, + } } #[wasm_bindgen(getter)] - pub fn edge(&self) -> EdgeData { + pub fn edge(&self) -> EdgeStruct { self.edge.clone() } @@ -224,6 +336,11 @@ impl RecTraversalData { self.children.clone() } + #[wasm_bindgen(setter)] + pub fn set_children(&mut self, children: Vec) { + self.children = children; + } + #[wasm_bindgen(js_name = toString)] pub fn to_string(&self) -> String { format!("{:?}", self) @@ -302,7 +419,12 @@ impl NoteGraph { } for edge in edges { - self.int_safe_add_edge(&edge.from, &edge.to, &edge.edge_type); + self.int_safe_add_edge( + &edge.source, + &edge.target, + &edge.edge_type, + &edge.edge_source, + ); } let elapsed = now.elapsed(); @@ -557,16 +679,23 @@ impl NoteGraph { let start_node = self .int_get_node_index(&entry_node) .ok_or(NoteGraphError::new("Node not found"))?; + let start_node_weight = self.int_get_node_weight(start_node)?; let mut node_count = 1; let mut result = Vec::new(); for edge in self.graph.edges(start_node) { - let edge_weight = edge.weight(); + let target = edge.target(); + + let edge_struct = EdgeStruct::new( + start_node_weight.clone(), + self.int_get_node_weight(target)?.clone(), + edge.weight().clone(), + ); result.push(self.int_rec_traverse( start_node, - edge_weight.clone(), + edge_struct, &edge_types, 0, max_depth, @@ -620,7 +749,7 @@ impl NoteGraph { break; } - let mut edges_to_add: Vec<(NodeIndex, NodeIndex, String)> = Vec::new(); + let mut edges_to_add: Vec<(NodeIndex, NodeIndex, EdgeData)> = Vec::new(); for rule in self.transitive_rules.iter() { // if there is any edge type that the graph doesn't have, we can skip the rule @@ -667,10 +796,17 @@ impl NoteGraph { continue; } + let edge_data = EdgeData::new( + rule.edge_type.clone(), + format!("transitive:{}", rule.name), + true, + i, + ); + if rule.close_reversed { - edges_to_add.push((end_node, start_node, rule.edge_type.clone())); + edges_to_add.push((end_node, start_node, edge_data)); } else { - edges_to_add.push((start_node, end_node, rule.edge_type.clone())); + edges_to_add.push((start_node, end_node, edge_data)); } } } @@ -687,11 +823,11 @@ impl NoteGraph { let now2 = Instant::now(); utils::log(format!("Adding {} Edges ", edges_to_add.len())); - for (from, to, edge_type) in edges_to_add { + for (from, to, edge_data) in edges_to_add { self.int_add_edge( from, to, - EdgeData::new(edge_type, true, i), + edge_data, &mut Some(&mut current_edge_type_tracker), ); } @@ -744,7 +880,7 @@ impl NoteGraph { pub fn int_rec_traverse( &self, node: NodeIndex, - edge: EdgeData, + edge: EdgeStruct, edge_types: &Vec, depth: u32, max_depth: u32, @@ -753,14 +889,21 @@ impl NoteGraph { let mut new_children = Vec::new(); if depth < max_depth { - for edge in self.graph.edges(node) { - let target = edge.target(); - let edge_weight = edge.weight(); + for outgoing_edge in self.graph.edges(node) { + let edge_weight = outgoing_edge.weight(); if edge_types.contains(&edge_weight.edge_type) { + let target = outgoing_edge.target(); + + let edge_struct = EdgeStruct::new( + edge.target.clone(), + self.int_get_node_weight(target)?.clone(), + edge_weight.clone(), + ); + new_children.push(self.int_rec_traverse( target, - edge_weight.clone(), + edge_struct, edge_types, depth + 1, max_depth, @@ -772,12 +915,7 @@ impl NoteGraph { *node_count += new_children.len(); - Ok(RecTraversalData { - node: self.int_get_node_weight(node)?.clone(), - edge, - depth, - children: new_children, - }) + Ok(RecTraversalData::new(edge, depth, new_children)) } /// Returns the node weight for a specific node index. @@ -1212,49 +1350,55 @@ impl NoteGraph { } } - pub fn int_safe_add_edge(&mut self, from_path: &String, to_path: &String, edge_type: &String) { - let from = self.node_hash.get(from_path); - let to = self.node_hash.get(to_path); + pub fn int_safe_add_edge( + &mut self, + source_path: &String, + target_path: &String, + edge_type: &String, + edge_source: &String, + ) { + let source = self.node_hash.get(source_path); + let target = self.node_hash.get(target_path); - let from_index: NodeIndex; - let to_index: NodeIndex; + let source_index: NodeIndex; + let target_index: NodeIndex; let mut add_from_to_hash = false; let mut add_to_to_hash = false; - if from.is_none() { - from_index = self + if source.is_none() { + source_index = self .graph - .add_node(NodeData::new_unresolved(from_path.clone())); + .add_node(NodeData::new_unresolved(source_path.clone())); add_from_to_hash = true; } else { - from_index = from.unwrap().clone(); + source_index = source.unwrap().clone(); } - if to.is_none() { - if from_path == to_path { - to_index = from_index; + if target.is_none() { + if source_path == target_path { + target_index = source_index; } else { - to_index = self + target_index = self .graph - .add_node(NodeData::new_unresolved(to_path.clone())); + .add_node(NodeData::new_unresolved(target_path.clone())); add_to_to_hash = true; } } else { - to_index = to.unwrap().clone(); + target_index = target.unwrap().clone(); } if add_from_to_hash { - self.node_hash.insert(from_path.clone(), from_index); + self.node_hash.insert(source_path.clone(), source_index); } if add_to_to_hash { - self.node_hash.insert(from_path.clone(), to_index); + self.node_hash.insert(source_path.clone(), target_index); } self.int_add_edge( - from_index, - to_index, - EdgeData::new(edge_type.clone(), false, 0), + source_index, + target_index, + EdgeData::new(edge_type.clone(), edge_source.clone(), false, 0), &mut None, ); } diff --git a/wasm/src/graph_construction.rs b/wasm/src/graph_construction.rs index 2d00af83..59a005a3 100644 --- a/wasm/src/graph_construction.rs +++ b/wasm/src/graph_construction.rs @@ -56,21 +56,29 @@ impl GraphConstructionNodeData { #[derive(Clone, Debug)] pub struct GraphConstructionEdgeData { #[wasm_bindgen(skip)] - pub from: String, + pub source: String, #[wasm_bindgen(skip)] - pub to: String, + pub target: String, #[wasm_bindgen(skip)] pub edge_type: String, + #[wasm_bindgen(skip)] + pub edge_source: String, } #[wasm_bindgen] impl GraphConstructionEdgeData { #[wasm_bindgen(constructor)] - pub fn new(from: String, to: String, edge_type: String) -> GraphConstructionEdgeData { + pub fn new( + source: String, + target: String, + edge_type: String, + edge_source: String, + ) -> GraphConstructionEdgeData { GraphConstructionEdgeData { - from, - to, + source, + target, edge_type, + edge_source, } } diff --git a/wasm/src/graph_rules.rs b/wasm/src/graph_rules.rs index 727ec5aa..387f9d87 100644 --- a/wasm/src/graph_rules.rs +++ b/wasm/src/graph_rules.rs @@ -3,6 +3,8 @@ use wasm_bindgen::prelude::*; #[wasm_bindgen] #[derive(Clone)] pub struct TransitiveGraphRule { + #[wasm_bindgen(skip)] + pub name: String, // the path by edge type #[wasm_bindgen(skip)] pub path: Vec, @@ -22,6 +24,7 @@ pub struct TransitiveGraphRule { impl TransitiveGraphRule { #[wasm_bindgen(constructor)] pub fn new( + name: String, path: Vec, edge_type: String, rounds: u8, @@ -29,6 +32,7 @@ impl TransitiveGraphRule { close_reversed: bool, ) -> TransitiveGraphRule { TransitiveGraphRule { + name, path, edge_type, rounds, diff --git a/wasm/src/graph_update.rs b/wasm/src/graph_update.rs index f3b9dac6..4986dc48 100644 --- a/wasm/src/graph_update.rs +++ b/wasm/src/graph_update.rs @@ -110,19 +110,21 @@ impl GraphUpdate for RenameNoteGraphUpdate { #[wasm_bindgen] #[derive(Clone)] pub struct AddEdgeGraphUpdate { - from: String, - to: String, + source: String, + target: String, edge_type: String, + edge_source: String, } #[wasm_bindgen] impl AddEdgeGraphUpdate { #[wasm_bindgen(constructor)] - pub fn new(from: String, to: String, edge_type: String) -> Self { + pub fn new(source: String, target: String, edge_type: String, edge_source: String) -> Self { Self { - from, - to, + source, + target, edge_type, + edge_source, } } @@ -133,7 +135,12 @@ impl AddEdgeGraphUpdate { impl GraphUpdate for AddEdgeGraphUpdate { fn apply(&self, graph: &mut NoteGraph) -> Result<()> { - Ok(graph.int_safe_add_edge(&self.from, &self.to, &self.edge_type)) + Ok(graph.int_safe_add_edge( + &self.source, + &self.target, + &self.edge_type, + &self.edge_source, + )) } } diff --git a/wasm/tests/common/mod.rs b/wasm/tests/common/mod.rs index 082653e4..737ee0f2 100644 --- a/wasm/tests/common/mod.rs +++ b/wasm/tests/common/mod.rs @@ -22,6 +22,7 @@ pub fn tdata_generate_tree( "root".to_string(), i.to_string(), "down".to_string(), + "typed-link".to_string(), )); } @@ -37,6 +38,7 @@ pub fn tdata_generate_tree( current.clone(), next.clone(), "down".to_string(), + "typed-link".to_string(), )); stack.push(next); } diff --git a/wasm/tests/graph.rs b/wasm/tests/graph.rs index 24be74b9..0afe6e2d 100644 --- a/wasm/tests/graph.rs +++ b/wasm/tests/graph.rs @@ -8,7 +8,7 @@ use breadcrumbs_graph_wasm::{ graph_update::{ AddEdgeGraphUpdate, AddNoteGraphUpdate, BatchGraphUpdate, RemoveNoteGraphUpdate, }, - utils::graph_eq, + utils::{graph_eq, log}, }; use wasm_bindgen_test::*; mod common; @@ -31,8 +31,22 @@ fn test_implied_edge_rules_reverse_direction() { let mut graph = NoteGraph::new(); graph.set_transitive_rules(vec![ - TransitiveGraphRule::new(vec!["down".to_string()], "up".to_string(), 5, false, true), - TransitiveGraphRule::new(vec!["up".to_string()], "down".to_string(), 5, false, true), + TransitiveGraphRule::new( + "".to_string(), + vec!["down".to_string()], + "up".to_string(), + 5, + false, + true, + ), + TransitiveGraphRule::new( + "".to_string(), + vec!["up".to_string()], + "down".to_string(), + 5, + false, + true, + ), ]); graph.build_graph(data.0, data.1); @@ -57,9 +71,24 @@ fn test_implied_edge_rules_sibling() { let mut graph = NoteGraph::new(); graph.set_transitive_rules(vec![ - TransitiveGraphRule::new(vec!["down".to_string()], "up".to_string(), 5, false, true), - TransitiveGraphRule::new(vec!["up".to_string()], "down".to_string(), 5, false, true), TransitiveGraphRule::new( + "".to_string(), + vec!["down".to_string()], + "up".to_string(), + 5, + false, + true, + ), + TransitiveGraphRule::new( + "".to_string(), + vec!["up".to_string()], + "down".to_string(), + 5, + false, + true, + ), + TransitiveGraphRule::new( + "".to_string(), vec!["up".to_string(), "down".to_string()], "same".to_string(), 5, @@ -95,9 +124,24 @@ fn test_implied_edge_rules_sibling_can_loop() { let mut graph = NoteGraph::new(); graph.set_transitive_rules(vec![ - TransitiveGraphRule::new(vec!["down".to_string()], "up".to_string(), 5, false, true), - TransitiveGraphRule::new(vec!["up".to_string()], "down".to_string(), 5, false, true), TransitiveGraphRule::new( + "".to_string(), + vec!["down".to_string()], + "up".to_string(), + 5, + false, + true, + ), + TransitiveGraphRule::new( + "".to_string(), + vec!["up".to_string()], + "down".to_string(), + 5, + false, + true, + ), + TransitiveGraphRule::new( + "".to_string(), vec!["up".to_string(), "down".to_string()], "same".to_string(), 5, @@ -177,10 +221,20 @@ fn test_remove_add_update() { false, )) .add_to_batch(&mut batch); - AddEdgeGraphUpdate::new("0".to_string(), "00".to_string(), "down".to_string()) - .add_to_batch(&mut batch); - AddEdgeGraphUpdate::new("0".to_string(), "01".to_string(), "down".to_string()) - .add_to_batch(&mut batch); + AddEdgeGraphUpdate::new( + "0".to_string(), + "00".to_string(), + "down".to_string(), + "typed-link".to_string(), + ) + .add_to_batch(&mut batch); + AddEdgeGraphUpdate::new( + "0".to_string(), + "01".to_string(), + "down".to_string(), + "typed-link".to_string(), + ) + .add_to_batch(&mut batch); graph_1.apply_update(batch).unwrap(); @@ -209,10 +263,20 @@ fn test_remove_add_separate_updates() { false, )) .add_to_batch(&mut batch_2); - AddEdgeGraphUpdate::new("0".to_string(), "00".to_string(), "down".to_string()) - .add_to_batch(&mut batch_2); - AddEdgeGraphUpdate::new("0".to_string(), "01".to_string(), "down".to_string()) - .add_to_batch(&mut batch_2); + AddEdgeGraphUpdate::new( + "0".to_string(), + "00".to_string(), + "down".to_string(), + "typed-link".to_string(), + ) + .add_to_batch(&mut batch_2); + AddEdgeGraphUpdate::new( + "0".to_string(), + "01".to_string(), + "down".to_string(), + "typed-link".to_string(), + ) + .add_to_batch(&mut batch_2); graph_1.apply_update(batch_2).unwrap(); assert!(graph_eq(&graph_1.graph, &graph_2.graph)); From f18ee4fc6a1ff7f39e96f0dfec7b999e2ac1ccba Mon Sep 17 00:00:00 2001 From: Moritz Jung Date: Mon, 29 Apr 2024 14:44:18 +0200 Subject: [PATCH 06/65] fix list index --- src/commands/list_index/index.ts | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/src/commands/list_index/index.ts b/src/commands/list_index/index.ts index c67261db..308cfc4c 100644 --- a/src/commands/list_index/index.ts +++ b/src/commands/list_index/index.ts @@ -54,23 +54,16 @@ export namespace ListIndex { let index = ""; const real_indent = options.indent.replace(/\\t/g, "\t"); - tree.forEach(({ children, depth, edge, node }) => { - const display = stringify_node(node, { + tree.forEach(({ children, depth, edge }) => { + const display = stringify_node(edge.target, { show_node_options: options.show_node_options, }); - const link = Links.ify(node.path, display, { + const link = Links.ify(edge.target.path, display, { link_kind: options.link_kind, }); - // TODO: show_attributes - // const attr = options.show_attributes.length - // ? ` (${url_search_params( - // untyped_pick(edge.attr, options.show_attributes), - // { trim_lone_param: true }, - // )})` - // : ""; - const attr = ""; + const attr = edge.get_attribute_label(options.show_attributes); index += real_indent.repeat(depth) + `- ${link}${attr}\n`; From 583ff3698a9001cddc448bd3b3cc3594ebd1c171 Mon Sep 17 00:00:00 2001 From: Moritz Jung Date: Mon, 29 Apr 2024 14:45:15 +0200 Subject: [PATCH 07/65] fix edge link class --- src/components/EdgeLink.svelte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/EdgeLink.svelte b/src/components/EdgeLink.svelte index eabee132..c30a4fd0 100644 --- a/src/components/EdgeLink.svelte +++ b/src/components/EdgeLink.svelte @@ -30,5 +30,5 @@ resolved={target_node.resolved} cls="{cls} BC-edge {!edge.implied ? 'BC-edge-explicit' - : `BC-edge-implied BC-edge-implied-${ 'transitive' /*edge.attr.implied_kind */}`}" + : `BC-edge-implied BC-edge-implied-${edge.edge_source}`}" /> From e12f0e179e1654aa0b1e86dfbc52a5f8ef88125d Mon Sep 17 00:00:00 2001 From: SkepticMystic Date: Mon, 29 Apr 2024 17:08:02 +0200 Subject: [PATCH 08/65] Builders accumulate GraphConstructionData immediately, instead of intermediate representation --- src/graph/builders/explicit/dataview_note.ts | 22 ++--- src/graph/builders/explicit/date_note.ts | 42 +++++---- src/graph/builders/explicit/dendron_note.ts | 30 ++++--- src/graph/builders/explicit/folder_note.ts | 28 +++--- .../builders/explicit/johnny_decimal_note.ts | 42 +++++---- src/graph/builders/explicit/list_note.ts | 88 +++++++++++-------- src/graph/builders/explicit/regex_note.ts | 31 +++---- src/graph/builders/explicit/tag_note.ts | 31 +++---- src/graph/builders/explicit/typed_link.ts | 57 +++++++----- src/graph/builders/index.ts | 53 +++++------ src/interfaces/graph.ts | 16 ++-- 11 files changed, 252 insertions(+), 188 deletions(-) diff --git a/src/graph/builders/explicit/dataview_note.ts b/src/graph/builders/explicit/dataview_note.ts index 6049433a..e39e018a 100644 --- a/src/graph/builders/explicit/dataview_note.ts +++ b/src/graph/builders/explicit/dataview_note.ts @@ -9,6 +9,7 @@ import type { import { log } from "src/logger"; import type BreadcrumbsPlugin from "src/main"; import { fail, graph_build_fail, succ } from "src/utils/result"; +import { GraphConstructionEdgeData } from "wasm/pkg/breadcrumbs_graph_wasm"; const get_dataview_note_info = ( plugin: BreadcrumbsPlugin, @@ -58,7 +59,7 @@ export const _add_explicit_edges_dataview_note: ExplicitEdgeBuilder = ( plugin, all_files, ) => { - const results: EdgeBuilderResults = { nodes: [], edges: [], errors: [] } + const results: EdgeBuilderResults = { nodes: [], edges: [], errors: [] }; all_files.obsidian?.forEach( ({ file: dataview_note_file, cache: dataview_note_cache }) => { @@ -90,7 +91,8 @@ export const _add_explicit_edges_dataview_note: ExplicitEdgeBuilder = ( dataview_note_path, ); if (!dataview_note_info.ok) { - if (dataview_note_info.error) results.errors.push(dataview_note_info.error); + if (dataview_note_info.error) + results.errors.push(dataview_note_info.error); return; } const { field, query } = dataview_note_info.data; @@ -114,18 +116,16 @@ export const _add_explicit_edges_dataview_note: ExplicitEdgeBuilder = ( pages.forEach((page) => { // NOTE: I _believe_ we don't need to even safe_add_node, since dv will only return resolved notes - results.edges.push({ - source_id: dataview_note_page.file.path, - target_id: page.file.path, - attr: { + results.edges.push( + new GraphConstructionEdgeData( + dataview_note_page.file.path, + page.file.path, field, - explicit: true, - source: "dataview_note", - }, - } + "dataview_note", + ), ); }); }); - return results + return results; }; diff --git a/src/graph/builders/explicit/date_note.ts b/src/graph/builders/explicit/date_note.ts index 6a6d6538..62eafee7 100644 --- a/src/graph/builders/explicit/date_note.ts +++ b/src/graph/builders/explicit/date_note.ts @@ -4,6 +4,10 @@ import type { ExplicitEdgeBuilder, } from "src/interfaces/graph"; import { Paths } from "src/utils/paths"; +import { + GraphConstructionEdgeData, + GraphConstructionNodeData, +} from "wasm/pkg/breadcrumbs_graph_wasm"; // TODO: Option to point up to month, (and for month to point up to year?) @@ -11,11 +15,12 @@ export const _add_explicit_edges_date_note: ExplicitEdgeBuilder = ( plugin, all_files, ) => { - const results: EdgeBuilderResults = { nodes: [], edges: [], errors: [] } + const results: EdgeBuilderResults = { nodes: [], edges: [], errors: [] }; const date_note_settings = plugin.settings.explicit_edge_sources.date_note; - if (!date_note_settings.enabled) { return results } - else if ( + if (!date_note_settings.enabled) { + return results; + } else if ( !plugin.settings.edge_fields.find( (field) => field.label === date_note_settings.default_field, ) @@ -26,7 +31,7 @@ export const _add_explicit_edges_date_note: ExplicitEdgeBuilder = ( message: `The default Date Note field "${date_note_settings.default_field}" is not a valid Breadcrumbs Edge field`, }); - return results + return results; } const date_notes: { @@ -94,19 +99,26 @@ export const _add_explicit_edges_date_note: ExplicitEdgeBuilder = ( // NOTE: We have a full path, so we can go straight to the file without the given source_path const target_file = plugin.app.vault.getFileByPath(target_id); if (!target_file) { - results.nodes.push({ id: target_id, attr: { resolved: false } }); + results.nodes.push( + new GraphConstructionNodeData( + target_id, + [], + false, + false, + false, + ), + ); } - results.edges.push({ - target_id, - source_id: date_note.path, - attr: { - explicit: true, - source: "date_note", - field: date_note_settings.default_field, - } - }); + results.edges.push( + new GraphConstructionEdgeData( + target_id, + date_note.path, + date_note_settings.default_field, + "date_note", + ), + ); }); - return results + return results; }; diff --git a/src/graph/builders/explicit/dendron_note.ts b/src/graph/builders/explicit/dendron_note.ts index 5048c3e8..449e2db2 100644 --- a/src/graph/builders/explicit/dendron_note.ts +++ b/src/graph/builders/explicit/dendron_note.ts @@ -1,13 +1,16 @@ import { META_ALIAS } from "src/const/metadata_fields"; // import type { BCGraph } from "src/graph/MyMultiGraph"; import type { - BreadcrumbsError, EdgeBuilderResults, ExplicitEdgeBuilder, } from "src/interfaces/graph"; import type BreadcrumbsPlugin from "src/main"; import { Paths } from "src/utils/paths"; import { fail, graph_build_fail, succ } from "src/utils/result"; +import { + GraphConstructionEdgeData, + GraphConstructionNodeData, +} from "wasm/pkg/breadcrumbs_graph_wasm"; const get_dendron_note_info = ( plugin: BreadcrumbsPlugin, @@ -86,7 +89,9 @@ const handle_dendron_note = ( const target_file = plugin.app.vault.getFileByPath(target_id); if (!target_file) { - results.nodes.push({ id: target_id, attr: { resolved: false } }); + results.nodes.push( + new GraphConstructionNodeData(target_id, [], false, false, false), + ); // If !target_file, we can recursively call handle_dendron_note // To add the unresolved edges along the way @@ -102,25 +107,24 @@ const handle_dendron_note = ( ); } - results.edges.push({ - source_id, - target_id, - attr: { + results.edges.push( + new GraphConstructionEdgeData( + source_id, + target_id, field, - explicit: true, - source: "dendron_note", - } - }); + "dendron_note", + ), + ); }; export const _add_explicit_edges_dendron_note: ExplicitEdgeBuilder = ( plugin, all_files, ) => { - const results: EdgeBuilderResults = { nodes: [], edges: [], errors: [] } + const results: EdgeBuilderResults = { nodes: [], edges: [], errors: [] }; if (!plugin.settings.explicit_edge_sources.dendron_note.enabled) { - return results + return results; } all_files.obsidian?.forEach(({ file, cache }) => { @@ -131,5 +135,5 @@ export const _add_explicit_edges_dendron_note: ExplicitEdgeBuilder = ( handle_dendron_note(plugin, results, page.file.path, page); }); - return results + return results; }; diff --git a/src/graph/builders/explicit/folder_note.ts b/src/graph/builders/explicit/folder_note.ts index 6ab4ecfc..4f450be4 100644 --- a/src/graph/builders/explicit/folder_note.ts +++ b/src/graph/builders/explicit/folder_note.ts @@ -7,6 +7,7 @@ import type { import type { Result } from "src/interfaces/result"; import type BreadcrumbsPlugin from "src/main"; import { fail, graph_build_fail, succ } from "src/utils/result"; +import { GraphConstructionEdgeData } from "wasm/pkg/breadcrumbs_graph_wasm"; type FolderNoteData = { field: string; @@ -75,7 +76,7 @@ export const _add_explicit_edges_folder_note: ExplicitEdgeBuilder = async ( plugin, all_files, ) => { - const results: EdgeBuilderResults = { nodes: [], edges: [], errors: [] } + const results: EdgeBuilderResults = { nodes: [], edges: [], errors: [] }; const folder_notes: { file: { path: string; folder: string }; @@ -92,7 +93,8 @@ export const _add_explicit_edges_folder_note: ExplicitEdgeBuilder = async ( folder_note_file.path, ); if (!folder_note_info.ok) { - if (folder_note_info.error) results.errors.push(folder_note_info.error); + if (folder_note_info.error) + results.errors.push(folder_note_info.error); return; } @@ -113,7 +115,8 @@ export const _add_explicit_edges_folder_note: ExplicitEdgeBuilder = async ( folder_note_page.file.path, ); if (!folder_note_info.ok) { - if (folder_note_info.error) results.errors.push(folder_note_info.error); + if (folder_note_info.error) + results.errors.push(folder_note_info.error); return; } @@ -140,20 +143,19 @@ export const _add_explicit_edges_folder_note: ExplicitEdgeBuilder = async ( } // We know path is resolved - results.edges.push({ - target_id: target_path, - source_id: folder_note.path, - attr: { - explicit: true, - field: data.field, - source: "folder_note", - }, - }); + results.edges.push( + new GraphConstructionEdgeData( + target_path, + folder_note.path, + data.field, + "folder_note", + ), + ); }, data.recurse, ), ), ); - return results + return results; }; diff --git a/src/graph/builders/explicit/johnny_decimal_note.ts b/src/graph/builders/explicit/johnny_decimal_note.ts index 77c11710..b26de443 100644 --- a/src/graph/builders/explicit/johnny_decimal_note.ts +++ b/src/graph/builders/explicit/johnny_decimal_note.ts @@ -9,6 +9,10 @@ import type BreadcrumbsPlugin from "src/main"; import { Paths } from "src/utils/paths"; import { fail, graph_build_fail, succ } from "src/utils/result"; import { ensure_not_ends_with } from "src/utils/strings"; +import { + GraphConstructionEdgeData, + GraphConstructionNodeData, +} from "wasm/pkg/breadcrumbs_graph_wasm"; const get_johnny_decimal_note_info = ( plugin: BreadcrumbsPlugin, @@ -85,20 +89,27 @@ const handle_johnny_decimal_note = ( // NOTE: I don't think this can ever happen... if target_note, then target_file must exist if (!target_file) { - results.nodes.push({ id: target_note.path, attr: { resolved: false } }); + results.nodes.push( + new GraphConstructionNodeData( + target_note.path, + [], + false, + false, + false, + ), + ); } const { field } = johnny_decimal_note_info.data; - results.edges.push({ - source_id: source_note.path, - target_id: target_note.path, - attr: { + results.edges.push( + new GraphConstructionEdgeData( + source_note.path, + target_note.path, field, - explicit: true, - source: "johnny_decimal_note", - } - }); + "johnny_decimal_note", + ), + ); }; type JohnnyDecimalNote = { @@ -112,10 +123,10 @@ export const _add_explicit_edges_johnny_decimal_note: ExplicitEdgeBuilder = ( plugin, all_files, ) => { - const results: EdgeBuilderResults = { nodes: [], edges: [], errors: [] } + const results: EdgeBuilderResults = { nodes: [], edges: [], errors: [] }; if (!plugin.settings.explicit_edge_sources.johnny_decimal_note.enabled) { - return results + return results; } const { delimiter } = @@ -156,13 +167,8 @@ export const _add_explicit_edges_johnny_decimal_note: ExplicitEdgeBuilder = ( }); johnny_decimal_notes.forEach((note) => { - handle_johnny_decimal_note( - plugin, - results, - note, - johnny_decimal_notes, - ); + handle_johnny_decimal_note(plugin, results, note, johnny_decimal_notes); }); - return results + return results; }; diff --git a/src/graph/builders/explicit/list_note.ts b/src/graph/builders/explicit/list_note.ts index bcd39f4f..ad79415c 100644 --- a/src/graph/builders/explicit/list_note.ts +++ b/src/graph/builders/explicit/list_note.ts @@ -9,6 +9,10 @@ import type { import type BreadcrumbsPlugin from "src/main"; import { resolve_relative_target_path } from "src/utils/obsidian"; import { fail, graph_build_fail, succ } from "src/utils/result"; +import { + GraphConstructionEdgeData, + GraphConstructionNodeData, +} from "wasm/pkg/breadcrumbs_graph_wasm"; const get_list_note_info = ( plugin: BreadcrumbsPlugin, @@ -167,26 +171,27 @@ const handle_neighbour_list_item = ({ ); if (!file) { - results.nodes.push({ id: target_id, attr: { resolved: false } }); + results.nodes.push( + new GraphConstructionNodeData(target_id, [], false, false, false), + ); } // NOTE: Currently no support for field overrides for neighbour-fields - results.edges.push({ - source_id, - target_id, - attr: { - explicit: true, - source: "list_note", - field: list_note_info.data.neighbour_field, - } - }); + results.edges.push( + new GraphConstructionEdgeData( + source_id, + target_id, + list_note_info.data.neighbour_field, + "list_note", + ), + ); }; export const _add_explicit_edges_list_note: ExplicitEdgeBuilder = ( plugin, all_files, ) => { - const results: EdgeBuilderResults = { nodes: [], edges: [], errors: [] } + const results: EdgeBuilderResults = { nodes: [], edges: [], errors: [] }; all_files.obsidian?.forEach( ({ file: list_note_file, cache: list_note_cache }) => { @@ -198,7 +203,8 @@ export const _add_explicit_edges_list_note: ExplicitEdgeBuilder = ( list_note_file.path, ); if (!list_note_info.ok) { - if (list_note_info.error) results.errors.push(list_note_info.error); + if (list_note_info.error) + results.errors.push(list_note_info.error); return; } else { new Notice( @@ -242,7 +248,15 @@ export const _add_explicit_edges_list_note: ExplicitEdgeBuilder = ( // The node wouldn't have been added in the simple_loop if it wasn't resolved. if (!source_file) { - results.nodes.push({ id: source_path, attr: { resolved: false } }); + results.nodes.push( + new GraphConstructionNodeData( + source_path, + [], + false, + false, + false, + ), + ); } // Then, add the edge from the list_note itself, to the top-level list_items (if it's not excluded) @@ -266,17 +280,12 @@ export const _add_explicit_edges_list_note: ExplicitEdgeBuilder = ( } results.edges.push( - { - source_id: list_note_page.file.path, - target_id: source_path, - attr: { - explicit: true, - source: "list_note", - field: - source_override_field.data?.field ?? - list_note_info.data.field, - }, - } + new GraphConstructionEdgeData( + list_note_page.file.path, + source_path, + list_note_info.data.field, + "list_note", + ), ); } @@ -322,24 +331,29 @@ export const _add_explicit_edges_list_note: ExplicitEdgeBuilder = ( // But then I'd need to break up the iteration to first gather all sources, then handle the targets // This way we can guarentee the target exists if (!target_file) { - results.nodes.push({ id: target_path, attr: { resolved: false } }); + results.nodes.push( + new GraphConstructionNodeData( + target_path, + [], + false, + false, + false, + ), + ); } - results.edges.push({ - source_id: source_path, - target_id: target_path, - attr: { - explicit: true, - source: "list_note", - field: - target_override_field.data?.field ?? - list_note_info.data.field, - } - }); + results.edges.push( + new GraphConstructionEdgeData( + source_path, + target_path, + list_note_info.data.field, + "list_note", + ), + ); }); }, ); }); - return results + return results; }; diff --git a/src/graph/builders/explicit/regex_note.ts b/src/graph/builders/explicit/regex_note.ts index 4c46013d..208964cf 100644 --- a/src/graph/builders/explicit/regex_note.ts +++ b/src/graph/builders/explicit/regex_note.ts @@ -6,6 +6,7 @@ import type { import { log } from "src/logger"; import type BreadcrumbsPlugin from "src/main"; import { fail, graph_build_fail, succ } from "src/utils/result"; +import { GraphConstructionEdgeData } from "wasm/pkg/breadcrumbs_graph_wasm"; const get_regex_note_info = ( plugin: BreadcrumbsPlugin, @@ -77,7 +78,7 @@ export const _add_explicit_edges_regex_note: ExplicitEdgeBuilder = ( plugin, all_files, ) => { - const results: EdgeBuilderResults = { nodes: [], edges: [], errors: [] } + const results: EdgeBuilderResults = { nodes: [], edges: [], errors: [] }; const regex_note_files: { path: string; @@ -111,27 +112,27 @@ export const _add_explicit_edges_regex_note: ExplicitEdgeBuilder = ( }); // Return early before bringing all nodes into memory - if (!regex_note_files) return results + if (!regex_note_files) return results; - const nodes = all_files.obsidian?.map(note => note.file.path) - ?? all_files.dataview?.map(note => note.file.path) - ?? [] // Won't happen, but makes TS happy + const nodes = + all_files.obsidian?.map((note) => note.file.path) ?? + all_files.dataview?.map((note) => note.file.path) ?? + []; // Won't happen, but makes TS happy regex_note_files.forEach((regex_note) => { nodes .filter((node) => regex_note.info.regex.test(node)) .forEach((target_id) => { - results.edges.push({ - target_id, - source_id: regex_note.path, - attr: { - explicit: true, - source: "regex_note", - field: regex_note.info.field, - } - }); + results.edges.push( + new GraphConstructionEdgeData( + target_id, + regex_note.path, + regex_note.info.field, + "regex_note", + ), + ); }); }); - return results + return results; }; diff --git a/src/graph/builders/explicit/tag_note.ts b/src/graph/builders/explicit/tag_note.ts index 02c1ff85..0ff73bb4 100644 --- a/src/graph/builders/explicit/tag_note.ts +++ b/src/graph/builders/explicit/tag_note.ts @@ -7,6 +7,7 @@ import { log } from "src/logger"; import type BreadcrumbsPlugin from "src/main"; import { fail, graph_build_fail, succ } from "src/utils/result"; import { ensure_starts_with } from "src/utils/strings"; +import { GraphConstructionEdgeData } from "wasm/pkg/breadcrumbs_graph_wasm"; const get_tag_note_info = ( plugin: BreadcrumbsPlugin, @@ -66,7 +67,7 @@ export const _add_explicit_edges_tag_note: ExplicitEdgeBuilder = ( plugin, all_files, ) => { - const results: EdgeBuilderResults = { nodes: [], edges: [], errors: [], } + const results: EdgeBuilderResults = { nodes: [], edges: [], errors: [] }; // More efficient than quadratic looping over all_files, // We gather the tag_notes, and the tags the each note has in one go @@ -101,7 +102,8 @@ export const _add_explicit_edges_tag_note: ExplicitEdgeBuilder = ( tag_note_file.path, ); if (!tag_note_info.ok) { - if (tag_note_info.error) results.errors.push(tag_note_info.error); + if (tag_note_info.error) + results.errors.push(tag_note_info.error); return; } @@ -153,25 +155,24 @@ export const _add_explicit_edges_tag_note: ExplicitEdgeBuilder = ( const target_paths = tag_note.exact ? tag_paths_map.get(tag_note.tag) : all_tags - .filter((tag) => tag.startsWith(tag_note.tag)) - // We know that the tag_note.tag is in the tag_paths_map, so this is safe - .flatMap((tag) => tag_paths_map.get(tag)!); + .filter((tag) => tag.startsWith(tag_note.tag)) + // We know that the tag_note.tag is in the tag_paths_map, so this is safe + .flatMap((tag) => tag_paths_map.get(tag)!); // Adding these edges is comparatively simple. // We know the target_path is resolved, since it only gets added to the map // if it's a resolved note with a tag in it target_paths?.forEach((target_path) => { - results.edges.push({ - source_id: tag_note.source_path, - target_id: target_path, - attr: { - explicit: true, - source: "tag_note", - field: tag_note.field, - } - }) + results.edges.push( + new GraphConstructionEdgeData( + tag_note.source_path, + target_path, + tag_note.field, + "tag_note", + ), + ); }); }); - return results + return results; }; diff --git a/src/graph/builders/explicit/typed_link.ts b/src/graph/builders/explicit/typed_link.ts index f103c922..6d570ce2 100644 --- a/src/graph/builders/explicit/typed_link.ts +++ b/src/graph/builders/explicit/typed_link.ts @@ -4,6 +4,10 @@ import type { } from "src/interfaces/graph"; import { ensure_is_array } from "src/utils/arrays"; import { resolve_relative_target_path } from "src/utils/obsidian"; +import { + GraphConstructionEdgeData, + GraphConstructionNodeData, +} from "wasm/pkg/breadcrumbs_graph_wasm"; const MARKDOWN_LINK_REGEX = /\[(.+?)\]\((.+?)\)/; @@ -11,7 +15,7 @@ export const _add_explicit_edges_typed_link: ExplicitEdgeBuilder = ( plugin, all_files, ) => { - const results: EdgeBuilderResults = { nodes: [], edges: [], errors: [] } + const results: EdgeBuilderResults = { nodes: [], edges: [], errors: [] }; const field_labels = new Set( plugin.settings.edge_fields.map((f) => f.label), @@ -36,18 +40,25 @@ export const _add_explicit_edges_typed_link: ExplicitEdgeBuilder = ( if (!target_file) { // Unresolved nodes don't have aliases - results.nodes.push({ id: target_id, attr: { resolved: false } }); + results.nodes.push( + new GraphConstructionNodeData( + target_id, + [], + false, + false, + false, + ), + ); } - results.edges.push({ - target_id, - source_id: source_file.path, - attr: { + results.edges.push( + new GraphConstructionEdgeData( + target_id, + source_file.path, field, - explicit: true, - source: "typed_link", - } - }); + "typed_link", + ), + ); }); }, ); @@ -113,23 +124,29 @@ export const _add_explicit_edges_typed_link: ExplicitEdgeBuilder = ( if (!target_file) { // It's an unresolved link, so we add a node for it // But still do it safely, as a previous file may point to the same unresolved node - results.nodes.push({ id: target_path, attr: { resolved: false } }); + results.nodes.push( + new GraphConstructionNodeData( + target_path, + [], + false, + false, + false, + ), + ); } // If the file exists, we should have already added a node for it in the simple loop over all markdown files - results.edges.push({ - source_id: source_file.path, - target_id: target_path, - attr: { + results.edges.push( + new GraphConstructionEdgeData( + source_file.path, + target_path, field, - explicit: true, - source: "typed_link", - } - }, + "typed_link", + ), ); }); }); }); - return results + return results; }; diff --git a/src/graph/builders/index.ts b/src/graph/builders/index.ts index 9e5ed996..f01bc6c3 100644 --- a/src/graph/builders/index.ts +++ b/src/graph/builders/index.ts @@ -8,10 +8,13 @@ import { type BCNode, type BCNodeAttributes } from "../MyMultiGraph"; import { add_explicit_edges } from "./explicit"; import { get_all_files, type AllFiles } from "./explicit/files"; // import { _add_implied_edges_transitive } from "./implied/transitive"; -import { GraphConstructionEdgeData, GraphConstructionNodeData } from "wasm/pkg/breadcrumbs_graph_wasm"; +import { + GraphConstructionEdgeData, + GraphConstructionNodeData, +} from "wasm/pkg/breadcrumbs_graph_wasm"; const get_initial_nodes = (all_files: AllFiles) => { - const nodes: GraphConstructionNodeData[] = [] + const nodes: GraphConstructionNodeData[] = []; if (all_files.obsidian) { all_files.obsidian.forEach(({ file, cache }) => { @@ -31,7 +34,15 @@ const get_initial_nodes = (all_files: AllFiles) => { attr.ignore_out_edges = true; } - nodes.push(new GraphConstructionNodeData(file.path, attr.aliases ?? [], true, attr.ignore_in_edges ?? false, attr.ignore_out_edges ?? false)) + nodes.push( + new GraphConstructionNodeData( + file.path, + attr.aliases ?? [], + true, + attr.ignore_in_edges ?? false, + attr.ignore_out_edges ?? false, + ), + ); }); } else { all_files.dataview.forEach((page) => { @@ -51,24 +62,21 @@ const get_initial_nodes = (all_files: AllFiles) => { attr.ignore_out_edges = true; } - nodes.push(new GraphConstructionNodeData(page.file.path, attr.aliases ?? [], true, attr.ignore_in_edges ?? false, attr.ignore_out_edges ?? false)) + nodes.push( + new GraphConstructionNodeData( + page.file.path, + attr.aliases ?? [], + true, + attr.ignore_in_edges ?? false, + attr.ignore_out_edges ?? false, + ), + ); }); } - return nodes + return nodes; }; -// TODO: these functions should not be needed. The explicit edge builders should return the WASM data directly -const construction_data_from_node = (node: BCNode): GraphConstructionNodeData => { - const attr = node.attr; - return new GraphConstructionNodeData(node.id, attr.aliases ?? [], attr.resolved, attr.ignore_in_edges ?? false, attr.ignore_out_edges ?? false); -} - -// TODO: these functions should not be needed. The explicit edge builders should return the WASM data directly -const construction_data_from_edge = (edge: EdgeToAdd): GraphConstructionEdgeData => { - return new GraphConstructionEdgeData(edge.source_id, edge.target_id, edge.attr.field, edge.attr.explicit ? edge.attr.source : edge.attr.implied_kind); -} - export const rebuild_graph = async (plugin: BreadcrumbsPlugin) => { const timer = new Timer(); @@ -84,25 +92,20 @@ export const rebuild_graph = async (plugin: BreadcrumbsPlugin) => { // Explicit edges const explicit_edge_results = await Promise.all( EXPLICIT_EDGE_SOURCES.map(async (source) => { - const results = await add_explicit_edges[source]( - plugin, - all_files, - ); + const results = await add_explicit_edges[source](plugin, all_files); return { source, results }; }), ); const edges: GraphConstructionEdgeData[] = []; - - for (const { source, results } of explicit_edge_results) { - nodes.push(...results.nodes.map(construction_data_from_node)); - edges.push(...results.edges.map(construction_data_from_edge)); + for (const { results } of explicit_edge_results) { + nodes.push(...results.nodes); + edges.push(...results.edges); } log.debug(timer.elapsedMessage("Collecting edges and nodes")); - // TODO plugin.graph.build_graph(nodes, edges); // log.debug(timer.elapsedMessage("Adding initial edges")); diff --git a/src/interfaces/graph.ts b/src/interfaces/graph.ts index d46980e1..2ffb972a 100644 --- a/src/interfaces/graph.ts +++ b/src/interfaces/graph.ts @@ -2,15 +2,19 @@ import type { BCEdgeAttributes, BCNode } from "src/graph/MyMultiGraph"; import type { AllFiles } from "src/graph/builders/explicit/files"; import type BreadcrumbsPlugin from "src/main"; import type { MaybePromise } from "."; +import { + GraphConstructionEdgeData, + GraphConstructionNodeData, +} from "wasm/pkg/breadcrumbs_graph_wasm"; export type BreadcrumbsError = { // TODO: Differentiate between invalid edge-field and invalid metadata-field values // BUT: Some errors might be a metadata field with an invalid edge-field value code: - | "deprecated_field" - | "invalid_field_value" - | "invalid_setting_value" - | "invalid_yaml"; + | "deprecated_field" + | "invalid_field_value" + | "invalid_setting_value" + | "invalid_yaml"; message: string; path: string; }; @@ -23,8 +27,8 @@ export type EdgeToAdd = { }; export type EdgeBuilderResults = { - nodes: BCNode[] - edges: EdgeToAdd[]; + nodes: GraphConstructionNodeData[]; + edges: GraphConstructionEdgeData[]; errors: BreadcrumbsError[]; }; From 6eb258e737d7e2c78c7d2d425a599f85ef8a6539 Mon Sep 17 00:00:00 2001 From: Moritz Jung Date: Mon, 29 Apr 2024 17:21:57 +0200 Subject: [PATCH 09/65] first half of mermaid options --- wasm/Cargo.toml | 2 ++ wasm/src/graph.rs | 38 ++++++++++++++++++++++++++++++++++---- 2 files changed, 36 insertions(+), 4 deletions(-) diff --git a/wasm/Cargo.toml b/wasm/Cargo.toml index 9ecfccd6..82732720 100644 --- a/wasm/Cargo.toml +++ b/wasm/Cargo.toml @@ -22,6 +22,8 @@ petgraph = "0.6.4" web-time = "1.1.0" js-sys = "0.3.69" itertools = "0.12.1" +serde = { version = "1.0", features = ["derive"] } +serde-wasm-bindgen = "0.4" [dev-dependencies] wasm-bindgen-test = "0.3.34" diff --git a/wasm/src/graph.rs b/wasm/src/graph.rs index e95d1455..ae3f2632 100644 --- a/wasm/src/graph.rs +++ b/wasm/src/graph.rs @@ -264,6 +264,18 @@ impl EdgeStruct { // } // } +#[wasm_bindgen] +#[derive(Clone, Debug)] +pub struct MermaidGraphOptions { + active_node: Option, + init_line: String, + chart_type: String, + direction: String, + collapse_opposing_edges: bool, + edge_label_attributes: Vec, + node_label_fn: Option, +} + #[wasm_bindgen] pub struct MermaidGraphData { mermaid: String, @@ -497,6 +509,7 @@ impl NoteGraph { entry_nodes: Vec, edge_types: Vec, max_depth: u32, + diagram_options: MermaidGraphOptions, ) -> Result { let now = Instant::now(); @@ -519,7 +532,10 @@ impl NoteGraph { let traversal_elapsed = now.elapsed(); let mut result = String::new(); - result.push_str("flowchart LR\n"); + // NOTE: double curly braces are used to escape the curly braces in the string + result.push_str(&diagram_options.init_line); + result.push_str("\n"); + result.push_str(format!("{} {}\n", diagram_options.chart_type, diagram_options.direction).as_str()); // accumulate edges by direction, so that we can collapse them in the next step let mut accumulated_edges: HashMap< @@ -565,11 +581,25 @@ impl NoteGraph { // add nodes to the graph for element in node_map.iter() { let weight = self.int_get_node_weight(element.0.clone())?; + + let node_label = match diagram_options.node_label_fn { + Some(ref function) => { + match function.call1(&JsValue::NULL, &weight.clone().into()) { + Ok(value) => value.as_string().unwrap_or_default(), + Err(e) => { + return Err(NoteGraphError::new(format!("Error calling function: {:?}", e).as_str())); + } + } + } + None => { + weight.path.clone() + } + }; + result.push_str(&format!( - " {}[\"{} ({})\"]\n", + " {}[\"{}\"]\n", element.0.index(), - weight.path, - element.1 + node_label )); if !weight.resolved { unresolved_nodes.insert(element.0.index()); From 223355762ebe8fe48cff8edeebd89f3c8b2bc2a5 Mon Sep 17 00:00:00 2001 From: Moritz Jung Date: Mon, 29 Apr 2024 20:22:51 +0200 Subject: [PATCH 10/65] mermaid code block collapse edge option --- wasm/src/graph.rs | 164 ++++++++++++++++++++++++---------------------- 1 file changed, 86 insertions(+), 78 deletions(-) diff --git a/wasm/src/graph.rs b/wasm/src/graph.rs index ae3f2632..39d6639c 100644 --- a/wasm/src/graph.rs +++ b/wasm/src/graph.rs @@ -535,7 +535,13 @@ impl NoteGraph { // NOTE: double curly braces are used to escape the curly braces in the string result.push_str(&diagram_options.init_line); result.push_str("\n"); - result.push_str(format!("{} {}\n", diagram_options.chart_type, diagram_options.direction).as_str()); + result.push_str( + format!( + "{} {}\n", + diagram_options.chart_type, diagram_options.direction + ) + .as_str(), + ); // accumulate edges by direction, so that we can collapse them in the next step let mut accumulated_edges: HashMap< @@ -581,26 +587,22 @@ impl NoteGraph { // add nodes to the graph for element in node_map.iter() { let weight = self.int_get_node_weight(element.0.clone())?; - + let node_label = match diagram_options.node_label_fn { Some(ref function) => { match function.call1(&JsValue::NULL, &weight.clone().into()) { Ok(value) => value.as_string().unwrap_or_default(), Err(e) => { - return Err(NoteGraphError::new(format!("Error calling function: {:?}", e).as_str())); + return Err(NoteGraphError::new( + format!("Error calling function: {:?}", e).as_str(), + )); } } } - None => { - weight.path.clone() - } + None => weight.path.clone(), }; - result.push_str(&format!( - " {}[\"{}\"]\n", - element.0.index(), - node_label - )); + result.push_str(&format!(" {}[\"{}\"]\n", element.0.index(), node_label)); if !weight.resolved { unresolved_nodes.insert(element.0.index()); } @@ -608,75 +610,22 @@ impl NoteGraph { // collapse edge data and add them to the graph for (from, to, forward, backward) in accumulated_edges.values() { - let mut label = String::new(); - - let same_elements = forward - .iter() - .zip(backward.iter()) - .all(|(a, b)| a.edge_type == b.edge_type); - let all_implied = forward - .iter() - .zip_longest(backward.iter()) - .all(|pair| match pair { - EitherOrBoth::Both(a, b) => a.implied && b.implied, - EitherOrBoth::Left(a) => a.implied, - EitherOrBoth::Right(b) => b.implied, - }); - - let arrow_type: String; - if backward.is_empty() { - if all_implied { - arrow_type = String::from("-.->"); - } else { - arrow_type = String::from("-->"); - } - } else { - if all_implied { - arrow_type = String::from("<-.->"); - } else { - arrow_type = String::from("<-->"); - } - } - - if same_elements { - label.push_str( - forward - .iter() - .map(|edge| edge.edge_type.clone()) - .collect::>() - .join(", ") - .as_str(), - ); + if diagram_options.collapse_opposing_edges { + result.push_str(&self.generate_mermaid_edge( + from, + to, + forward.clone(), + backward.clone(), + )); } else { - label.push_str( - forward - .iter() - .map(|edge| edge.edge_type.clone()) - .collect::>() - .join(", ") - .as_str(), - ); - // forward can never be empty - if !backward.is_empty() { - label.push_str(" / "); - } - label.push_str( - backward - .iter() - .map(|edge| edge.edge_type.clone()) - .collect::>() - .join(", ") - .as_str(), - ); + result.push_str(&self.generate_mermaid_edge(from, to, forward.clone(), Vec::new())); + result.push_str(&self.generate_mermaid_edge( + to, + from, + backward.clone(), + Vec::new(), + )); } - - result.push_str(&format!( - " {} {}|{}| {}\n", - from.index(), - arrow_type, - label, - to.index() - )); } if !unresolved_nodes.is_empty() { @@ -698,6 +647,65 @@ impl NoteGraph { )) } + fn generate_mermaid_edge( + &self, + source: &NodeIndex, + target: &NodeIndex, + forward: Vec, + backward: Vec, + ) -> String { + let mut label = String::new(); + + let same_elements = forward + .iter() + .zip(backward.iter()) + .all(|(a, b)| a.edge_type == b.edge_type); + let all_implied = forward + .iter() + .zip_longest(backward.iter()) + .all(|pair| match pair { + EitherOrBoth::Both(a, b) => a.implied && b.implied, + EitherOrBoth::Left(a) => a.implied, + EitherOrBoth::Right(b) => b.implied, + }); + + let arrow_type = match (backward.is_empty(), all_implied) { + (true, true) => "-.->", + (true, false) => "-->", + (false, true) => "-.-", + (false, false) => "---", + }; + + label.push_str( + forward + .iter() + .map(|edge| edge.edge_type.clone()) + .collect::>() + .join(", ") + .as_str(), + ); + + if !same_elements && !backward.is_empty() { + label.push_str(" | "); + label.push_str( + backward + .iter() + .map(|edge| edge.edge_type.clone()) + .collect::>() + .join(", ") + .as_str(), + ); + } + + format!( + " {} {}|{}| {}\n", + source.index(), + arrow_type, + label, + target.index() + ) + } + pub fn rec_traverse( &self, entry_node: String, From ad0aca8addde9a6c3b49df4c0cacb4fafbe0331d Mon Sep 17 00:00:00 2001 From: Moritz Jung Date: Wed, 1 May 2024 11:06:35 +0200 Subject: [PATCH 11/65] split up rust code --- src/components/EdgeLink.svelte | 4 +- wasm/src/graph.rs | 608 +++------------------------------ wasm/src/graph_mermaid.rs | 312 +++++++++++++++++ wasm/src/graph_traversal.rs | 260 ++++++++++++++ wasm/src/lib.rs | 2 + 5 files changed, 625 insertions(+), 561 deletions(-) create mode 100644 wasm/src/graph_mermaid.rs create mode 100644 wasm/src/graph_traversal.rs diff --git a/src/components/EdgeLink.svelte b/src/components/EdgeLink.svelte index c30a4fd0..2d249285 100644 --- a/src/components/EdgeLink.svelte +++ b/src/components/EdgeLink.svelte @@ -1,10 +1,9 @@
diff --git a/wasm/src/graph.rs b/wasm/src/graph.rs index aba3b72a..bb6f482e 100644 --- a/wasm/src/graph.rs +++ b/wasm/src/graph.rs @@ -383,6 +383,7 @@ impl NoteGraph { }); } + /// Returns all edge types that are present in the graph. pub fn edge_types(&self) -> Vec { self.edge_types.iter().cloned().collect() } diff --git a/wasm/src/graph_mermaid.rs b/wasm/src/graph_mermaid.rs index 36d9a776..435c2334 100644 --- a/wasm/src/graph_mermaid.rs +++ b/wasm/src/graph_mermaid.rs @@ -7,6 +7,7 @@ use web_time::Instant; use crate::{ graph::{EdgeData, NoteGraph}, + graph_traversal::TraversalOptions, utils::{NoteGraphError, Result}, }; @@ -85,33 +86,17 @@ impl MermaidGraphData { impl NoteGraph { pub fn generate_mermaid_graph( &self, - entry_nodes: Vec, - edge_types: Vec, - max_depth: u32, + traversal_options: TraversalOptions, diagram_options: MermaidGraphOptions, ) -> Result { let now = Instant::now(); - let mut entry_node_indices: Vec> = Vec::new(); - for node in entry_nodes { - let node_index = self - .int_get_node_index(&node) - .ok_or(NoteGraphError::new("Node not found"))?; - entry_node_indices.push(node_index); - } - - let (node_map, edge_map) = self.int_traverse_breadth_first( - entry_node_indices, - Some(&edge_types), - max_depth, - |_, depth| depth, - |edge| edge, - ); + let (node_map, edge_map) = self.int_traverse_basic(&traversal_options)?; let traversal_elapsed = now.elapsed(); let mut result = String::new(); - // NOTE: double curly braces are used to escape the curly braces in the string + result.push_str(&diagram_options.init_line); result.push_str("\n"); result.push_str( @@ -128,7 +113,6 @@ impl NoteGraph { (NodeIndex, NodeIndex, Vec, Vec), > = HashMap::new(); - // TODO: this sucks, maybe use a tuple of node indices instead of strings for (_, edge_ref) in edge_map { let forward_dir = (edge_ref.source(), edge_ref.target()); @@ -170,7 +154,8 @@ impl NoteGraph { let node_label = match diagram_options.node_label_fn { Some(ref function) => { match function.call1(&JsValue::NULL, &weight.clone().into()) { - Ok(value) => value.as_string().unwrap_or_default(), + // TODO: maybe error when the return value is not a string? + Ok(value) => value.as_string().unwrap_or(weight.path.clone()), Err(e) => { return Err(NoteGraphError::new( format!("Error calling function: {:?}", e).as_str(), diff --git a/wasm/src/graph_traversal.rs b/wasm/src/graph_traversal.rs index de2899d4..ef66d31d 100644 --- a/wasm/src/graph_traversal.rs +++ b/wasm/src/graph_traversal.rs @@ -15,6 +15,61 @@ use crate::{ }, }; +#[wasm_bindgen] +#[derive(Clone, Debug)] +pub struct TraversalOptions { + entry_nodes: Vec, + // if this is None, all edge types will be traversed + edge_types: Option>, + max_depth: u32, + /// if true, multiple traversals - one for each edge type - will be performed and the results will be combined + /// if false, one traversal over all edge types will be performed + separate_edges: bool, +} + +#[wasm_bindgen] +impl TraversalOptions { + #[wasm_bindgen(constructor)] + pub fn new( + entry_nodes: Vec, + edge_types: Option>, + max_depth: u32, + separate_edges: bool, + ) -> TraversalOptions { + TraversalOptions { + entry_nodes, + edge_types, + max_depth, + separate_edges, + } + } + + #[wasm_bindgen(getter)] + pub fn entry_nodes(&self) -> Vec { + self.entry_nodes.clone() + } + + #[wasm_bindgen(getter)] + pub fn edge_types(&self) -> Option> { + self.edge_types.clone() + } + + #[wasm_bindgen(getter)] + pub fn max_depth(&self) -> u32 { + self.max_depth + } + + #[wasm_bindgen(getter)] + pub fn separate_edges(&self) -> bool { + self.separate_edges + } + + #[wasm_bindgen(js_name = toString)] + pub fn to_string(&self) -> String { + format!("{:?}", self) + } +} + #[wasm_bindgen] #[derive(Clone, Debug)] pub struct RecTraversalData { @@ -22,6 +77,8 @@ pub struct RecTraversalData { edge: EdgeStruct, // the depth of the node in the traversal depth: u32, + // the number of total children of the node, so also children of children + number_of_children: u32, // the children of the node children: Vec, } @@ -29,10 +86,16 @@ pub struct RecTraversalData { #[wasm_bindgen] impl RecTraversalData { #[wasm_bindgen(constructor)] - pub fn new(edge: EdgeStruct, depth: u32, children: Vec) -> RecTraversalData { + pub fn new( + edge: EdgeStruct, + depth: u32, + number_of_children: u32, + children: Vec, + ) -> RecTraversalData { RecTraversalData { edge, depth, + number_of_children, children, } } @@ -64,51 +127,117 @@ impl RecTraversalData { } #[wasm_bindgen] -impl NoteGraph { - pub fn rec_traverse( - &self, - entry_node: String, - edge_types: Vec, +#[derive(Clone, Debug)] +pub struct RecTraversalResult { + data: Vec, + node_count: u32, + max_depth: u32, + traversal_time: u64, +} + +#[wasm_bindgen] +impl RecTraversalResult { + #[wasm_bindgen(constructor)] + pub fn new( + data: Vec, + node_count: u32, max_depth: u32, - ) -> Result> { - let now = Instant::now(); + traversal_time: u64, + ) -> RecTraversalResult { + RecTraversalResult { + data, + node_count, + max_depth, + traversal_time, + } + } + + #[wasm_bindgen(getter)] + pub fn data(&self) -> Vec { + self.data.clone() + } + + #[wasm_bindgen(getter)] + pub fn node_count(&self) -> u32 { + self.node_count + } + + #[wasm_bindgen(getter)] + pub fn max_depth(&self) -> u32 { + self.max_depth + } - let start_node = self - .int_get_node_index(&entry_node) - .ok_or(NoteGraphError::new("Node not found"))?; - let start_node_weight = self.int_get_node_weight(start_node)?; + #[wasm_bindgen(getter)] + pub fn traversal_time(&self) -> u64 { + self.traversal_time + } + + #[wasm_bindgen(js_name = toString)] + pub fn to_string(&self) -> String { + format!("{:?}", self) + } +} + +#[wasm_bindgen] +impl NoteGraph { + pub fn rec_traverse(&self, options: TraversalOptions) -> Result { + let now = Instant::now(); - let mut node_count = 1; let mut result = Vec::new(); - for edge in self.graph.edges(start_node) { - let target = edge.target(); - - let edge_struct = EdgeStruct::new( - start_node_weight.clone(), - self.int_get_node_weight(target)?.clone(), - edge.weight().clone(), - ); - - result.push(self.int_rec_traverse( - start_node, - edge_struct, - &edge_types, - 0, - max_depth, - &mut node_count, - )?); - - node_count += 1; + let edge_types = options.edge_types.unwrap_or(self.edge_types()); + + for entry_node in &options.entry_nodes { + let start_node = self + .int_get_node_index(&entry_node) + .ok_or(NoteGraphError::new("Node not found"))?; + + let start_node_weight = self.int_get_node_weight(start_node)?; + + for edge in self.graph.edges(start_node) { + if !self.int_edge_matches_edge_filter(edge.weight(), Some(&edge_types)) { + continue; + } + + let target = edge.target(); + + let edge_struct = EdgeStruct::new( + start_node_weight.clone(), + self.int_get_node_weight(target)?.clone(), + edge.weight().clone(), + ); + + if options.separate_edges { + result.push(self.int_rec_traverse( + start_node, + edge_struct.clone(), + Some(&vec![edge_struct.edge_type()]), + 0, + options.max_depth, + )?); + } else { + result.push(self.int_rec_traverse( + start_node, + edge_struct, + Some(&edge_types), + 0, + options.max_depth, + )?); + } + } } + let node_count = self.int_rec_count_children(&mut result); + let max_depth = self.int_rec_max_depth(&result); + let total_elapsed = now.elapsed(); - utils::log(format!( - "Total tree took {:.2?} ({} nodes)", - total_elapsed, node_count - )); - Ok(result) + Ok(RecTraversalResult::new( + result, + node_count, + max_depth, + total_elapsed.as_millis() as u64, + )) } } @@ -120,10 +249,9 @@ impl NoteGraph { &self, node: NodeIndex, edge: EdgeStruct, - edge_types: &Vec, + edge_types: Option<&Vec>, depth: u32, max_depth: u32, - node_count: &mut usize, ) -> Result { let mut new_children = Vec::new(); @@ -131,7 +259,7 @@ impl NoteGraph { for outgoing_edge in self.graph.edges(node) { let edge_weight = outgoing_edge.weight(); - if edge_types.contains(&edge_weight.edge_type) { + if self.int_edge_matches_edge_filter(&edge_weight, edge_types) { let target = outgoing_edge.target(); let edge_struct = EdgeStruct::new( @@ -146,15 +274,79 @@ impl NoteGraph { edge_types, depth + 1, max_depth, - node_count, )?) } } } - *node_count += new_children.len(); + Ok(RecTraversalData::new(edge, depth, 0, new_children)) + } + + pub fn int_rec_count_children(&self, data: &mut Vec) -> u32 { + let mut total_children = 0; + + for datum in data.iter_mut() { + datum.number_of_children = self.int_rec_count_children(&mut datum.children); + total_children += 1 + datum.number_of_children; + } + + total_children + } - Ok(RecTraversalData::new(edge, depth, new_children)) + pub fn int_rec_max_depth(&self, data: &Vec) -> u32 { + data.iter() + .map(|datum| u32::max(self.int_rec_max_depth(&datum.children), datum.depth)) + .max() + .unwrap_or(0) + } + + pub fn int_traverse_basic( + &self, + options: &TraversalOptions, + ) -> Result<( + Vec<(NodeIndex, u32)>, + Vec<(EdgeIndex, EdgeReference)>, + )> { + let entry_nodes = options + .entry_nodes + .iter() + .map(|node| { + self.int_get_node_index(node) + .ok_or(NoteGraphError::new("Node not found")) + }) + .into_iter() + .collect::>>>()?; + + if options.separate_edges { + let mut node_list = Vec::new(); + let mut edge_list = Vec::new(); + + let all_edge_types = self.edge_types(); + let edge_types: &Vec = options.edge_types.as_ref().unwrap_or(&all_edge_types); + + for edge_type in edge_types { + let (nodes, edges) = self.int_traverse_depth_first( + entry_nodes.clone(), + Some(&vec![edge_type.clone()]), + options.max_depth, + |_, depth| depth, + |edge| edge, + ); + + node_list.extend(nodes); + edge_list.extend(edges); + } + + Ok((node_list, edge_list)) + } else { + Ok(self.int_traverse_depth_first( + entry_nodes, + options.edge_types.as_ref(), + options.max_depth, + |_, depth| depth, + |edge| edge, + )) + } } /// Traverses the tree in a depth first manner and calls the provided callbacks for each node and edge. From e67b4ba68c5e22444175d0cfb5a7351916f6cab4 Mon Sep 17 00:00:00 2001 From: Moritz Jung Date: Wed, 1 May 2024 17:14:51 +0200 Subject: [PATCH 13/65] fix more views --- src/components/NestedEdgeList.svelte | 3 - .../codeblocks/CodeblockTree.svelte | 4 - src/components/page_views/TrailView.svelte | 70 +-- .../page_views/TrailViewGrid.svelte | 8 +- .../page_views/TrailViewPath.svelte | 20 +- src/components/side_views/Matrix.svelte | 41 +- .../side_views/MatrixEdgeField.svelte | 14 +- src/components/side_views/TreeView.svelte | 3 - src/graph/MyMultiGraph.ts | 104 ++--- src/graph/builders/implied/transitive.ts | 10 +- src/graph/builders/index.ts | 38 +- src/graph/distance.ts | 34 +- src/graph/objectify_mappers.ts | 76 +-- src/graph/traverse.ts | 348 +++++++------- src/graph/utils.ts | 88 ++-- src/utils/arrays.ts | 14 + src/utils/mermaid.ts | 442 +++++++++--------- wasm/src/graph.rs | 65 ++- wasm/src/graph_traversal.rs | 88 ++++ 19 files changed, 824 insertions(+), 646 deletions(-) diff --git a/src/components/NestedEdgeList.svelte b/src/components/NestedEdgeList.svelte index fb9f2d30..53fae124 100644 --- a/src/components/NestedEdgeList.svelte +++ b/src/components/NestedEdgeList.svelte @@ -1,11 +1,8 @@
- {#key sorted_paths} - {#if sorted_paths.length} + {#key traversal_data} + {#if !traversal_data.is_empty()}
- import type { BCEdge } from "src/graph/MyMultiGraph"; import type BreadcrumbsPlugin from "src/main"; import { ensure_square_array, @@ -7,16 +6,17 @@ transpose, } from "src/utils/arrays"; import EdgeLink from "../EdgeLink.svelte"; + import type { Path } from "wasm/pkg/breadcrumbs_graph_wasm"; export let plugin: BreadcrumbsPlugin; - export let all_paths: BCEdge[][]; + export let all_paths: Path[]; - const reversed = all_paths.map((path) => [...path].reverse()); + const reversed = all_paths.map((path) => path.reverse_edges); const square = ensure_square_array(reversed, null, true); const col_runs = transpose(square).map((col) => - gather_by_runs(col, (e) => (e ? e.target_id : null)), + gather_by_runs(col, (e) => (e ? e.target.path : null)), ); diff --git a/src/components/page_views/TrailViewPath.svelte b/src/components/page_views/TrailViewPath.svelte index 9b25604b..134d1784 100644 --- a/src/components/page_views/TrailViewPath.svelte +++ b/src/components/page_views/TrailViewPath.svelte @@ -1,14 +1,12 @@
@@ -19,13 +17,11 @@ {#if j !== 0} {/if} diff --git a/src/components/side_views/Matrix.svelte b/src/components/side_views/Matrix.svelte index 87ab720d..986eab05 100644 --- a/src/components/side_views/Matrix.svelte +++ b/src/components/side_views/Matrix.svelte @@ -1,5 +1,5 @@
diff --git a/src/components/side_views/MatrixEdgeField.svelte b/src/components/side_views/MatrixEdgeField.svelte index feb4a557..4b611ae1 100644 --- a/src/components/side_views/MatrixEdgeField.svelte +++ b/src/components/side_views/MatrixEdgeField.svelte @@ -1,15 +1,14 @@
- {#key traversal_data} - {#if !traversal_data.is_empty()} + {#key sorted_paths} + {#if sorted_paths.length}
= MAX_DEPTH} - on:click={() => - (depth = Math.min(MAX_DEPTH, depth + 1))} + on:click={() => (depth = Math.min(MAX_DEPTH, depth + 1))} > + diff --git a/wasm/src/graph_traversal.rs b/wasm/src/graph_traversal.rs index a73aab60..c89f542e 100644 --- a/wasm/src/graph_traversal.rs +++ b/wasm/src/graph_traversal.rs @@ -82,8 +82,10 @@ impl Path { self.edges.len() } - pub fn truncate(&mut self, limit: usize) { - self.edges.truncate(limit); + pub fn truncate(&self, limit: usize) -> Path { + let mut copy = self.clone(); + copy.edges.truncate(limit); + copy } #[wasm_bindgen(getter)] From c47575fbe075e7138425647c768ed9012a9340a2 Mon Sep 17 00:00:00 2001 From: Moritz Jung Date: Thu, 2 May 2024 21:04:00 +0200 Subject: [PATCH 17/65] fix more bugs --- src/codeblocks/MDRC.ts | 8 +++++--- src/codeblocks/index.ts | 20 ------------------- .../codeblocks/CodeblockMermaid.svelte | 6 +++++- .../codeblocks/CodeblockTree.svelte | 20 ++++++++++++++++--- src/main.ts | 6 +++--- wasm/src/graph_traversal.rs | 16 +++++++++++++++ 6 files changed, 46 insertions(+), 30 deletions(-) diff --git a/src/codeblocks/MDRC.ts b/src/codeblocks/MDRC.ts index c206abfa..1f9dad67 100644 --- a/src/codeblocks/MDRC.ts +++ b/src/codeblocks/MDRC.ts @@ -6,6 +6,7 @@ import { log } from "src/logger"; import type BreadcrumbsPlugin from "src/main"; import { Timer } from "src/utils/timer"; import { Codeblocks } from "."; +import { BCEvent } from "src/main"; export class CodeblockMDRC extends MarkdownRenderChild { source: string; @@ -45,8 +46,6 @@ export class CodeblockMDRC extends MarkdownRenderChild { log.debug("CodeblockMDRC.load"); - Codeblocks.register(this); - this.containerEl.empty(); const timer_inner = new Timer(); @@ -111,11 +110,14 @@ export class CodeblockMDRC extends MarkdownRenderChild { log.debug(timer_inner.elapsedMessage("component creation", true)); log.debug(timer_outer.elapsedMessage("CodeblockMDRC.onload")); + + this.registerEvent(this.plugin.events.on(BCEvent.GRAPH_UPDATE, () => { + this.update(); + })); } onunload(): void { log.debug("CodeblockMDRC.unload"); - Codeblocks.unregister(this); this.component?.$destroy(); } diff --git a/src/codeblocks/index.ts b/src/codeblocks/index.ts index 51b340ff..98fec71c 100644 --- a/src/codeblocks/index.ts +++ b/src/codeblocks/index.ts @@ -132,27 +132,7 @@ You can use \`app.plugins.plugins.dataview.api.pages("")\` to test your q return { options: parsed, file_path }; }; -const active_codeblocks: Map = new Map(); - -const register = (codeBlock: CodeblockMDRC) => { - active_codeblocks.set(codeBlock.id, codeBlock); -}; - -const unregister = (codeBlock: CodeblockMDRC) => { - active_codeblocks.delete(codeBlock.id); -}; - -const update_all = () => { - for (const codeBlock of active_codeblocks.values()) { - void codeBlock.update(); - } -}; - export const Codeblocks = { parse_source, postprocess_options, - - register, - unregister, - update_all, }; diff --git a/src/components/codeblocks/CodeblockMermaid.svelte b/src/components/codeblocks/CodeblockMermaid.svelte index 209ca2a6..be3f285e 100644 --- a/src/components/codeblocks/CodeblockMermaid.svelte +++ b/src/components/codeblocks/CodeblockMermaid.svelte @@ -73,7 +73,11 @@ }; onMount(() => { - update(); + try { + update(); + } catch (e) { + log.warn("Error updating codeblock mermaid", e); + } }); // const sort = get_edge_sorter( diff --git a/src/components/codeblocks/CodeblockTree.svelte b/src/components/codeblocks/CodeblockTree.svelte index 4b803d83..5b7ed96c 100644 --- a/src/components/codeblocks/CodeblockTree.svelte +++ b/src/components/codeblocks/CodeblockTree.svelte @@ -11,6 +11,8 @@ import CopyToClipboardButton from "../button/CopyToClipboardButton.svelte"; import CodeblockErrors from "./CodeblockErrors.svelte"; import { TraversalOptions, type RecTraversalData } from "wasm/pkg/breadcrumbs_graph_wasm"; + import { log } from "src/logger"; + import { Timer } from "src/utils/timer"; export let plugin: BreadcrumbsPlugin; export let options: ICodeblock["Options"]; @@ -23,21 +25,33 @@ ); const { show_node_options } = plugin.settings.views.codeblocks; + const DEFAULT_MAX_DEPTH = 100; + let tree: RecTraversalData[] = []; export const update = () => { + const max_depth = options.depth[1] ?? DEFAULT_MAX_DEPTH; + tree = plugin.graph.rec_traverse( new TraversalOptions( [file_path], - options.fields, - options.depth[1] ?? 100, + options.fields ?? [], + max_depth === Infinity ? DEFAULT_MAX_DEPTH : max_depth, !options["merge-fields"], ) ).data; }; onMount(() => { - update(); + const timer = new Timer(); + + try { + update(); + } catch (e) { + log.warn("Error updating codeblock tree", e); + } + + log.debug(timer.elapsedMessage("CodeblockTree initial traversal")); }); // TODO reimplement all this logic diff --git a/src/main.ts b/src/main.ts index c1a728f1..dd9bb305 100644 --- a/src/main.ts +++ b/src/main.ts @@ -445,9 +445,9 @@ export default class BreadcrumbsPlugin extends Plugin { redraw_page_views(this); } - if (options?.redraw_codeblocks !== false) { - Codeblocks.update_all(); - } + // if (options?.redraw_codeblocks !== false) { + // Codeblocks.update_all(); + // } if (options?.redraw_side_views === true) { this.app.workspace diff --git a/wasm/src/graph_traversal.rs b/wasm/src/graph_traversal.rs index c89f542e..72113592 100644 --- a/wasm/src/graph_traversal.rs +++ b/wasm/src/graph_traversal.rs @@ -15,6 +15,8 @@ use crate::{ }, }; +const TRAVERSAL_COUNT_LIMIT: u32 = 10_000; + #[wasm_bindgen] #[derive(Clone, Debug)] pub struct TraversalOptions { @@ -282,6 +284,8 @@ impl NoteGraph { let edge_types = options.edge_types.unwrap_or(self.edge_types()); + let mut traversal_count = 0; + for entry_node in &options.entry_nodes { let start_node = self .int_get_node_index(&entry_node) @@ -302,6 +306,8 @@ impl NoteGraph { edge.weight().clone(), ); + traversal_count += 1; + if options.separate_edges { result.push(self.int_rec_traverse( target, @@ -309,6 +315,7 @@ impl NoteGraph { Some(&vec![edge_struct.edge_type()]), 0, options.max_depth, + &mut traversal_count, )?); } else { result.push(self.int_rec_traverse( @@ -317,6 +324,7 @@ impl NoteGraph { Some(&edge_types), 0, options.max_depth, + &mut traversal_count, )?); } } @@ -347,6 +355,7 @@ impl NoteGraph { edge_types: Option<&Vec>, depth: u32, max_depth: u32, + traversal_count: &mut u32, ) -> Result { let mut new_children = Vec::new(); @@ -365,12 +374,19 @@ impl NoteGraph { edge_data.clone(), ); + *traversal_count += 1; + + if *traversal_count > TRAVERSAL_COUNT_LIMIT { + return Err(NoteGraphError::new("Traversal count limit reached")); + } + new_children.push(self.int_rec_traverse( target, edge_struct, edge_types, depth + 1, max_depth, + traversal_count, )?) } } From f895f71c00366bb120c7ec8532fd7d5788226ffc Mon Sep 17 00:00:00 2001 From: Moritz Jung Date: Thu, 2 May 2024 21:56:26 +0200 Subject: [PATCH 18/65] rust performance improvements due to vector based set --- wasm/Cargo.toml | 2 ++ wasm/src/graph.rs | 19 ++++++++++--------- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/wasm/Cargo.toml b/wasm/Cargo.toml index 82732720..62a2cb19 100644 --- a/wasm/Cargo.toml +++ b/wasm/Cargo.toml @@ -24,6 +24,8 @@ js-sys = "0.3.69" itertools = "0.12.1" serde = { version = "1.0", features = ["derive"] } serde-wasm-bindgen = "0.4" +smallvec = "1.13.2" +vec-collections = "0.4.3" [dev-dependencies] wasm-bindgen-test = "0.3.34" diff --git a/wasm/src/graph.rs b/wasm/src/graph.rs index b7819da3..1d81c7ea 100644 --- a/wasm/src/graph.rs +++ b/wasm/src/graph.rs @@ -8,6 +8,7 @@ use petgraph::{ visit::{EdgeRef, IntoEdgeReferences, IntoNodeReferences, NodeRef}, Directed, }; +use vec_collections::{AbstractVecSet, VecSet}; use wasm_bindgen::prelude::*; use web_time::Instant; @@ -294,7 +295,7 @@ pub struct NoteGraph { #[wasm_bindgen(skip)] pub transitive_rules: Vec, #[wasm_bindgen(skip)] - pub edge_types: HashSet, + pub edge_types: VecSet<[String; 16]>, #[wasm_bindgen(skip)] pub node_hash: HashMap>, update_callback: Option, @@ -306,7 +307,7 @@ impl NoteGraph { NoteGraph { graph: StableGraph::::default(), transitive_rules: Vec::new(), - edge_types: HashSet::new(), + edge_types: VecSet::empty(), node_hash: HashMap::new(), update_callback: None, } @@ -338,7 +339,7 @@ impl NoteGraph { let now = Instant::now(); self.graph = StableGraph::::default(); - self.edge_types = HashSet::new(); + self.edge_types = VecSet::empty(); // self.graph.reserve_exact_nodes(nodes.len()); @@ -474,7 +475,7 @@ impl NoteGraph { // we would need to also check back edges though // a rule like [A, B] -> (C with back edge D) would do nothing if applied multiple times, since the edges on the left side were not modified - let mut edge_type_tracker: HashSet = self.edge_types.clone(); + let mut edge_type_tracker = self.edge_types.clone(); for i in 1..(max_rounds + 1) { if edge_type_tracker.is_empty() { @@ -550,7 +551,7 @@ impl NoteGraph { // utils::log(format!("New edge count: {}", edges_to_add.len())); - let mut current_edge_type_tracker: HashSet = HashSet::new(); + let mut current_edge_type_tracker: VecSet<[String; 16]> = VecSet::empty(); let now2 = Instant::now(); utils::log(format!("Adding {} Edges ", edges_to_add.len())); @@ -575,7 +576,7 @@ impl NoteGraph { } pub fn int_rebuild_edge_type_tracker(&mut self) { - self.edge_types = HashSet::new(); + self.edge_types = VecSet::empty(); for edge in self.graph.edge_references() { self.edge_types.insert(edge.weight().edge_type.clone()); @@ -591,7 +592,7 @@ impl NoteGraph { pub fn int_add_to_edge_type_tracker( &mut self, edge_type: &String, - edge_type_tracker: &mut Option<&mut HashSet>, + edge_type_tracker: &mut Option<&mut VecSet<[String; 16]>>, add_to_global: bool, ) { if add_to_global { @@ -775,7 +776,7 @@ impl NoteGraph { from: NodeIndex, to: NodeIndex, edge_data: EdgeData, - edge_type_tracker: &mut Option<&mut HashSet>, + edge_type_tracker: &mut Option<&mut VecSet<[String; 16]>>, ) { if self.int_has_edge(from, to, &edge_data.edge_type) { return; @@ -1007,7 +1008,7 @@ impl NoteGraph { } pub fn assert_correct_trackers(&self) { - let mut edge_types: HashSet = HashSet::new(); + let mut edge_types: VecSet<[String; 16]> = VecSet::empty(); for edge in self.graph.edge_references() { edge_types.insert(edge.weight().edge_type.clone()); From 062194d54fa0baa802e48b25cb1f8f9d277f75b8 Mon Sep 17 00:00:00 2001 From: Moritz Jung Date: Fri, 3 May 2024 00:41:12 +0200 Subject: [PATCH 19/65] fix lot's of clippy lint issues --- wasm/Cargo.toml | 6 +- wasm/src/graph.rs | 375 +++++---------------------------- wasm/src/graph_construction.rs | 4 +- wasm/src/graph_data.rs | 267 +++++++++++++++++++++++ wasm/src/graph_mermaid.rs | 108 +++++----- wasm/src/graph_rules.rs | 2 +- wasm/src/graph_traversal.rs | 94 +++++---- wasm/src/graph_update.rs | 11 +- wasm/src/lib.rs | 7 +- wasm/src/utils.rs | 8 +- 10 files changed, 452 insertions(+), 430 deletions(-) create mode 100644 wasm/src/graph_data.rs diff --git a/wasm/Cargo.toml b/wasm/Cargo.toml index 62a2cb19..f981dc32 100644 --- a/wasm/Cargo.toml +++ b/wasm/Cargo.toml @@ -32,5 +32,7 @@ wasm-bindgen-test = "0.3.34" [profile.release] # Tell `rustc` to optimize for small code size. -opt-level = 3 -lto = true +opt-level = 3 # Highest level of optimization. +lto = "fat" # Enable link-time optimization. +overflow-checks = false # Disable integer overflow checks. +incremental = true # Enable incremental compilation for faster builds. diff --git a/wasm/src/graph.rs b/wasm/src/graph.rs index 1d81c7ea..a765a57b 100644 --- a/wasm/src/graph.rs +++ b/wasm/src/graph.rs @@ -1,10 +1,7 @@ -use std::{ - collections::{HashMap, HashSet}, - fmt::Debug, -}; +use std::collections::HashMap; use petgraph::{ - stable_graph::{EdgeIndex, EdgeReference, Edges, NodeIndex, StableGraph}, + stable_graph::{Edges, StableGraph}, visit::{EdgeRef, IntoEdgeReferences, IntoNodeReferences, NodeRef}, Directed, }; @@ -14,6 +11,7 @@ use web_time::Instant; use crate::{ graph_construction::{GraphConstructionEdgeData, GraphConstructionNodeData}, + graph_data::{EdgeData, EdgeStruct, NGEdgeIndex, NGEdgeRef, NGNodeIndex, NodeData}, graph_rules::TransitiveGraphRule, graph_update::BatchGraphUpdate, utils::{self, NoteGraphError, Result}, @@ -26,267 +24,6 @@ pub fn edge_matches_edge_filter(edge: &EdgeData, edge_types: Option<&Vec } } -#[wasm_bindgen] -#[derive(Clone, Debug, PartialEq)] -pub struct EdgeData { - #[wasm_bindgen(skip)] - pub edge_type: String, - #[wasm_bindgen(skip)] - pub edge_source: String, - #[wasm_bindgen(skip)] - pub implied: bool, - #[wasm_bindgen(skip)] - pub round: u8, -} - -#[wasm_bindgen] -impl EdgeData { - #[wasm_bindgen(constructor)] - pub fn new(edge_type: String, edge_source: String, implied: bool, round: u8) -> EdgeData { - EdgeData { - edge_type, - edge_source, - implied, - round, - } - } - - #[wasm_bindgen(getter)] - pub fn edge_type(&self) -> String { - self.edge_type.clone() - } - - #[wasm_bindgen(getter)] - pub fn edge_source(&self) -> String { - self.edge_source.clone() - } - - #[wasm_bindgen(getter)] - pub fn implied(&self) -> bool { - self.implied - } - - #[wasm_bindgen(getter)] - pub fn round(&self) -> u8 { - self.round - } - - #[wasm_bindgen(js_name = toString)] - pub fn to_string(&self) -> String { - format!("{:#?}", self) - } -} - -impl EdgeData { - pub fn get_attribute_label(&self, attributes: &Vec) -> String { - let mut result = vec![]; - - // the mapping that exist on the JS side are as follows - // "field" | "explicit" | "source" | "implied_kind" | "round" - - // TODO: maybe change the attribute options so that the JS side better matches the data - - for attribute in attributes { - let data = match attribute.as_str() { - "field" => Some(("field", self.edge_type.clone())), - "explicit" => Some(("explicit", (!self.implied).to_string())), - "source" => { - if !self.implied { - Some(("source", self.edge_source.clone())) - } else { - None - } - } - "implied_kind" => { - if self.implied { - Some(("implied_kind", self.edge_source.clone())) - } else { - None - } - } - "round" => Some(("round", self.round.to_string())), - _ => None, - }; - - match data { - Some(data) => result.push(format!("{}={}", data.0, data.1)), - None => {} - } - } - - result.join(" ") - } -} - -// impl PartialEq for EdgeData { -// fn eq(&self, other: &Self) -> bool { -// self.edge_type == other.edge_type -// } -// } - -#[wasm_bindgen] -#[derive(Clone, Debug, PartialEq)] -pub struct NodeData { - #[wasm_bindgen(skip)] - pub path: String, - #[wasm_bindgen(skip)] - pub aliases: Vec, - #[wasm_bindgen(skip)] - pub resolved: bool, - #[wasm_bindgen(skip)] - pub ignore_in_edges: bool, - #[wasm_bindgen(skip)] - pub ignore_out_edges: bool, -} - -#[wasm_bindgen] -impl NodeData { - #[wasm_bindgen(constructor)] - pub fn new( - path: String, - aliases: Vec, - resolved: bool, - ignore_in_edges: bool, - ignore_out_edges: bool, - ) -> NodeData { - NodeData { - path, - aliases, - resolved, - ignore_in_edges, - ignore_out_edges, - } - } - - #[wasm_bindgen(getter)] - pub fn path(&self) -> String { - self.path.clone() - } - - #[wasm_bindgen(getter)] - pub fn aliases(&self) -> Vec { - self.aliases.clone() - } - - #[wasm_bindgen(getter)] - pub fn resolved(&self) -> bool { - self.resolved - } - - #[wasm_bindgen(getter)] - pub fn ignore_in_edges(&self) -> bool { - self.ignore_in_edges - } - - #[wasm_bindgen(getter)] - pub fn ignore_out_edges(&self) -> bool { - self.ignore_out_edges - } - - #[wasm_bindgen(js_name = toString)] - pub fn to_string(&self) -> String { - format!("{:#?}", self) - } -} - -impl NodeData { - pub fn from_construction_data(data: &GraphConstructionNodeData) -> NodeData { - NodeData { - path: data.path.clone(), - aliases: data.aliases.clone(), - resolved: data.resolved, - ignore_in_edges: data.ignore_in_edges, - ignore_out_edges: data.ignore_out_edges, - } - } - - pub fn new_unresolved(path: String) -> NodeData { - NodeData { - path, - aliases: Vec::new(), - resolved: false, - ignore_in_edges: false, - ignore_out_edges: false, - } - } -} - -#[wasm_bindgen] -#[derive(Clone, Debug, PartialEq)] -pub struct EdgeStruct { - #[wasm_bindgen(skip)] - pub source: NodeData, - #[wasm_bindgen(skip)] - pub target: NodeData, - #[wasm_bindgen(skip)] - pub edge: EdgeData, -} - -#[wasm_bindgen] -impl EdgeStruct { - #[wasm_bindgen(constructor)] - pub fn new(source: NodeData, target: NodeData, edge: EdgeData) -> EdgeStruct { - EdgeStruct { - source, - target, - edge, - } - } - - #[wasm_bindgen(getter)] - pub fn source(&self) -> NodeData { - self.source.clone() - } - - #[wasm_bindgen(getter)] - pub fn target(&self) -> NodeData { - self.target.clone() - } - - #[wasm_bindgen(getter)] - pub fn edge_type(&self) -> String { - self.edge.edge_type.clone() - } - - #[wasm_bindgen(getter)] - pub fn edge_source(&self) -> String { - self.edge.edge_source.clone() - } - - #[wasm_bindgen(getter)] - pub fn implied(&self) -> bool { - self.edge.implied - } - - #[wasm_bindgen(getter)] - pub fn round(&self) -> u8 { - self.edge.round - } - - pub fn get_attribute_label(&self, attributes: Vec) -> String { - self.edge.get_attribute_label(&attributes) - } - - pub fn matches_edge_filter(&self, edge_types: Option>) -> bool { - edge_matches_edge_filter(&self.edge, edge_types.as_ref()) - } - - pub fn is_self_loop(&self) -> bool { - self.source.path == self.target.path - } - - #[wasm_bindgen(js_name = toString)] - pub fn to_string(&self) -> String { - format!("{:#?}", self) - } -} - -// impl PartialEq for NodeData { -// fn eq(&self, other: &Self) -> bool { -// self.path == other.path -// } -// } - #[wasm_bindgen] #[derive(Clone)] pub struct NoteGraph { @@ -297,7 +34,7 @@ pub struct NoteGraph { #[wasm_bindgen(skip)] pub edge_types: VecSet<[String; 16]>, #[wasm_bindgen(skip)] - pub node_hash: HashMap>, + pub node_hash: HashMap, update_callback: Option, } @@ -395,7 +132,7 @@ impl NoteGraph { pub fn iterate_nodes(&self, f: &js_sys::Function) { let this = JsValue::NULL; - self.graph.node_references().into_iter().for_each(|node| { + self.graph.node_references().for_each(|node| { match f.call1(&this, &node.weight().clone().into()) { Ok(_) => {} Err(e) => utils::log(format!("Error calling function: {:?}", e)), @@ -406,7 +143,7 @@ impl NoteGraph { pub fn iterate_edges(&self, f: &js_sys::Function) { let this = JsValue::NULL; - self.graph.edge_references().into_iter().for_each(|edge| { + self.graph.edge_references().for_each(|edge| { match f.call1(&this, &edge.weight().clone().into()) { Ok(_) => {} Err(e) => utils::log(format!("Error calling function: {:?}", e)), @@ -452,6 +189,12 @@ impl NoteGraph { } } +impl Default for NoteGraph { + fn default() -> Self { + Self::new() + } +} + /// Internal methods, not exposed to the wasm interface. /// All of these methods are prefixed with `int_`. impl NoteGraph { @@ -482,7 +225,7 @@ impl NoteGraph { break; } - let mut edges_to_add: Vec<(NodeIndex, NodeIndex, EdgeData)> = Vec::new(); + let mut edges_to_add: Vec<(NGNodeIndex, NGNodeIndex, EdgeData)> = Vec::new(); for rule in self.transitive_rules.iter() { // if there is any edge type that the graph doesn't have, we can skip the rule @@ -584,30 +327,30 @@ impl NoteGraph { } /// Returns the node index for a specific node weight. - pub fn int_get_node_index(&self, node: &String) -> Option> { - self.node_hash.get(node).map(|index| index.clone()) + pub fn int_get_node_index(&self, node: &String) -> Option { + self.node_hash.get(node).copied() } /// Adds an edge type to the global edge type tracker and an optional local edge type tracker. pub fn int_add_to_edge_type_tracker( &mut self, - edge_type: &String, + edge_type: &str, edge_type_tracker: &mut Option<&mut VecSet<[String; 16]>>, add_to_global: bool, ) { if add_to_global { - self.edge_types.insert(edge_type.clone()); + self.edge_types.insert(edge_type.to_owned()); } match edge_type_tracker { Some(inner) => { - inner.insert(edge_type.clone()); + inner.insert(edge_type.to_owned()); } None => {} } } - pub fn int_edge_ref_to_struct(&self, edge: EdgeReference) -> Option { + pub fn int_edge_ref_to_struct(&self, edge: NGEdgeRef) -> Option { let source = self.graph.node_weight(edge.source())?.clone(); let target = self.graph.node_weight(edge.target())?.clone(); @@ -616,7 +359,7 @@ impl NoteGraph { pub fn int_edge_to_struct( &self, - edge_index: EdgeIndex, + edge_index: NGEdgeIndex, edge_data: EdgeData, ) -> Option { let (source_index, target_index) = self.graph.edge_endpoints(edge_index)?; @@ -629,44 +372,38 @@ impl NoteGraph { /// Returns the node weight for a specific node index. /// /// Will return an error if the node is not found. - pub fn int_get_node_weight(&self, node: NodeIndex) -> Result<&NodeData> { + pub fn int_get_node_weight(&self, node: NGNodeIndex) -> Result<&NodeData> { self.graph .node_weight(node) .ok_or(NoteGraphError::new("Node not found")) } - pub fn int_has_incoming_edges(&self, node: NodeIndex) -> bool { + pub fn int_has_incoming_edges(&self, node: NGNodeIndex) -> bool { self.graph .edges_directed(node, petgraph::Direction::Incoming) .count() > 0 } - pub fn int_has_outgoing_edges(&self, node: NodeIndex) -> bool { + pub fn int_has_outgoing_edges(&self, node: NGNodeIndex) -> bool { self.graph .edges_directed(node, petgraph::Direction::Outgoing) .count() > 0 } - pub fn int_iter_incoming_edges( - &self, - node: NodeIndex, - ) -> Edges<'_, EdgeData, Directed, u32> { + pub fn int_iter_incoming_edges(&self, node: NGNodeIndex) -> Edges<'_, EdgeData, Directed, u32> { self.graph .edges_directed(node, petgraph::Direction::Incoming) } - pub fn int_iter_outgoing_edges( - &self, - node: NodeIndex, - ) -> Edges<'_, EdgeData, Directed, u32> { + pub fn int_iter_outgoing_edges(&self, node: NGNodeIndex) -> Edges<'_, EdgeData, Directed, u32> { self.graph .edges_directed(node, petgraph::Direction::Outgoing) } - pub fn int_remove_outgoing_edges(&mut self, node: NodeIndex) { - let mut edges_to_remove: Vec> = Vec::new(); + pub fn int_remove_outgoing_edges(&mut self, node: NGNodeIndex) { + let mut edges_to_remove: Vec = Vec::new(); for edge in self.graph.edges(node) { edges_to_remove.push(edge.id()); @@ -677,7 +414,7 @@ impl NoteGraph { } } - pub fn int_set_node_resolved(&mut self, node: NodeIndex, resolved: bool) -> Result<()> { + pub fn int_set_node_resolved(&mut self, node: NGNodeIndex, resolved: bool) -> Result<()> { let node = self.graph.node_weight_mut(node); match node { Some(node) => { @@ -700,7 +437,7 @@ impl NoteGraph { /// Returns the edge weight for a specific edge index. /// /// Will return an error if the edge is not found. - pub fn int_get_edge_weight(&self, edge: EdgeIndex) -> Result<&EdgeData> { + pub fn int_get_edge_weight(&self, edge: NGEdgeIndex) -> Result<&EdgeData> { self.graph .edge_weight(edge) .ok_or(NoteGraphError::new("Edge not found")) @@ -717,7 +454,7 @@ impl NoteGraph { pub fn int_remove_implied_edges(&mut self) { let now = Instant::now(); - // let mut edges_to_remove: Vec> = Vec::new(); + // let mut edges_to_remove: Vec = Vec::new(); self.graph.retain_edges(|frozen_graph, edge| { let weight = frozen_graph.edge_weight(edge).unwrap(); @@ -773,8 +510,8 @@ impl NoteGraph { /// Will add the added edge types to the global edge type tracker and an optional local edge type tracker. fn int_add_edge( &mut self, - from: NodeIndex, - to: NodeIndex, + from: NGNodeIndex, + to: NGNodeIndex, edge_data: EdgeData, edge_type_tracker: &mut Option<&mut VecSet<[String; 16]>>, ) { @@ -789,14 +526,13 @@ impl NoteGraph { fn int_remove_edge( &mut self, - from: NodeIndex, - to: NodeIndex, + from: NGNodeIndex, + to: NGNodeIndex, edge_type: &String, ) -> Result<()> { let edge = self .graph .edges(from) - .into_iter() .find(|e| e.target() == to && *e.weight().edge_type == *edge_type); match edge { @@ -810,13 +546,12 @@ impl NoteGraph { pub fn int_get_edge( &self, - from: NodeIndex, - to: NodeIndex, + from: NGNodeIndex, + to: NGNodeIndex, edge_type: &String, - ) -> Option> { + ) -> Option { self.graph .edges(from) - .into_iter() .find(|e| e.target() == to && *e.weight().edge_type == *edge_type) } @@ -825,7 +560,7 @@ impl NoteGraph { from: &String, to: &String, edge_type: &String, - ) -> Option> { + ) -> Option { let from_index = self.int_get_node_index(from)?; let to_index = self.int_get_node_index(to)?; @@ -833,15 +568,9 @@ impl NoteGraph { } /// Checks if an edge exists between two nodes with a specific edge type. - pub fn int_has_edge( - &self, - from: NodeIndex, - to: NodeIndex, - edge_type: &String, - ) -> bool { + pub fn int_has_edge(&self, from: NGNodeIndex, to: NGNodeIndex, edge_type: &String) -> bool { self.graph .edges(from) - .into_iter() .any(|e| e.target() == to && *e.weight().edge_type == *edge_type) } @@ -891,7 +620,7 @@ impl NoteGraph { self.int_set_node_resolved(index, false)?; - let edges_to_remove: Vec> = self + let edges_to_remove: Vec = self .int_iter_outgoing_edges(index) .map(|edge| edge.id()) .collect(); @@ -908,19 +637,19 @@ impl NoteGraph { Ok(()) } - pub fn int_safe_rename_node(&mut self, old_name: &String, new_name: &String) -> Result<()> { + pub fn int_safe_rename_node(&mut self, old_name: &String, new_name: &str) -> Result<()> { let node_index = self .int_get_node_index(old_name) .ok_or(NoteGraphError::new("Old node not found"))?; - self.graph.node_weight_mut(node_index).unwrap().path = new_name.clone(); + self.graph.node_weight_mut(node_index).unwrap().path = new_name.to_owned(); self.node_hash.remove(old_name); - self.node_hash.insert(new_name.clone(), node_index); + self.node_hash.insert(new_name.to_owned(), node_index); Ok(()) } - fn int_safe_delete_edge_ref(&mut self, edge: EdgeIndex) -> Result<()> { + fn int_safe_delete_edge_ref(&mut self, edge: NGEdgeIndex) -> Result<()> { let (_, target) = self .graph .edge_endpoints(edge) @@ -958,14 +687,14 @@ impl NoteGraph { &mut self, source_path: &String, target_path: &String, - edge_type: &String, - edge_source: &String, + edge_type: &str, + edge_source: &str, ) { let source = self.node_hash.get(source_path); let target = self.node_hash.get(target_path); - let source_index: NodeIndex; - let target_index: NodeIndex; + let source_index: NGNodeIndex; + let target_index: NGNodeIndex; let mut add_from_to_hash = false; let mut add_to_to_hash = false; @@ -975,7 +704,7 @@ impl NoteGraph { .add_node(NodeData::new_unresolved(source_path.clone())); add_from_to_hash = true; } else { - source_index = source.unwrap().clone(); + source_index = *source.unwrap(); } if target.is_none() { @@ -988,7 +717,7 @@ impl NoteGraph { add_to_to_hash = true; } } else { - target_index = target.unwrap().clone(); + target_index = *target.unwrap(); } if add_from_to_hash { @@ -1002,7 +731,7 @@ impl NoteGraph { self.int_add_edge( source_index, target_index, - EdgeData::new(edge_type.clone(), edge_source.clone(), false, 0), + EdgeData::new(edge_type.to_owned(), edge_source.to_owned(), false, 0), &mut None, ); } @@ -1016,7 +745,7 @@ impl NoteGraph { assert_eq!(edge_types, self.edge_types); - let mut node_hash: HashMap> = HashMap::new(); + let mut node_hash: HashMap = HashMap::new(); for node_ref in self.graph.node_references() { node_hash.insert(node_ref.weight().path.clone(), node_ref.id()); diff --git a/wasm/src/graph_construction.rs b/wasm/src/graph_construction.rs index d1bbda5e..84143598 100644 --- a/wasm/src/graph_construction.rs +++ b/wasm/src/graph_construction.rs @@ -35,7 +35,7 @@ impl GraphConstructionNodeData { } #[wasm_bindgen(js_name = toString)] - pub fn to_string(&self) -> String { + pub fn to_fancy_string(&self) -> String { format!("{:#?}", self) } } @@ -83,7 +83,7 @@ impl GraphConstructionEdgeData { } #[wasm_bindgen(js_name = toString)] - pub fn to_string(&self) -> String { + pub fn to_fancy_string(&self) -> String { format!("{:#?}", self) } } diff --git a/wasm/src/graph_data.rs b/wasm/src/graph_data.rs new file mode 100644 index 00000000..2db9fcca --- /dev/null +++ b/wasm/src/graph_data.rs @@ -0,0 +1,267 @@ +use std::fmt::Debug; + +use petgraph::{ + graph::{EdgeIndex, NodeIndex}, + stable_graph::EdgeReference, +}; +use wasm_bindgen::prelude::*; + +use crate::{graph::edge_matches_edge_filter, graph_construction::GraphConstructionNodeData}; + +pub type NGEdgeIndex = EdgeIndex; +pub type NGNodeIndex = NodeIndex; +pub type NGEdgeRef<'a> = EdgeReference<'a, EdgeData, u32>; + +#[wasm_bindgen] +#[derive(Clone, Debug, PartialEq)] +pub struct EdgeData { + #[wasm_bindgen(skip)] + pub edge_type: String, + #[wasm_bindgen(skip)] + pub edge_source: String, + #[wasm_bindgen(skip)] + pub implied: bool, + #[wasm_bindgen(skip)] + pub round: u8, +} + +#[wasm_bindgen] +impl EdgeData { + #[wasm_bindgen(constructor)] + pub fn new(edge_type: String, edge_source: String, implied: bool, round: u8) -> EdgeData { + EdgeData { + edge_type, + edge_source, + implied, + round, + } + } + + #[wasm_bindgen(getter)] + pub fn edge_type(&self) -> String { + self.edge_type.clone() + } + + #[wasm_bindgen(getter)] + pub fn edge_source(&self) -> String { + self.edge_source.clone() + } + + #[wasm_bindgen(getter)] + pub fn implied(&self) -> bool { + self.implied + } + + #[wasm_bindgen(getter)] + pub fn round(&self) -> u8 { + self.round + } + + #[wasm_bindgen(js_name = toString)] + pub fn to_fancy_string(&self) -> String { + format!("{:#?}", self) + } +} + +impl EdgeData { + pub fn get_attribute_label(&self, attributes: &Vec) -> String { + let mut result = vec![]; + + // the mapping that exist on the JS side are as follows + // "field" | "explicit" | "source" | "implied_kind" | "round" + + // TODO: maybe change the attribute options so that the JS side better matches the data + + for attribute in attributes { + let data = match attribute.as_str() { + "field" => Some(("field", self.edge_type.clone())), + "explicit" => Some(("explicit", (!self.implied).to_string())), + "source" => { + if !self.implied { + Some(("source", self.edge_source.clone())) + } else { + None + } + } + "implied_kind" => { + if self.implied { + Some(("implied_kind", self.edge_source.clone())) + } else { + None + } + } + "round" => Some(("round", self.round.to_string())), + _ => None, + }; + + if let Some(data) = data { + result.push(format!("{}={}", data.0, data.1)); + } + } + + result.join(" ") + } +} + +// impl PartialEq for EdgeData { +// fn eq(&self, other: &Self) -> bool { +// self.edge_type == other.edge_type +// } +// } + +#[wasm_bindgen] +#[derive(Clone, Debug, PartialEq)] +pub struct NodeData { + #[wasm_bindgen(skip)] + pub path: String, + #[wasm_bindgen(skip)] + pub aliases: Vec, + #[wasm_bindgen(skip)] + pub resolved: bool, + #[wasm_bindgen(skip)] + pub ignore_in_edges: bool, + #[wasm_bindgen(skip)] + pub ignore_out_edges: bool, +} + +#[wasm_bindgen] +impl NodeData { + #[wasm_bindgen(constructor)] + pub fn new( + path: String, + aliases: Vec, + resolved: bool, + ignore_in_edges: bool, + ignore_out_edges: bool, + ) -> NodeData { + NodeData { + path, + aliases, + resolved, + ignore_in_edges, + ignore_out_edges, + } + } + + #[wasm_bindgen(getter)] + pub fn path(&self) -> String { + self.path.clone() + } + + #[wasm_bindgen(getter)] + pub fn aliases(&self) -> Vec { + self.aliases.clone() + } + + #[wasm_bindgen(getter)] + pub fn resolved(&self) -> bool { + self.resolved + } + + #[wasm_bindgen(getter)] + pub fn ignore_in_edges(&self) -> bool { + self.ignore_in_edges + } + + #[wasm_bindgen(getter)] + pub fn ignore_out_edges(&self) -> bool { + self.ignore_out_edges + } + + #[wasm_bindgen(js_name = toString)] + pub fn to_fancy_string(&self) -> String { + format!("{:#?}", self) + } +} + +impl NodeData { + pub fn from_construction_data(data: &GraphConstructionNodeData) -> NodeData { + NodeData { + path: data.path.clone(), + aliases: data.aliases.clone(), + resolved: data.resolved, + ignore_in_edges: data.ignore_in_edges, + ignore_out_edges: data.ignore_out_edges, + } + } + + pub fn new_unresolved(path: String) -> NodeData { + NodeData { + path, + aliases: Vec::new(), + resolved: false, + ignore_in_edges: false, + ignore_out_edges: false, + } + } +} + +#[wasm_bindgen] +#[derive(Clone, Debug, PartialEq)] +pub struct EdgeStruct { + #[wasm_bindgen(skip)] + pub source: NodeData, + #[wasm_bindgen(skip)] + pub target: NodeData, + #[wasm_bindgen(skip)] + pub edge: EdgeData, +} + +#[wasm_bindgen] +impl EdgeStruct { + #[wasm_bindgen(constructor)] + pub fn new(source: NodeData, target: NodeData, edge: EdgeData) -> EdgeStruct { + EdgeStruct { + source, + target, + edge, + } + } + + #[wasm_bindgen(getter)] + pub fn source(&self) -> NodeData { + self.source.clone() + } + + #[wasm_bindgen(getter)] + pub fn target(&self) -> NodeData { + self.target.clone() + } + + #[wasm_bindgen(getter)] + pub fn edge_type(&self) -> String { + self.edge.edge_type.clone() + } + + #[wasm_bindgen(getter)] + pub fn edge_source(&self) -> String { + self.edge.edge_source.clone() + } + + #[wasm_bindgen(getter)] + pub fn implied(&self) -> bool { + self.edge.implied + } + + #[wasm_bindgen(getter)] + pub fn round(&self) -> u8 { + self.edge.round + } + + pub fn get_attribute_label(&self, attributes: Vec) -> String { + self.edge.get_attribute_label(&attributes) + } + + pub fn matches_edge_filter(&self, edge_types: Option>) -> bool { + edge_matches_edge_filter(&self.edge, edge_types.as_ref()) + } + + pub fn is_self_loop(&self) -> bool { + self.source.path == self.target.path + } + + #[wasm_bindgen(js_name = toString)] + pub fn to_fancy_string(&self) -> String { + format!("{:#?}", self) + } +} diff --git a/wasm/src/graph_mermaid.rs b/wasm/src/graph_mermaid.rs index 769617f3..cc45ded6 100644 --- a/wasm/src/graph_mermaid.rs +++ b/wasm/src/graph_mermaid.rs @@ -1,16 +1,25 @@ use std::collections::HashMap; use itertools::{EitherOrBoth, Itertools}; -use petgraph::{stable_graph::NodeIndex, visit::EdgeRef}; +use petgraph::{ + stable_graph::{EdgeReference, NodeIndex}, + visit::EdgeRef, +}; use wasm_bindgen::prelude::*; use web_time::Instant; use crate::{ - graph::{EdgeData, NoteGraph}, - graph_traversal::TraversalOptions, + graph::NoteGraph, + graph_data::EdgeData, + graph_traversal::{EdgeVec, TraversalOptions}, utils::{NoteGraphError, Result}, }; +pub type AccumulatedEdgeHashMap = HashMap< + (NodeIndex, NodeIndex), + (NodeIndex, NodeIndex, Vec, Vec), +>; + #[wasm_bindgen] #[derive(Clone, Debug)] pub struct MermaidGraphOptions { @@ -47,7 +56,7 @@ impl MermaidGraphOptions { } #[wasm_bindgen(js_name = toString)] - pub fn to_string(&self) -> String { + pub fn to_fancy_string(&self) -> String { format!("{:#?}", self) } } @@ -78,7 +87,7 @@ impl MermaidGraphData { } #[wasm_bindgen(js_name = toString)] - pub fn to_string(&self) -> String { + pub fn to_fancy_string(&self) -> String { format!("{:#?}", self) } } @@ -102,14 +111,14 @@ impl NoteGraph { ) -> Result { let now = Instant::now(); - let (node_map, edge_map) = self.int_traverse_basic(&traversal_options)?; + let (nodes, edges) = self.int_traverse_basic(&traversal_options)?; let traversal_elapsed = now.elapsed(); let mut result = String::new(); result.push_str(&diagram_options.init_line); - result.push_str("\n"); + result.push('\n'); result.push_str( format!( "{} {}\n", @@ -119,48 +128,13 @@ impl NoteGraph { ); // accumulate edges by direction, so that we can collapse them in the next step - let mut accumulated_edges: HashMap< - (NodeIndex, NodeIndex), - (NodeIndex, NodeIndex, Vec, Vec), - > = HashMap::new(); - - for (_, edge_ref) in edge_map { - let forward_dir = (edge_ref.source(), edge_ref.target()); - - let entry1 = accumulated_edges.get_mut(&forward_dir); - match entry1 { - Some((_, _, forward, _)) => { - forward.push(edge_ref.weight().clone()); - } - None => { - let backward_dir = (edge_ref.target(), edge_ref.source()); - - let entry2 = accumulated_edges.get_mut(&backward_dir); - match entry2 { - Some((_, _, _, backward)) => { - backward.push(edge_ref.weight().clone()); - } - None => { - accumulated_edges.insert( - forward_dir, - ( - edge_ref.source(), - edge_ref.target(), - vec![edge_ref.weight().clone()], - Vec::new(), - ), - ); - } - } - } - } - } + let accumulated_edges = NoteGraph::int_accumulate_edges(edges); let mut unresolved_nodes = Vec::new(); // add nodes to the graph - for element in node_map.iter() { - let weight = self.int_get_node_weight(element.0.clone())?; + for element in nodes.iter() { + let weight = self.int_get_node_weight(element.0)?; let node_label = match diagram_options.node_label_fn { Some(ref function) => { @@ -218,10 +192,10 @@ impl NoteGraph { result.push_str(&format!("class {} BC-active-node\n", index.index())); } - if !node_map.is_empty() { + if !nodes.is_empty() { result.push_str(&format!( "class {} internal-link\n", - node_map.iter().map(|(index, _)| index.index()).join(",") + nodes.iter().map(|(index, _)| index.index()).join(",") )); } @@ -309,4 +283,44 @@ impl NoteGraph { ) } } + + pub fn int_accumulate_edges( + edges: EdgeVec>, + ) -> AccumulatedEdgeHashMap { + let mut accumulated_edges: AccumulatedEdgeHashMap = HashMap::new(); + + for (_, edge_ref) in edges { + let forward_dir = (edge_ref.source(), edge_ref.target()); + + let entry1 = accumulated_edges.get_mut(&forward_dir); + match entry1 { + Some((_, _, forward, _)) => { + forward.push(edge_ref.weight().clone()); + } + None => { + let backward_dir = (edge_ref.target(), edge_ref.source()); + + let entry2 = accumulated_edges.get_mut(&backward_dir); + match entry2 { + Some((_, _, _, backward)) => { + backward.push(edge_ref.weight().clone()); + } + None => { + accumulated_edges.insert( + forward_dir, + ( + edge_ref.source(), + edge_ref.target(), + vec![edge_ref.weight().clone()], + Vec::new(), + ), + ); + } + } + } + } + } + + accumulated_edges + } } diff --git a/wasm/src/graph_rules.rs b/wasm/src/graph_rules.rs index 509303cf..e6113c8a 100644 --- a/wasm/src/graph_rules.rs +++ b/wasm/src/graph_rules.rs @@ -42,7 +42,7 @@ impl TransitiveGraphRule { } #[wasm_bindgen(js_name = toString)] - pub fn to_string(&self) -> String { + pub fn to_fancy_string(&self) -> String { format!("{:#?}", self) } } diff --git a/wasm/src/graph_traversal.rs b/wasm/src/graph_traversal.rs index 72113592..0363f3ff 100644 --- a/wasm/src/graph_traversal.rs +++ b/wasm/src/graph_traversal.rs @@ -1,22 +1,24 @@ use std::collections::HashSet; -use petgraph::{ - stable_graph::{EdgeIndex, EdgeReference, NodeIndex}, - visit::EdgeRef, -}; +use petgraph::visit::EdgeRef; use wasm_bindgen::prelude::*; use web_time::Instant; use crate::{ - graph::{EdgeData, EdgeStruct, NoteGraph}, + graph::NoteGraph, + graph_data::{EdgeStruct, NGEdgeIndex, NGEdgeRef, NGNodeIndex}, utils::{ - self, BreadthFirstTraversalDataStructure, DepthFirstTraversalDataStructure, + BreadthFirstTraversalDataStructure, DepthFirstTraversalDataStructure, GraphTraversalDataStructure, NoteGraphError, Result, }, }; const TRAVERSAL_COUNT_LIMIT: u32 = 10_000; +pub type NodeVec = Vec<(NGNodeIndex, T)>; +pub type EdgeVec = Vec<(NGEdgeIndex, T)>; +pub type NodeEdgeVec = (NodeVec, EdgeVec); + #[wasm_bindgen] #[derive(Clone, Debug)] pub struct TraversalOptions { @@ -67,7 +69,7 @@ impl TraversalOptions { } #[wasm_bindgen(js_name = toString)] - pub fn to_string(&self) -> String { + pub fn to_fancy_string(&self) -> String { format!("{:#?}", self) } } @@ -109,7 +111,7 @@ impl Path { } #[wasm_bindgen(js_name = toString)] - pub fn to_string(&self) -> String { + pub fn to_fancy_string(&self) -> String { format!("{:#?}", self) } } @@ -175,7 +177,7 @@ impl RecTraversalData { } #[wasm_bindgen(js_name = toString)] - pub fn to_string(&self) -> String { + pub fn to_fancy_string(&self) -> String { format!("{:#?}", self) } } @@ -253,7 +255,7 @@ impl RecTraversalResult { } #[wasm_bindgen(js_name = toString)] - pub fn to_string(&self) -> String { + pub fn to_fancy_string(&self) -> String { format!("{:#?}", self) } @@ -288,7 +290,7 @@ impl NoteGraph { for entry_node in &options.entry_nodes { let start_node = self - .int_get_node_index(&entry_node) + .int_get_node_index(entry_node) .ok_or(NoteGraphError::new("Node not found"))?; let start_node_weight = self.int_get_node_weight(start_node)?; @@ -330,8 +332,8 @@ impl NoteGraph { } } - let node_count = self.int_rec_count_children(&mut result); - let max_depth = self.int_rec_max_depth(&result); + let node_count = NoteGraph::int_rec_traversal_data_count_children(&mut result); + let max_depth = NoteGraph::int_rec_traversal_data_max_depth(&result); let total_elapsed = now.elapsed(); @@ -350,7 +352,7 @@ impl NoteGraph { /// Will return an error if the node weight for any node along the traversal is not found. pub fn int_rec_traverse( &self, - node: NodeIndex, + node: NGNodeIndex, edge: EdgeStruct, edge_types: Option<&Vec>, depth: u32, @@ -363,7 +365,7 @@ impl NoteGraph { for outgoing_edge in self.graph.edges(node) { let edge_data = outgoing_edge.weight(); - if self.int_edge_matches_edge_filter(&edge_data, edge_types) { + if self.int_edge_matches_edge_filter(edge_data, edge_types) { let target = outgoing_edge.target(); // assert!(*self.int_get_node_weight(node).unwrap() == edge.target); @@ -395,20 +397,26 @@ impl NoteGraph { Ok(RecTraversalData::new(edge, depth, 0, new_children)) } - pub fn int_rec_count_children(&self, data: &mut Vec) -> u32 { + pub fn int_rec_traversal_data_count_children(data: &mut [RecTraversalData]) -> u32 { let mut total_children = 0; for datum in data.iter_mut() { - datum.number_of_children = self.int_rec_count_children(&mut datum.children); + datum.number_of_children = + NoteGraph::int_rec_traversal_data_count_children(&mut datum.children); total_children += 1 + datum.number_of_children; } total_children } - pub fn int_rec_max_depth(&self, data: &Vec) -> u32 { + pub fn int_rec_traversal_data_max_depth(data: &[RecTraversalData]) -> u32 { data.iter() - .map(|datum| u32::max(self.int_rec_max_depth(&datum.children), datum.depth)) + .map(|datum| { + u32::max( + NoteGraph::int_rec_traversal_data_max_depth(&datum.children), + datum.depth, + ) + }) .max() .unwrap_or(0) } @@ -416,10 +424,7 @@ impl NoteGraph { pub fn int_traverse_basic( &self, options: &TraversalOptions, - ) -> Result<( - Vec<(NodeIndex, u32)>, - Vec<(EdgeIndex, EdgeReference)>, - )> { + ) -> Result> { let entry_nodes = options .entry_nodes .iter() @@ -427,8 +432,7 @@ impl NoteGraph { self.int_get_node_index(node) .ok_or(NoteGraphError::new("Node not found")) }) - .into_iter() - .collect::>>>()?; + .collect::>>()?; if options.separate_edges { let mut node_list = Vec::new(); @@ -468,14 +472,14 @@ impl NoteGraph { /// These lists are ordered by the order in which the nodes and edges were visited. /// Each node and edge is only visited once. /// At the depth limit, edges are only added if the target node is already in the depth map. - pub fn int_traverse_depth_first<'a, T, S>( + pub fn int_traverse_depth_first<'a, N, E>( &'a self, - entry_nodes: Vec>, + entry_nodes: Vec, edge_types: Option<&Vec>, max_depth: u32, - node_callback: fn(NodeIndex, u32) -> T, - edge_callback: fn(EdgeReference<'a, EdgeData, u32>) -> S, - ) -> (Vec<(NodeIndex, T)>, Vec<(EdgeIndex, S)>) { + node_callback: fn(NGNodeIndex, u32) -> N, + edge_callback: fn(NGEdgeRef<'a>) -> E, + ) -> NodeEdgeVec { let mut data_structure = DepthFirstTraversalDataStructure::new(); self.int_traverse_generic( @@ -494,14 +498,14 @@ impl NoteGraph { /// These lists are ordered by the order in which the nodes and edges were visited. /// Each node and edge is only visited once. /// At the depth limit, edges are only added if the target node is already in the depth map. - pub fn int_traverse_breadth_first<'a, T, S>( + pub fn int_traverse_breadth_first<'a, N, E>( &'a self, - entry_nodes: Vec>, + entry_nodes: Vec, edge_types: Option<&Vec>, max_depth: u32, - node_callback: fn(NodeIndex, u32) -> T, - edge_callback: fn(EdgeReference<'a, EdgeData, u32>) -> S, - ) -> (Vec<(NodeIndex, T)>, Vec<(EdgeIndex, S)>) { + node_callback: fn(NGNodeIndex, u32) -> N, + edge_callback: fn(NGEdgeRef<'a>) -> E, + ) -> NodeEdgeVec { let mut data_structure = BreadthFirstTraversalDataStructure::new(); self.int_traverse_generic( @@ -514,18 +518,18 @@ impl NoteGraph { ) } - pub fn int_traverse_generic<'a, T, S>( + pub fn int_traverse_generic<'a, N, E>( &'a self, - traversal_data_structure: &mut impl GraphTraversalDataStructure<(NodeIndex, u32)>, - entry_nodes: Vec>, + traversal_data_structure: &mut impl GraphTraversalDataStructure<(NGNodeIndex, u32)>, + entry_nodes: Vec, edge_types: Option<&Vec>, max_depth: u32, - node_callback: fn(NodeIndex, u32) -> T, - edge_callback: fn(EdgeReference<'a, EdgeData, u32>) -> S, - ) -> (Vec<(NodeIndex, T)>, Vec<(EdgeIndex, S)>) { - let mut node_list: Vec<(NodeIndex, T)> = Vec::new(); - let mut edge_list: Vec<(EdgeIndex, S)> = Vec::new(); - let mut visited_nodes: HashSet> = HashSet::new(); + node_callback: fn(NGNodeIndex, u32) -> N, + edge_callback: fn(NGEdgeRef<'a>) -> E, + ) -> NodeEdgeVec { + let mut node_list: NodeVec = Vec::new(); + let mut edge_list: EdgeVec = Vec::new(); + let mut visited_nodes: HashSet = HashSet::new(); for node in entry_nodes { node_list.push((node, node_callback(node, 0))); @@ -546,7 +550,7 @@ impl NoteGraph { // we only add the edge if we are not at the depth limit or if we are at the depth limit and the target node is already in the depth map // this captures all the outgoing edges from the nodes at the depth limit to nodes already present in the depth map - if !at_depth_limit || (at_depth_limit && already_visited) { + if !at_depth_limit || already_visited { edge_list.push((edge.id(), edge_callback(edge))); } diff --git a/wasm/src/graph_update.rs b/wasm/src/graph_update.rs index 4986dc48..c6d0c2dc 100644 --- a/wasm/src/graph_update.rs +++ b/wasm/src/graph_update.rs @@ -20,6 +20,12 @@ impl BatchGraphUpdate { } } +impl Default for BatchGraphUpdate { + fn default() -> Self { + Self::new() + } +} + impl BatchGraphUpdate { pub fn add_update(&mut self, update: Box) { self.updates.push(update); @@ -135,12 +141,13 @@ impl AddEdgeGraphUpdate { impl GraphUpdate for AddEdgeGraphUpdate { fn apply(&self, graph: &mut NoteGraph) -> Result<()> { - Ok(graph.int_safe_add_edge( + graph.int_safe_add_edge( &self.source, &self.target, &self.edge_type, &self.edge_source, - )) + ); + Ok(()) } } diff --git a/wasm/src/lib.rs b/wasm/src/lib.rs index d2a71b2a..456a1521 100644 --- a/wasm/src/lib.rs +++ b/wasm/src/lib.rs @@ -1,5 +1,6 @@ pub mod graph; pub mod graph_construction; +pub mod graph_data; pub mod graph_mermaid; pub mod graph_rules; pub mod graph_traversal; @@ -8,15 +9,11 @@ pub mod utils; use wasm_bindgen::prelude::*; -const DEBUG: bool = false; - #[wasm_bindgen] pub fn create_graph() -> graph::NoteGraph { // utils::log_str("Hello, from WASM!"); console_error_panic_hook::set_once(); - let graph = graph::NoteGraph::new(); - - return graph; + graph::NoteGraph::new() } diff --git a/wasm/src/utils.rs b/wasm/src/utils.rs index 5393b142..c34b8e66 100644 --- a/wasm/src/utils.rs +++ b/wasm/src/utils.rs @@ -1,5 +1,5 @@ use petgraph::visit::{EdgeRef, IntoEdgeReferences, IntoNodeReferences}; -use std::{collections::VecDeque, fmt}; +use std::{collections::VecDeque, error::Error, fmt}; use wasm_bindgen::prelude::*; #[wasm_bindgen] @@ -36,8 +36,8 @@ impl NoteGraphError { } #[wasm_bindgen(js_name = toString)] - pub fn to_string(&self) -> String { - format!("{:?}", self) + pub fn to_fancy_string(&self) -> String { + format!("{:#?}", self) } } @@ -47,6 +47,8 @@ impl fmt::Display for NoteGraphError { } } +impl Error for NoteGraphError {} + pub struct DepthFirstTraversalDataStructure { stack: Vec, } From 5ba7883ed61bca64dd9fa56716ac579bd8f0b9d1 Mon Sep 17 00:00:00 2001 From: Moritz Jung Date: Sat, 4 May 2024 01:34:37 +0200 Subject: [PATCH 20/65] fixes; rust sorting; settings rule previews --- src/commands/freeze_edges/index.ts | 8 +- src/commands/list_index/index.ts | 6 +- src/components/NestedEdgeList.svelte | 5 +- .../codeblocks/CodeblockMermaid.svelte | 35 +-- .../codeblocks/CodeblockTree.svelte | 53 +++-- .../TransitiveImpliedRelations.svelte | 13 +- src/components/side_views/Matrix.svelte | 6 +- .../side_views/MatrixEdgeField.svelte | 12 +- src/components/side_views/TreeView.svelte | 30 +-- src/graph/builders/index.ts | 5 - src/graph/traverse.ts | 15 +- src/utils/mermaid.ts | 41 ++++ wasm/.cargo/config.toml | 2 + wasm/Cargo.toml | 1 - wasm/src/edge_sorting.rs | 203 ++++++++++++++++++ wasm/src/graph.rs | 11 +- wasm/src/graph_data.rs | 82 ++++++- wasm/src/graph_mermaid.rs | 14 +- wasm/src/graph_rules.rs | 46 ++++ wasm/src/graph_traversal.rs | 29 ++- wasm/src/lib.rs | 1 + 21 files changed, 502 insertions(+), 116 deletions(-) create mode 100644 wasm/.cargo/config.toml create mode 100644 wasm/src/edge_sorting.rs diff --git a/src/commands/freeze_edges/index.ts b/src/commands/freeze_edges/index.ts index 1973b524..ada96549 100644 --- a/src/commands/freeze_edges/index.ts +++ b/src/commands/freeze_edges/index.ts @@ -11,12 +11,8 @@ export const freeze_implied_edges_to_note = async ( options: BreadcrumbsSettings["commands"]["freeze_implied_edges"]["default_options"], ) => { const implied_edges = plugin.graph.get_outgoing_edges(source_file.path).filter( - (e) => - // Don't freeze a note to itself (self_is_sibling) - !e.is_self_loop() && - e.implied // && - // If field === null, we don't have an opposite field to freeze to - // e.attr.field !== null, + // Don't freeze a note to itself (self_is_sibling) + (e) => !e.is_self_loop() && e.implied, ); await drop_crumbs(plugin, source_file, implied_edges, options); diff --git a/src/commands/list_index/index.ts b/src/commands/list_index/index.ts index d6e6f2ac..8758be3b 100644 --- a/src/commands/list_index/index.ts +++ b/src/commands/list_index/index.ts @@ -2,13 +2,12 @@ import type { EdgeSortId } from "src/const/graph"; import type { EdgeAttribute } from "src/graph/MyMultiGraph"; import { Traverse } from "src/graph/traverse"; import { - get_edge_sorter, stringify_node, } from "src/graph/utils"; import type { LinkKind } from "src/interfaces/links"; import type { ShowNodeOptions } from "src/interfaces/settings"; import { Links } from "src/utils/links"; -import { TraversalOptions, type NoteGraph, type RecTraversalData } from "wasm/pkg/breadcrumbs_graph_wasm"; +import { TraversalOptions, create_edge_sorter, type NoteGraph, type RecTraversalData } from "wasm/pkg/breadcrumbs_graph_wasm"; export namespace ListIndex { export type Options = { @@ -87,8 +86,9 @@ export namespace ListIndex { // Traverse.build_tree(graph, start_node, options, (e) => // has_edge_attrs(e, { $or_fields: options.fields }), // ), + graph, graph.rec_traverse(traversal_options).data, - get_edge_sorter(options.edge_sort_id), + create_edge_sorter(options.edge_sort_id.field, options.edge_sort_id.order === -1), ), options, ); diff --git a/src/components/NestedEdgeList.svelte b/src/components/NestedEdgeList.svelte index 53fae124..92c3cb62 100644 --- a/src/components/NestedEdgeList.svelte +++ b/src/components/NestedEdgeList.svelte @@ -1,12 +1,11 @@ -{#each tree.sort((a, b) => sort(a.edge, b.edge)) as item, i} +{#each sort_traversal_data(plugin.graph, tree, sort) as item, i}
{#if item.children.length} diff --git a/src/components/codeblocks/CodeblockMermaid.svelte b/src/components/codeblocks/CodeblockMermaid.svelte index be3f285e..0b6b6b1e 100644 --- a/src/components/codeblocks/CodeblockMermaid.svelte +++ b/src/components/codeblocks/CodeblockMermaid.svelte @@ -10,7 +10,7 @@ import MermaidDiagram from "../Mermaid/MermaidDiagram.svelte"; import CopyToClipboardButton from "../button/CopyToClipboardButton.svelte"; import CodeblockErrors from "./CodeblockErrors.svelte"; - import { MermaidGraphOptions, NodeData, TraversalOptions } from "wasm/pkg/breadcrumbs_graph_wasm"; + import { MermaidGraphOptions, NodeData, NoteGraphError, TraversalOptions } from "wasm/pkg/breadcrumbs_graph_wasm"; import { remove_nullish_keys } from "src/utils/objects"; import { Paths } from "src/utils/paths"; import { Links } from "src/utils/links"; @@ -21,10 +21,9 @@ export let file_path: string; let mermaid: string = ""; + let error: NoteGraphError | undefined = undefined; export const update = () => { - // TODO pass all the options and then implement them all the options on the rust side - const max_depth = options.depth[1] ?? 100; const traversal_options = new TraversalOptions( @@ -63,21 +62,25 @@ ), ); } - } + }, + true, ); - log.debug(traversal_options.toString()); - log.debug(mermaid_options.toString()); + try { + mermaid = plugin.graph.generate_mermaid_graph(traversal_options, mermaid_options).mermaid; + error = undefined; + } catch (e) { + log.error("Error generating mermaid graph", e); - mermaid = plugin.graph.generate_mermaid_graph(traversal_options, mermaid_options).mermaid; + if (e instanceof NoteGraphError) { + mermaid = ""; + error = e; + } + } }; onMount(() => { - try { - update(); - } catch (e) { - log.warn("Error updating codeblock mermaid", e); - } + update(); }); // const sort = get_edge_sorter( @@ -212,7 +215,11 @@
{:else} - -

No paths found.

+ {#if error} +

{error.message}

+ {:else} + +

No paths found.

+ {/if} {/if}
diff --git a/src/components/codeblocks/CodeblockTree.svelte b/src/components/codeblocks/CodeblockTree.svelte index 5b7ed96c..28c6733b 100644 --- a/src/components/codeblocks/CodeblockTree.svelte +++ b/src/components/codeblocks/CodeblockTree.svelte @@ -1,16 +1,13 @@ @@ -48,7 +54,7 @@
{#key sort} - {#each edges.sort(sort) as edge} + {#each edges as edge}
diff --git a/src/components/side_views/TreeView.svelte b/src/components/side_views/TreeView.svelte index af081cb9..b87794d1 100644 --- a/src/components/side_views/TreeView.svelte +++ b/src/components/side_views/TreeView.svelte @@ -1,7 +1,4 @@ -{#each sort_traversal_data(plugin.graph, tree, sort) as item, i} +{#each tree as item, i}
{#if item.children.length} @@ -56,7 +54,6 @@ {#if item.children.length}
{ @@ -40,12 +40,14 @@ try { tree = plugin.graph.rec_traverse( traversal_options - ).data; + ); + tree.sort(plugin.graph, sort); + error = undefined; } catch (e) { log.error("Error updating codeblock tree", e); if (e instanceof NoteGraphError) { - tree = []; + tree = undefined; error = e; } } @@ -123,12 +125,12 @@ {/if} - {#if tree.length} + {#if tree && !tree.is_empty()}
e.matches_edge_filter(edge_field_labels)), - (e) => e.edge_type, - ) + ? plugin.graph.get_filtered_grouped_outgoing_edges($active_file_store.path, edge_field_labels) : null; @@ -97,13 +92,11 @@ {#key grouped_out_edges} {#if grouped_out_edges}
- {#each plugin.settings.edge_fields as field} - {@const edges = grouped_out_edges[field.label]} + {@const edges = grouped_out_edges.get_sorted_edges(field.label, plugin.graph, sort)} {#if edges?.length} { - edges = sort_edges(plugin.graph, edges, _sort); - }; - let open = true; @@ -53,7 +45,7 @@
- {#key sort} + {#key edges} {#each edges as edge}
diff --git a/src/components/side_views/TreeView.svelte b/src/components/side_views/TreeView.svelte index b87794d1..749e718b 100644 --- a/src/components/side_views/TreeView.svelte +++ b/src/components/side_views/TreeView.svelte @@ -32,16 +32,18 @@ new TraversalOptions( [$active_file_store!.path], edge_field_labels, - 100, + 20, !merge_fields, ), - ).data - : []; + ) + : undefined; $: sort = create_edge_sorter( edge_sort_id.field, edge_sort_id.order === -1, - );; + ); + + $: tree?.sort(plugin.graph, sort); // const base_traversal = (attr: EdgeAttrFilters) => // Traverse.build_tree( @@ -105,14 +107,13 @@
{#key tree || sort} - {#if tree.length} + {#if tree && !tree.is_empty()} {:else}
No paths found
diff --git a/wasm/src/edge_sorting.rs b/wasm/src/edge_sorting.rs index ef089937..2027a7b9 100644 --- a/wasm/src/edge_sorting.rs +++ b/wasm/src/edge_sorting.rs @@ -5,7 +5,7 @@ use crate::{ graph::NoteGraph, graph_data::EdgeStruct, graph_traversal::RecTraversalData, - utils::{self, NoteGraphError, Result}, + utils::{NoteGraphError, Result}, }; #[derive(Clone, Debug)] diff --git a/wasm/src/graph.rs b/wasm/src/graph.rs index af9a658d..7c3a076b 100644 --- a/wasm/src/graph.rs +++ b/wasm/src/graph.rs @@ -11,7 +11,9 @@ use web_time::Instant; use crate::{ graph_construction::{GraphConstructionEdgeData, GraphConstructionNodeData}, - graph_data::{EdgeData, EdgeStruct, NGEdgeIndex, NGEdgeRef, NGNodeIndex, NodeData}, + graph_data::{ + EdgeData, EdgeStruct, GroupedEdgeList, NGEdgeIndex, NGEdgeRef, NGNodeIndex, NodeData, + }, graph_rules::TransitiveGraphRule, graph_update::BatchGraphUpdate, utils::{self, NoteGraphError, Result}, @@ -163,6 +165,23 @@ impl NoteGraph { } } + pub fn get_filtered_grouped_outgoing_edges( + &self, + node: String, + edge_types: Option>, + ) -> GroupedEdgeList { + let node_index = self.int_get_node_index(&node); + + GroupedEdgeList::from_vec(match node_index { + Some(node_index) => self + .int_iter_outgoing_edges(node_index) + .filter_map(|edge| self.int_edge_ref_to_struct(edge)) + .filter(|edge| edge_matches_edge_filter(&edge.edge, edge_types.as_ref())) + .collect(), + None => Vec::new(), + }) + } + pub fn get_incoming_edges(&self, node: String) -> Vec { let node_index = self.int_get_node_index(&node); diff --git a/wasm/src/graph_data.rs b/wasm/src/graph_data.rs index eae3a4ce..6ac2e6bb 100644 --- a/wasm/src/graph_data.rs +++ b/wasm/src/graph_data.rs @@ -1,4 +1,4 @@ -use std::fmt::Debug; +use std::{collections::HashMap, fmt::Debug}; use petgraph::{ graph::{EdgeIndex, NodeIndex}, @@ -8,7 +8,9 @@ use petgraph::{ use wasm_bindgen::prelude::*; use crate::{ - graph::edge_matches_edge_filter, graph_construction::GraphConstructionNodeData, utils::Result, + edge_sorting::EdgeSorter, + graph::{edge_matches_edge_filter, NoteGraph}, + graph_construction::GraphConstructionNodeData, }; pub type NGEdgeIndex = EdgeIndex; @@ -327,3 +329,102 @@ impl EdgeStruct { )) } } + +#[wasm_bindgen] +#[derive(Clone, Debug, PartialEq)] +pub struct EdgeList { + #[wasm_bindgen(skip)] + pub edges: Vec, +} + +#[wasm_bindgen] +impl EdgeList { + #[wasm_bindgen(constructor)] + pub fn new() -> EdgeList { + EdgeList { edges: Vec::new() } + } + + pub fn get_edges(&self) -> Vec { + self.edges.clone() + } + + pub fn get_sorted_edges(&self, graph: &NoteGraph, sorter: &EdgeSorter) -> Vec { + let mut edges = self.edges.clone(); + sorter.sort_edges(graph, &mut edges); + + edges + } + + pub fn group_by_type(&self) -> GroupedEdgeList { + let mut grouped_edges = GroupedEdgeList::new(); + + for edge in &self.edges { + grouped_edges.add_edge(edge.clone()); + } + + grouped_edges + } + + #[wasm_bindgen(js_name = toString)] + pub fn to_fancy_string(&self) -> String { + format!("{:#?}", self) + } +} + +impl EdgeList { + pub fn from_vec(edges: Vec) -> EdgeList { + EdgeList { edges } + } +} + +#[wasm_bindgen] +pub struct GroupedEdgeList { + #[wasm_bindgen(skip)] + pub edges: HashMap, +} + +#[wasm_bindgen] +impl GroupedEdgeList { + pub fn new() -> GroupedEdgeList { + GroupedEdgeList { + edges: HashMap::new(), + } + } + + pub fn from_edge_list(edge_list: EdgeList) -> GroupedEdgeList { + GroupedEdgeList::from_vec(edge_list.edges) + } + + pub fn from_vec(edge_list: Vec) -> GroupedEdgeList { + let mut grouped_edges = GroupedEdgeList::new(); + + for edge in edge_list { + grouped_edges.add_edge(edge); + } + + grouped_edges + } + + pub fn add_edge(&mut self, edge_struct: EdgeStruct) { + let edge_type = edge_struct.edge.edge_type.clone(); + let edge_list = self.edges.entry(edge_type).or_insert(EdgeList::new()); + edge_list.edges.push(edge_struct); + } + + pub fn get_edges(&self, edge_type: &str) -> Option> { + self.edges + .get(edge_type) + .map(|edge_list| edge_list.get_edges()) + } + + pub fn get_sorted_edges( + &self, + edge_type: &str, + graph: &NoteGraph, + sorter: &EdgeSorter, + ) -> Option> { + self.edges + .get(edge_type) + .map(|edge_list| edge_list.get_sorted_edges(graph, sorter)) + } +} diff --git a/wasm/src/graph_mermaid.rs b/wasm/src/graph_mermaid.rs index d51567c7..e4793ead 100644 --- a/wasm/src/graph_mermaid.rs +++ b/wasm/src/graph_mermaid.rs @@ -12,7 +12,7 @@ use crate::{ graph::NoteGraph, graph_data::EdgeData, graph_traversal::{EdgeVec, TraversalOptions}, - utils::{self, NoteGraphError, Result}, + utils::{NoteGraphError, Result}, }; pub type AccumulatedEdgeHashMap = HashMap< diff --git a/wasm/src/graph_traversal.rs b/wasm/src/graph_traversal.rs index d5a6379e..a4fc6ede 100644 --- a/wasm/src/graph_traversal.rs +++ b/wasm/src/graph_traversal.rs @@ -24,7 +24,7 @@ pub type NodeEdgeVec = (NodeVec, EdgeVec); #[derive(Clone, Debug)] pub struct TraversalOptions { entry_nodes: Vec, - // if this is None, all edge types will be traversed + /// if this is None, all edge types will be traversed edge_types: Option>, max_depth: u32, /// if true, multiple traversals - one for each edge type - will be performed and the results will be combined @@ -130,16 +130,16 @@ impl Path { #[wasm_bindgen] #[derive(Clone, Debug)] pub struct RecTraversalData { - // the edge struct that was traversed + /// the edge struct that was traversed #[wasm_bindgen(skip)] pub edge: EdgeStruct, - // the depth of the node in the traversal + /// the depth of the node in the traversal #[wasm_bindgen(skip)] pub depth: u32, - // the number of total children of the node, so also children of children + /// the number of total children of the node, so also children of children #[wasm_bindgen(skip)] pub number_of_children: u32, - // the children of the node + /// the children of the node #[wasm_bindgen(skip)] pub children: Vec, } @@ -288,6 +288,14 @@ impl RecTraversalResult { paths } + + pub fn sort(&mut self, graph: &NoteGraph, sorter: &EdgeSorter) { + for datum in &mut self.data { + datum.rec_sort_children(graph, sorter); + } + + sorter.sort_traversal_data(graph, &mut self.data); + } } #[wasm_bindgen] @@ -490,7 +498,7 @@ impl NoteGraph { /// A list of tuples of node indices and the result of the node callback and a list of tuples of edge indices and the result of the edge callback are returned. /// These lists are ordered by the order in which the nodes and edges were visited. /// Each node and edge is only visited once. - /// At the depth limit, edges are only added if the target node is already in the depth map. + /// At the depth limit, edges are only visited if they point to already visited nodes. pub fn int_traverse_depth_first<'a, N, E>( &'a self, entry_nodes: Vec, @@ -516,7 +524,7 @@ impl NoteGraph { /// A list of tuples of node indices and the result of the node callback and a list of tuples of edge indices and the result of the edge callback are returned. /// These lists are ordered by the order in which the nodes and edges were visited. /// Each node and edge is only visited once. - /// At the depth limit, edges are only added if the target node is already in the depth map. + /// At the depth limit, edges are only visited if they point to already visited nodes. pub fn int_traverse_breadth_first<'a, N, E>( &'a self, entry_nodes: Vec, From 8452bf89d02f4964c0da159701711be3a34b45ef Mon Sep 17 00:00:00 2001 From: SkepticMystic Date: Tue, 7 May 2024 11:01:55 +0200 Subject: [PATCH 22/65] fix(command:threading): Reimplement --- src/commands/thread/index.ts | 67 +++++++++++++++++++----------------- src/utils/drop_crumb.ts | 26 +++++--------- 2 files changed, 43 insertions(+), 50 deletions(-) diff --git a/src/commands/thread/index.ts b/src/commands/thread/index.ts index d4d8feaa..26df7bb3 100644 --- a/src/commands/thread/index.ts +++ b/src/commands/thread/index.ts @@ -5,6 +5,12 @@ import type BreadcrumbsPlugin from "src/main"; import { drop_crumbs } from "src/utils/drop_crumb"; import { Paths } from "src/utils/paths"; import { resolve_templates } from "src/utils/strings"; +import { + AddEdgeGraphUpdate, + AddNoteGraphUpdate, + BatchGraphUpdate, + GraphConstructionNodeData, +} from "wasm/pkg/breadcrumbs_graph_wasm"; export const thread = async ( plugin: BreadcrumbsPlugin, @@ -12,22 +18,20 @@ export const thread = async ( options: BreadcrumbsSettings["commands"]["thread"]["default_options"], ) => { const active_view = plugin.app.workspace.getActiveViewOfType(MarkdownView); - if (!active_view) return; + if (!active_view) return new Notice("No active markdown view"); const source_file = active_view.file; - if (!source_file) return; + if (!source_file) return new Notice("No active file"); // Resolve the target path template const template_data = { - attr: { - field, - }, + attr: { field }, source: { path: source_file.path, - folder: source_file.parent?.path ?? "", basename: source_file.basename, + folder: source_file.parent?.path ?? "", }, }; - log.info("template_data", template_data); + log.info("thread > template_data", template_data); const target_path = Paths.normalise( Paths.ensure_ext( @@ -46,37 +50,36 @@ export const thread = async ( const msg = `Error creating file "${target_path}". ${error instanceof Error ? error.message : error}`; new Notice(msg); - log.error(msg); + log.error("thread > create file error", msg); return; } - // Drop the crumbs - // NOTE: You technically can Promise.all, but it's safer to create the file first - // TODO - // await drop_crumbs( - // plugin, - // source_file, - // [ - // { - // attr, - // target_id: target_path, - // source_id: source_file.path, - // target_attr: { aliases: [] }, - // }, - // ], - // options, - // ); + // First add the edge so we can access the struct + const batch_update = new BatchGraphUpdate(); + + new AddNoteGraphUpdate( + new GraphConstructionNodeData(target_file.path, [], true, false, false), + ).add_to_batch(batch_update); + + new AddEdgeGraphUpdate( + source_file.path, + target_file.path, + field, + "typed-link", + ).add_to_batch(batch_update); + + plugin.graph.apply_update(batch_update); + + const edge = plugin.graph + .get_outgoing_edges(source_file.path) + .find( + (e) => e.edge_type === field && e.target.path === target_file!.path, + ); + if (!edge) return; - // Open the target file await Promise.all([ - // Let the cache update so that the refresh sees the new file - // NOTE: I half-completed a less-flaky solution by listening to app.metadataCache.on("changed", ...) - // But this only works if Dataview isn't enabled, and I couldn't find the correct event to listen to for Dataview - sleep(500), + drop_crumbs(plugin, source_file, [edge], options), active_view.leaf.openFile(target_file), ]); - - // Wait till the file is created and crumbs are dropped - await plugin.refresh(); }; diff --git a/src/utils/drop_crumb.ts b/src/utils/drop_crumb.ts index b14cb284..d46991af 100644 --- a/src/utils/drop_crumb.ts +++ b/src/utils/drop_crumb.ts @@ -8,26 +8,24 @@ import { group_projection, remove_duplicates, } from "src/utils/arrays"; -import { Paths } from "./paths"; import type { EdgeStruct } from "wasm/pkg/breadcrumbs_graph_wasm"; +import { Paths } from "./paths"; const linkify_edge = ( plugin: BreadcrumbsPlugin, - source_id: string, - target_id: string, - target_aliases: string[] | undefined, + { source, target }: EdgeStruct, ) => { // target_id is a full path - const target_file = plugin.app.vault.getFileByPath(target_id); + const target_file = plugin.app.vault.getFileByPath(target.path); if (!target_file) { - return `[[${Paths.drop_ext(target_id)}]]`; + return `[[${Paths.drop_ext(target.path)}]]`; } else { return plugin.app.fileManager.generateMarkdownLink( target_file, - source_id, + source.path, undefined, - target_aliases?.at(0), + target.aliases?.at(0), ); } }; @@ -39,16 +37,8 @@ export const drop_crumbs = async ( options: { destination: CrumbDestination | "none" }, ) => { const links_by_field = group_projection( - group_by(crumbs, (e) => e.edge_type!), - (edges) => - edges.map((e) => - linkify_edge( - plugin, - e.source.path, - e.target.path, - e.target.aliases, - ), - ), + group_by(crumbs, (e) => e.edge_type), + (edges) => edges.map((e) => linkify_edge(plugin, e)), ); switch (options.destination) { From 8a89247c2defc769a4635900c6be405c7f8940b9 Mon Sep 17 00:00:00 2001 From: SkepticMystic Date: Tue, 7 May 2024 11:52:05 +0200 Subject: [PATCH 23/65] fix(builders): Swap source and target when creating GraphConstructionEdgeData --- src/graph/builders/explicit/date_note.ts | 2 +- src/graph/builders/explicit/folder_note.ts | 2 +- src/graph/builders/explicit/typed_link.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/graph/builders/explicit/date_note.ts b/src/graph/builders/explicit/date_note.ts index 62eafee7..a9207386 100644 --- a/src/graph/builders/explicit/date_note.ts +++ b/src/graph/builders/explicit/date_note.ts @@ -112,8 +112,8 @@ export const _add_explicit_edges_date_note: ExplicitEdgeBuilder = ( results.edges.push( new GraphConstructionEdgeData( - target_id, date_note.path, + target_id, date_note_settings.default_field, "date_note", ), diff --git a/src/graph/builders/explicit/folder_note.ts b/src/graph/builders/explicit/folder_note.ts index 4f450be4..fa977c55 100644 --- a/src/graph/builders/explicit/folder_note.ts +++ b/src/graph/builders/explicit/folder_note.ts @@ -145,8 +145,8 @@ export const _add_explicit_edges_folder_note: ExplicitEdgeBuilder = async ( // We know path is resolved results.edges.push( new GraphConstructionEdgeData( - target_path, folder_note.path, + target_path, data.field, "folder_note", ), diff --git a/src/graph/builders/explicit/typed_link.ts b/src/graph/builders/explicit/typed_link.ts index 6d570ce2..1aefa355 100644 --- a/src/graph/builders/explicit/typed_link.ts +++ b/src/graph/builders/explicit/typed_link.ts @@ -53,8 +53,8 @@ export const _add_explicit_edges_typed_link: ExplicitEdgeBuilder = ( results.edges.push( new GraphConstructionEdgeData( - target_id, source_file.path, + target_id, field, "typed_link", ), From 0262bcddb8413f00d893ae27becfcc53861ca0d0 Mon Sep 17 00:00:00 2001 From: SkepticMystic Date: Tue, 7 May 2024 11:55:16 +0200 Subject: [PATCH 24/65] fix(builders): Rather push an error than show a notice if Dataview is required but missing --- src/codeblocks/index.ts | 3 +-- src/graph/builders/explicit/dataview_note.ts | 14 ++++++++++---- src/graph/builders/explicit/list_note.ts | 15 ++++++++++----- src/graph/builders/explicit/regex_note.ts | 12 ++++++------ src/interfaces/graph.ts | 3 ++- 5 files changed, 29 insertions(+), 18 deletions(-) diff --git a/src/codeblocks/index.ts b/src/codeblocks/index.ts index 98fec71c..5124527a 100644 --- a/src/codeblocks/index.ts +++ b/src/codeblocks/index.ts @@ -1,5 +1,4 @@ import { parseYaml } from "obsidian"; -import type { CodeblockMDRC } from "src/codeblocks/MDRC"; import { dataview_plugin } from "src/external/dataview"; import type { IDataview } from "src/external/dataview/interfaces"; import type { BreadcrumbsError } from "src/interfaces/graph"; @@ -112,7 +111,7 @@ const postprocess_options = ( try { const pages = dataview_plugin .get_api(plugin.app) - ?.pages(parsed["dataview-from"]) as + ?.pages(parsed["dataview-from"], source_path) as | undefined | IDataview.Page[]; diff --git a/src/graph/builders/explicit/dataview_note.ts b/src/graph/builders/explicit/dataview_note.ts index e39e018a..cacff9f4 100644 --- a/src/graph/builders/explicit/dataview_note.ts +++ b/src/graph/builders/explicit/dataview_note.ts @@ -71,13 +71,19 @@ export const _add_explicit_edges_dataview_note: ExplicitEdgeBuilder = ( dataview_note_file.path, ); if (!dataview_note_info.ok) { - if (dataview_note_info.error) + if (dataview_note_info.error) { results.errors.push(dataview_note_info.error); + } return; } else { - new Notice( - "dataview-notes are not implemented without Dataview enabled", - ); + results.errors.push({ + code: "missing_other_plugin", + path: dataview_note_file.path, + message: + "dataview-notes are not implemented without Dataview enabled", + }); + + return; } }, ); diff --git a/src/graph/builders/explicit/list_note.ts b/src/graph/builders/explicit/list_note.ts index ad79415c..7bf1e7f5 100644 --- a/src/graph/builders/explicit/list_note.ts +++ b/src/graph/builders/explicit/list_note.ts @@ -1,4 +1,3 @@ -import { Notice } from "obsidian"; import { META_ALIAS } from "src/const/metadata_fields"; import type { IDataview } from "src/external/dataview/interfaces"; // import type { BCGraph } from "src/graph/MyMultiGraph"; @@ -203,13 +202,19 @@ export const _add_explicit_edges_list_note: ExplicitEdgeBuilder = ( list_note_file.path, ); if (!list_note_info.ok) { - if (list_note_info.error) + if (list_note_info.error) { results.errors.push(list_note_info.error); + } return; } else { - new Notice( - "list-notes are not implemented without Dataview enabled", - ); + results.errors.push({ + path: list_note_file.path, + code: "missing_other_plugin", + message: + "list-notes are not implemented without Dataview enabled", + }); + + return; } // TODO: Gonna have to read the contents of the file and parse it pretty manually... diff --git a/src/graph/builders/explicit/regex_note.ts b/src/graph/builders/explicit/regex_note.ts index 208964cf..4579cd23 100644 --- a/src/graph/builders/explicit/regex_note.ts +++ b/src/graph/builders/explicit/regex_note.ts @@ -23,7 +23,7 @@ const get_regex_note_info = ( return graph_build_fail({ path, code: "invalid_field_value", - message: `regex-note-regex is not a string: '${regex_str}'`, + message: `${META_ALIAS["regex-note-regex"]} is not a string: '${regex_str}'`, }); } @@ -32,7 +32,7 @@ const get_regex_note_info = ( return graph_build_fail({ path, code: "invalid_field_value", - message: `regex-note-flags is not a string: '${flags}'`, + message: `${META_ALIAS["regex-note-flags"]} is not a string: '${flags}'`, }); } @@ -44,7 +44,7 @@ const get_regex_note_info = ( return graph_build_fail({ path, code: "invalid_field_value", - message: `regex-note-regex is not a valid regex: ${regex_str}`, + message: `${META_ALIAS["regex-note-regex"]} is not a valid regex: '${regex_str}'`, }); } @@ -58,13 +58,13 @@ const get_regex_note_info = ( return graph_build_fail({ path, code: "invalid_field_value", - message: `regex-note-field is not a string: '${field}'`, + message: `${META_ALIAS["regex-note-field"]} is not a string: '${field}'`, }); } else if (!plugin.settings.edge_fields.find((f) => f.label === field)) { return graph_build_fail({ path, code: "invalid_field_value", - message: `dataview-note-field is not a valid field: '${field}'`, + message: `${META_ALIAS["regex-note-field"]} is not a valid field: '${field}'`, }); } @@ -125,8 +125,8 @@ export const _add_explicit_edges_regex_note: ExplicitEdgeBuilder = ( .forEach((target_id) => { results.edges.push( new GraphConstructionEdgeData( - target_id, regex_note.path, + target_id, regex_note.info.field, "regex_note", ), diff --git a/src/interfaces/graph.ts b/src/interfaces/graph.ts index 0a73a1fa..6fae7006 100644 --- a/src/interfaces/graph.ts +++ b/src/interfaces/graph.ts @@ -14,7 +14,8 @@ export type BreadcrumbsError = { | "deprecated_field" | "invalid_field_value" | "invalid_setting_value" - | "invalid_yaml"; + | "invalid_yaml" + | "missing_other_plugin"; message: string; path: string; }; From 8dfd92ca073bd3017e5221d8c8f125cc514c7577 Mon Sep 17 00:00:00 2001 From: SkepticMystic Date: Tue, 7 May 2024 11:55:45 +0200 Subject: [PATCH 25/65] test: Delete irrelevant TS tests --- tests/__mocks__/graph/index.ts | 36 ----------- tests/graph/MyMultiGraph.test.ts | 106 ------------------------------- tests/graph/traverse.test.ts | 106 ------------------------------- tests/graph/utils.test.ts | 28 -------- 4 files changed, 276 deletions(-) delete mode 100644 tests/__mocks__/graph/index.ts delete mode 100644 tests/graph/MyMultiGraph.test.ts delete mode 100644 tests/graph/traverse.test.ts delete mode 100644 tests/graph/utils.test.ts diff --git a/tests/__mocks__/graph/index.ts b/tests/__mocks__/graph/index.ts deleted file mode 100644 index 84c96c95..00000000 --- a/tests/__mocks__/graph/index.ts +++ /dev/null @@ -1,36 +0,0 @@ -import type { BCEdge, BCEdgeAttributes } from "src/graph/MyMultiGraph"; - -export const _mock_edge = ( - source_id: string, - target_id: string, - attr?: Partial, -): Pick< - BCEdge, - "attr" | "source_id" | "target_id" | "source_attr" | "target_attr" -> => ({ - source_id, - target_id, - source_attr: { resolved: true }, - target_attr: { resolved: true }, - attr: - attr?.explicit === true || attr?.explicit === undefined - ? { - field: "down", - source: "typed_link", - explicit: true as const, - - ...(attr as Partial< - Extract - >), - } - : { - round: 1, - field: "down", - explicit: false as const, - implied_kind: "transitive:opposite_direction", - - ...(attr as Partial< - Extract - >), - }, -}); diff --git a/tests/graph/MyMultiGraph.test.ts b/tests/graph/MyMultiGraph.test.ts deleted file mode 100644 index b50317c2..00000000 --- a/tests/graph/MyMultiGraph.test.ts +++ /dev/null @@ -1,106 +0,0 @@ -import { BCGraph } from "src/graph/MyMultiGraph"; -import { _mock_edge } from "tests/__mocks__/graph"; -import { describe, test } from "vitest"; - -describe("safe_add_node", () => { - test("regular", (t) => { - const g = new BCGraph(); - - t.expect(g.safe_add_node("a", { resolved: true })).toBe(true); - }); - - test("duplicate", (t) => { - const g = new BCGraph(); - - g.safe_add_node("a", { resolved: true }); - - t.expect(g.safe_add_node("a", { resolved: true })).toBe(false); - }); -}); - -describe("upsert_node", () => { - test("add", (t) => { - const g = new BCGraph(); - - g.upsert_node("a", { resolved: true }); - - t.expect(g.getNodeAttribute("a", "resolved")).toBe(true); - }); - - test("patch", (t) => { - const g = new BCGraph(); - - g.addNode("a", { resolved: false }); - - g.upsert_node("a", { resolved: true }); - - t.expect(g.getNodeAttribute("a", "resolved")).toBe(true); - }); -}); - -describe("safe_rename_node", () => { - test("regular", (t) => { - const g = new BCGraph(); - - g.addNode("a", { resolved: true }); - - const res = g.safe_rename_node("a", "b"); - - t.expect(res.ok).toBe(true); - t.expect(g.hasNode("a")).toBe(false); - t.expect(g.hasNode("b")).toBe(true); - }); - - test("old_id doesn't exist", (t) => { - const g = new BCGraph(); - - g.addNode("b", { resolved: true }); - - const res = g.safe_rename_node("a", "b"); - - t.expect(res.ok).toBe(false); - t.expect(g.hasNode("a")).toBe(false); - t.expect(g.hasNode("b")).toBe(true); - }); - - test("new_id exists", (t) => { - const g = new BCGraph(); - - g.addNode("a", { resolved: true }); - g.addNode("b", { resolved: true }); - - const res = g.safe_rename_node("a", "b"); - - t.expect(res.ok).toBe(false); - t.expect(g.hasNode("a")).toBe(true); - t.expect(g.hasNode("b")).toBe(true); - }); -}); - -describe("safe_add_directed_edge", () => { - test("regular", (t) => { - const g = new BCGraph(); - - g.addNode("a", { resolved: true }); - g.addNode("b", { resolved: true }); - - t.expect( - g.safe_add_directed_edge("a", "b", _mock_edge("a", "b").attr), - ).toBe(true); - - t.expect(g.hasDirectedEdge("a", "b")).toBe(true); - }); - - test("edge exists", (t) => { - const g = new BCGraph(); - - g.addNode("a", { resolved: true }); - g.addNode("b", { resolved: true }); - - g.safe_add_directed_edge("a", "b", _mock_edge("a", "b").attr); - - t.expect( - g.safe_add_directed_edge("a", "b", _mock_edge("a", "b").attr), - ).toBe(false); - }); -}); diff --git a/tests/graph/traverse.test.ts b/tests/graph/traverse.test.ts deleted file mode 100644 index 36777da7..00000000 --- a/tests/graph/traverse.test.ts +++ /dev/null @@ -1,106 +0,0 @@ -import { BCGraph } from "src/graph/MyMultiGraph"; -import { Traverse } from "src/graph/traverse"; -import { _mock_edge } from "tests/__mocks__/graph"; -import { describe, expect, test } from "vitest"; - -// TODO: This isn't the _best_ way to test these traversal functions -// But it does make writing the target data simpler -describe("all_paths", () => { - test("straight-line", () => { - const graph = new BCGraph({ - edges: [ - _mock_edge("a", "b", {}), - _mock_edge("b", "c", {}), - _mock_edge("c", "d", {}), - ], - }); - - const edges = Traverse.flatten_tree( - Traverse.build_tree(graph, "a", {}), - ).map((item) => ({ - source_id: item.edge.source_id, - target_id: item.edge.target_id, - })); - - expect(edges).toStrictEqual([ - { source_id: "a", target_id: "b" }, - { source_id: "b", target_id: "c" }, - { source_id: "c", target_id: "d" }, - ]); - }); - - test("fork", () => { - const graph = new BCGraph({ - edges: [ - _mock_edge("a", "b", {}), - _mock_edge("b", "c", {}), - _mock_edge("b", "d", {}), - ], - }); - - const edges = Traverse.flatten_tree( - Traverse.build_tree(graph, "a", {}), - ).map((item) => ({ - source_id: item.edge.source_id, - target_id: item.edge.target_id, - })); - - expect(edges).toEqual([ - { source_id: "a", target_id: "b" }, - { source_id: "b", target_id: "c" }, - { source_id: "b", target_id: "d" }, - ]); - }); - - test("diamond", () => { - const graph = new BCGraph({ - edges: [ - _mock_edge("a", "b", {}), - _mock_edge("b", "c", {}), - _mock_edge("b", "d", {}), - _mock_edge("c", "e", {}), - _mock_edge("d", "e", {}), - ], - }); - - const edges = Traverse.flatten_tree( - Traverse.build_tree(graph, "a", {}), - ).map((item) => ({ - source_id: item.edge.source_id, - target_id: item.edge.target_id, - })); - - expect(edges).toEqual([ - { source_id: "a", target_id: "b" }, - { source_id: "b", target_id: "c" }, - { source_id: "c", target_id: "e" }, - { source_id: "b", target_id: "d" }, - { source_id: "d", target_id: "e" }, - ]); - }); - - test("loop", () => { - const graph = new BCGraph({ - edges: [ - _mock_edge("a", "b", {}), - _mock_edge("b", "c", {}), - _mock_edge("c", "b", {}), - ], - }); - - const edges = Traverse.flatten_tree( - Traverse.build_tree(graph, "a", {}), - ).map((item) => ({ - source_id: item.edge.source_id, - target_id: item.edge.target_id, - })); - - expect(edges).toEqual([ - { source_id: "a", target_id: "b" }, - { source_id: "b", target_id: "c" }, - { source_id: "c", target_id: "b" }, - ]); - }); - - // TODO: Different hierarchies shouldn't contribute to the same path -}); diff --git a/tests/graph/utils.test.ts b/tests/graph/utils.test.ts deleted file mode 100644 index 8ceef156..00000000 --- a/tests/graph/utils.test.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { is_self_loop } from "src/graph/utils"; -import { _mock_edge } from "tests/__mocks__/graph"; -import { describe, test } from "vitest"; - -describe("is_self_loop", () => { - test("regular", (t) => { - t.expect(is_self_loop(_mock_edge("a", "a"))).toBe(true); - }); - - test("different", (t) => { - t.expect(is_self_loop(_mock_edge("a", "b"))).toBe(false); - }); -}); - -// const get_edges = () => [ -// _mock_edge("1/a", "1/b", { field: "a" }), -// _mock_edge("1/b", "2/c", { field: "a" }), -// _mock_edge("2/c", "2/d", { field: "a" }), -// _mock_edge("2/d", "3/e", { field: "a" }), -// ]; - -// describe("edge_sorter", () => { -// test("path", (t) => { -// const sorted = get_edges().sort( -// get_edge_sorter({ field: "path", order: 1 }), -// ); -// }); -// }); From 7fbfb316bcb1f927251dffac7823682991622d2d Mon Sep 17 00:00:00 2001 From: SkepticMystic Date: Tue, 7 May 2024 11:56:06 +0200 Subject: [PATCH 26/65] fix: Edge sort field is neighbour-field, not neighbour --- wasm/src/edge_sorting.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/wasm/src/edge_sorting.rs b/wasm/src/edge_sorting.rs index 2027a7b9..2e5ecbb1 100644 --- a/wasm/src/edge_sorting.rs +++ b/wasm/src/edge_sorting.rs @@ -24,8 +24,8 @@ impl SortField { "basename" => Some(SortField::Basename), "field" => Some(SortField::EdgeType), "explicit" => Some(SortField::Implied), - s if s.starts_with("neighbour:") => { - Some(SortField::Neighbour(s["neighbour:".len()..].to_string())) + s if s.starts_with("neighbour-field:") => { + Some(SortField::Neighbour(s["neighbour-field:".len()..].to_string())) } _ => None, } From aa91e863f27e90172b26f63993542092f1027a54 Mon Sep 17 00:00:00 2001 From: SkepticMystic Date: Tue, 7 May 2024 11:56:29 +0200 Subject: [PATCH 27/65] Reimplement trim_lone_param in get_attribute_label --- wasm/src/graph_data.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/wasm/src/graph_data.rs b/wasm/src/graph_data.rs index 6ac2e6bb..66e44d71 100644 --- a/wasm/src/graph_data.rs +++ b/wasm/src/graph_data.rs @@ -108,7 +108,11 @@ impl EdgeData { } } - result.join(" ") + if result.len() == 1 { + result[0].split('=').nth(1).unwrap().to_string() + } else { + result.join(" ") + } } } From a7f662faea5cc918e2af9b36f37c34ca67571351 Mon Sep 17 00:00:00 2001 From: SkepticMystic Date: Tue, 7 May 2024 16:25:16 +0200 Subject: [PATCH 28/65] Remove some old TODOs + Add (RUST) label to existing ones --- src/api/index.ts | 14 ++-- .../codeblocks/CodeblockMermaid.svelte | 35 +++++---- .../codeblocks/CodeblockTree.svelte | 72 +++++++------------ src/components/side_views/TreeView.svelte | 54 +++++--------- src/graph/MyMultiGraph.ts | 1 - src/graph/utils.ts | 15 ++-- src/main.ts | 10 +-- wasm/src/graph.rs | 4 +- wasm/src/graph_data.rs | 3 +- wasm/src/graph_mermaid.rs | 1 - 10 files changed, 85 insertions(+), 124 deletions(-) diff --git a/src/api/index.ts b/src/api/index.ts index 67953e6d..314bc48c 100644 --- a/src/api/index.ts +++ b/src/api/index.ts @@ -23,14 +23,14 @@ export class BCAPI { await this.plugin.refresh(); } /** @deprecated Use refresh */ - public async refreshIndex() { - await this.refresh(); + public async refreshIndex() { + await this.refresh(); } /** @deprecated Filter edges of plugin.graph instead */ - public getSubForFields(fields: string[], g = this.plugin.graph) {}; + public getSubForFields(fields: string[], g = this.plugin.graph) {} - // TODO + // TODO(RUST) // public build_tree = Traverse.build_tree; // public breadth_first_traversal = Traverse.breadth_first; @@ -45,11 +45,11 @@ export class BCAPI { start_node, Object.assign({ ...ListIndex.DEFAULT_OPTIONS }, options), ); - }; + } // BREAKING /** @deprecated Use flatten_all_paths and flat_paths_to_index_list instead. Or, create_list_index */ - public createIndex() {}; + public createIndex() {} public get_neighbours(source = get(active_file_store)?.path) { return source && this.plugin.graph.has_node(source) @@ -58,7 +58,7 @@ export class BCAPI { } /** @deprecated Use get_neighbours instead */ - public getMatrixNeighbours() { + public getMatrixNeighbours() { return this.get_neighbours(); } } diff --git a/src/components/codeblocks/CodeblockMermaid.svelte b/src/components/codeblocks/CodeblockMermaid.svelte index 0f3ecb66..d2e786ce 100644 --- a/src/components/codeblocks/CodeblockMermaid.svelte +++ b/src/components/codeblocks/CodeblockMermaid.svelte @@ -10,7 +10,12 @@ import CopyToClipboardButton from "../button/CopyToClipboardButton.svelte"; import RenderExternalCodeblock from "../obsidian/RenderExternalCodeblock.svelte"; import CodeblockErrors from "./CodeblockErrors.svelte"; - import { MermaidGraphOptions, NodeData, NoteGraphError, TraversalOptions } from "wasm/pkg/breadcrumbs_graph_wasm"; + import { + MermaidGraphOptions, + NodeData, + NoteGraphError, + TraversalOptions, + } from "wasm/pkg/breadcrumbs_graph_wasm"; import { remove_nullish_keys } from "src/utils/objects"; import { Paths } from "src/utils/paths"; import { Links } from "src/utils/links"; @@ -20,7 +25,7 @@ export let errors: BreadcrumbsError[]; export let file_path: string; - let mermaid: string = ""; + let code: string = ""; let error: NoteGraphError | undefined = undefined; export const update = () => { @@ -55,7 +60,6 @@ .slice(2, -2); } else { return Paths.drop_ext( - Links.resolve_to_absolute_path( plugin.app, node_path, @@ -68,13 +72,16 @@ ); try { - mermaid = plugin.graph.generate_mermaid_graph(traversal_options, mermaid_options).mermaid; + code = plugin.graph.generate_mermaid_graph( + traversal_options, + mermaid_options, + ).mermaid; error = undefined; } catch (e) { log.error("Error generating mermaid graph", e); if (e instanceof NoteGraphError) { - mermaid = ""; + code = ""; error = e; } } @@ -83,7 +90,8 @@ onMount(() => { update(); }); - + + // TODO(RUST) // const sort = get_edge_sorter( // // @ts-expect-error: ts(2345) // options.sort, @@ -168,7 +176,6 @@ // }); // $: log.debug(mermaid); -
@@ -180,7 +187,7 @@ {/if} - {#if mermaid} + {#if code}
+ {:else if error} +

{error.message}

{:else} - {#if error} -

{error.message}

- {:else} - -

No paths found.

- {/if} + +

No paths found.

{/if}
diff --git a/src/components/codeblocks/CodeblockTree.svelte b/src/components/codeblocks/CodeblockTree.svelte index 8a9559be..e5e9c2ae 100644 --- a/src/components/codeblocks/CodeblockTree.svelte +++ b/src/components/codeblocks/CodeblockTree.svelte @@ -2,14 +2,20 @@ import type { ICodeblock } from "src/codeblocks/schema"; import { ListIndex } from "src/commands/list_index"; import type { BreadcrumbsError } from "src/interfaces/graph"; + import { log } from "src/logger"; import type BreadcrumbsPlugin from "src/main"; + import { active_file_store } from "src/stores/active_file"; + import { Timer } from "src/utils/timer"; import { onMount } from "svelte"; + import { + NoteGraphError, + RecTraversalResult, + TraversalOptions, + create_edge_sorter, + } from "wasm/pkg/breadcrumbs_graph_wasm"; import NestedEdgeList from "../NestedEdgeList.svelte"; import CopyToClipboardButton from "../button/CopyToClipboardButton.svelte"; import CodeblockErrors from "./CodeblockErrors.svelte"; - import { NoteGraphError, RecTraversalResult, TraversalOptions, create_edge_sorter, type RecTraversalData } from "wasm/pkg/breadcrumbs_graph_wasm"; - import { log } from "src/logger"; - import { Timer } from "src/utils/timer"; export let plugin: BreadcrumbsPlugin; export let options: ICodeblock["Options"]; @@ -30,19 +36,26 @@ export const update = () => { const max_depth = options.depth[1] ?? DEFAULT_MAX_DEPTH; + const fields = + options.fields ?? plugin.settings.edge_fields.map((f) => f.label); + + const source_path = file_path + ? file_path + : $active_file_store + ? $active_file_store.path + : ""; + const traversal_options = new TraversalOptions( - [file_path], - options.fields ?? [], + [source_path], + fields, max_depth === Infinity ? DEFAULT_MAX_DEPTH : max_depth, !options["merge-fields"], ); try { - tree = plugin.graph.rec_traverse( - traversal_options - ); + tree = plugin.graph.rec_traverse(traversal_options); tree.sort(plugin.graph, sort); - + error = undefined; } catch (e) { log.error("Error updating codeblock tree", e); @@ -61,34 +74,7 @@ log.debug(timer.elapsedMessage("CodeblockTree initial traversal")); }); - // TODO reimplement all this logic - - // // if the file_path is an empty string, so the code block is not rendered inside note, we fall back to the active file store - // $: source_path = file_path - // ? file_path - // : $active_file_store - // ? $active_file_store.path - // : ""; - - // // this is an exposed function that we can call from the outside to update the codeblock - // export const update = () => { - // tree = get_tree(); - // }; - - // const base_traversal = (attr: EdgeAttrFilters) => - // Traverse.build_tree( - // plugin.graph, - // source_path, - // { max_depth: options.depth[1] }, - // (e) => - // has_edge_attrs(e, { - // ...attr, - // $or_target_ids: options["dataview-from-paths"], - // }), - // ); - - // const edge_field_labels = - // options.fields ?? plugin.settings.edge_fields.map((f) => f.label); + // TODO(RUST): reimplement all this logic // const get_tree = () => { // if (source_path && plugin.graph.hasNode(source_path)) { @@ -112,8 +98,6 @@ // return []; // } // }; - - // onMount(update);
@@ -148,12 +132,10 @@ />
+ {:else if error} +

{error.message}

{:else} - {#if error} -

{error.message}

- {:else} - -

No paths found.

- {/if} + +

No paths found.

{/if}
diff --git a/src/components/side_views/TreeView.svelte b/src/components/side_views/TreeView.svelte index c58229fd..31cf8f75 100644 --- a/src/components/side_views/TreeView.svelte +++ b/src/components/side_views/TreeView.svelte @@ -9,7 +9,10 @@ import EdgeSortIdSelector from "../selector/EdgeSortIdSelector.svelte"; import FieldGroupLabelsSelector from "../selector/FieldGroupLabelsSelector.svelte"; import ShowAttributesSelectorMenu from "../selector/ShowAttributesSelectorMenu.svelte"; - import { TraversalOptions, create_edge_sorter } from "wasm/pkg/breadcrumbs_graph_wasm"; + import { + TraversalOptions, + create_edge_sorter, + } from "wasm/pkg/breadcrumbs_graph_wasm"; export let plugin: BreadcrumbsPlugin; @@ -27,45 +30,21 @@ field_group_labels, ); - $: tree = $active_file_store && plugin.graph.has_node($active_file_store.path) - ? plugin.graph.rec_traverse( - new TraversalOptions( - [$active_file_store!.path], - edge_field_labels, - 20, - !merge_fields, - ), - ) - : undefined; + $: tree = + $active_file_store && plugin.graph.has_node($active_file_store.path) + ? plugin.graph.rec_traverse( + new TraversalOptions( + [$active_file_store!.path], + edge_field_labels, + 20, + !merge_fields, + ), + ) + : undefined; - $: sort = create_edge_sorter( - edge_sort_id.field, - edge_sort_id.order === -1, - ); + $: sort = create_edge_sorter(edge_sort_id.field, edge_sort_id.order === -1); $: tree?.sort(plugin.graph, sort); - - // const base_traversal = (attr: EdgeAttrFilters) => - // Traverse.build_tree( - // plugin.graph, - // $active_file_store!.path, - // // TODO: Customisable max depth - // { max_depth: 20 }, - // (edge) => has_edge_attrs(edge, attr), - // ); - - // $: sort = get_edge_sorter(edge_sort_id); - - - - // $: tree = - // $active_file_store && plugin.graph.hasNode($active_file_store.path) - // ? merge_fields - // ? base_traversal({ $or_fields: edge_field_labels }) - // : edge_field_labels.flatMap((field) => - // base_traversal({ field }), - // ) - // : [];
@@ -109,7 +88,6 @@ {#key tree || sort} {#if tree && !tree.is_empty()} number; const sorters = { - path: (order) => (a, b) => a.target.path.localeCompare(b.target.path) * order, + path: (order) => (a, b) => + a.target.path.localeCompare(b.target.path) * order, basename: (order) => (a, b) => { const [a_field, b_field] = [ @@ -78,9 +78,7 @@ const sorters = { }, } satisfies Partial EdgeSorter>>; -export const get_edge_sorter: ( - sort: EdgeSortId, -) => EdgeSorter = (sort) => { +export const get_edge_sorter: (sort: EdgeSortId) => EdgeSorter = (sort) => { switch (sort.field) { case "path": { return sorters.path(sort.order); @@ -96,9 +94,10 @@ export const get_edge_sorter: ( case "explicit": { return (a, b) => { - if (a.implied === b.implied) { - return a.edge_source.localeCompare(b.edge_source) * sort.order; + return ( + a.edge_source.localeCompare(b.edge_source) * sort.order + ); } else { return a.implied ? -sort.order : sort.order; } @@ -116,7 +115,7 @@ export const get_edge_sorter: ( } switch (sort.field.split(":")[0]) { - // TODO + // TODO(RUST): I think this has actually been reimplemented though // BREAKING: Deprecate in favour of neighbour-field // case "neighbour": // case "neighbour-field": { diff --git a/src/main.ts b/src/main.ts index bae3b158..235c1629 100644 --- a/src/main.ts +++ b/src/main.ts @@ -20,7 +20,7 @@ import { deep_merge_objects } from "./utils/objects"; import { Timer } from "./utils/timer"; import { redraw_page_views } from "./views/page"; import { TreeView } from "./views/tree"; -import wasmbin from '../wasm/pkg/breadcrumbs_graph_wasm_bg.wasm'; +import wasmbin from "../wasm/pkg/breadcrumbs_graph_wasm_bg.wasm"; import init, { type InitInput, type NoteGraph, @@ -31,7 +31,7 @@ import init, { BatchGraphUpdate, RemoveNoteGraphUpdate, AddNoteGraphUpdate, -} from '../wasm/pkg'; +} from "../wasm/pkg"; export enum BCEvent { GRAPH_UPDATE = "graph-update", @@ -169,7 +169,7 @@ export default class BreadcrumbsPlugin extends Plugin { }), ); - // TODO + // TODO(RUST) // /// Vault // this.registerEvent( // this.app.vault.on("create", (file) => { @@ -255,7 +255,7 @@ export default class BreadcrumbsPlugin extends Plugin { log.debug("loaded Breadcrumbs plugin"); } - onunload() { } + onunload() {} async loadSettings() { this.settings = deep_merge_objects( @@ -366,7 +366,7 @@ export default class BreadcrumbsPlugin extends Plugin { (leaf.view as TreeView).onOpen(); }); } - }; + } // SOURCE: https://docs.obsidian.md/Plugins/User+interface/Views async activateView(view_id: string, options?: { side?: "left" | "right" }) { diff --git a/wasm/src/graph.rs b/wasm/src/graph.rs index 7c3a076b..a5945f02 100644 --- a/wasm/src/graph.rs +++ b/wasm/src/graph.rs @@ -233,7 +233,7 @@ impl NoteGraph { // rules look like // [A, B, C] -> D - // TODO: maybe we can keep track of edge types that were added in the last round and only check a rule that has any of those edge types on the left side + // We can keep track of edge types that were added in the last round and only check a rule that has any of those edge types on the left side // we would need to also check back edges though // a rule like [A, B] -> (C with back edge D) would do nothing if applied multiple times, since the edges on the left side were not modified @@ -612,7 +612,7 @@ impl NoteGraph { )); } - // TODO: also update the other things like aliases + // TODO(RUST): also update the other things like aliases self.int_set_node_resolved(node_index, true)?; } None => { diff --git a/wasm/src/graph_data.rs b/wasm/src/graph_data.rs index 66e44d71..16a4ce8a 100644 --- a/wasm/src/graph_data.rs +++ b/wasm/src/graph_data.rs @@ -79,8 +79,7 @@ impl EdgeData { // the mapping that exist on the JS side are as follows // "field" | "explicit" | "source" | "implied_kind" | "round" - // TODO: maybe change the attribute options so that the JS side better matches the data - + // TODO(RUST): maybe change the attribute options so that the JS side better matches the data for attribute in attributes { let data = match attribute.as_str() { "field" => Some(("field", self.edge_type.clone())), diff --git a/wasm/src/graph_mermaid.rs b/wasm/src/graph_mermaid.rs index e4793ead..23c98fa9 100644 --- a/wasm/src/graph_mermaid.rs +++ b/wasm/src/graph_mermaid.rs @@ -147,7 +147,6 @@ impl NoteGraph { let node_label = match diagram_options.node_label_fn { Some(ref function) => { match function.call1(&JsValue::NULL, &weight.clone().into()) { - // TODO: maybe error when the return value is not a string? Ok(value) => value.as_string().unwrap_or(weight.path.clone()), Err(e) => { return Err(NoteGraphError::new( From 5568e849ecd7d04db7e17477da064d1dbc745207 Mon Sep 17 00:00:00 2001 From: SkepticMystic Date: Tue, 7 May 2024 16:29:40 +0200 Subject: [PATCH 29/65] fix: Resolve merge-conflict conflicts --- src/commands/init.ts | 7 ++----- src/utils/transitive_rules.ts | 2 -- wasm/src/graph.rs | 1 + 3 files changed, 3 insertions(+), 7 deletions(-) diff --git a/src/commands/init.ts b/src/commands/init.ts index e9ff64bd..904bc332 100644 --- a/src/commands/init.ts +++ b/src/commands/init.ts @@ -141,10 +141,7 @@ export const init_all_commands = (plugin: BreadcrumbsPlugin) => { plugin.addCommand({ id: `breadcrumbs:jump-to-first-neighbour-group:${group.label}`, name: `Jump to first neighbour by group:${group.label}`, - callback: () => - jump_to_neighbour(plugin, { - attr: { $or_fields: group.fields }, - }), + callback: () => jump_to_neighbour(plugin, { fields: group.fields }), }); }); @@ -156,7 +153,7 @@ export const init_all_commands = (plugin: BreadcrumbsPlugin) => { callback: () => thread( plugin, - { field: label }, + label, plugin.settings.commands.thread.default_options, ), }); diff --git a/src/utils/transitive_rules.ts b/src/utils/transitive_rules.ts index f254b5fc..ff3944a7 100644 --- a/src/utils/transitive_rules.ts +++ b/src/utils/transitive_rules.ts @@ -1,4 +1,3 @@ -import type { BreadcrumbsSettings } from "src/interfaces/settings"; import type { Result } from "src/interfaces/result"; import type { BreadcrumbsSettings, EdgeField } from "src/interfaces/settings"; import { url_search_params } from "src/utils/url"; @@ -68,7 +67,6 @@ export const input_transitive_rule_schema = (data: { fields: EdgeField[] }) => { // ) => { // const edges: Omit[] = []; - // rule.chain.forEach((attr, i) => { // edges.push({ // source_id: i.toString(), diff --git a/wasm/src/graph.rs b/wasm/src/graph.rs index a5945f02..211cc08a 100644 --- a/wasm/src/graph.rs +++ b/wasm/src/graph.rs @@ -293,6 +293,7 @@ impl NoteGraph { let edge_data = EdgeData::new( rule.edge_type.clone(), + // TODO(RUST): Use stringify_transitive_rule if !name format!("transitive:{}", rule.name), true, i, From 2f2179f949ca72e3fc13774f4a362cc5973c0543 Mon Sep 17 00:00:00 2001 From: Moritz Jung Date: Tue, 7 May 2024 21:53:03 +0200 Subject: [PATCH 30/65] wasm logging; rust script in package json --- package.json | 5 +- src/commands/list_index/index.ts | 1 + src/graph/builders/index.ts | 4 ++ wasm/Cargo.toml | 5 +- wasm/README.md | 2 +- wasm/src/graph.rs | 86 +++++++++++------------ wasm/src/graph_mermaid.rs | 2 + wasm/src/lib.rs | 4 +- wasm/src/utils.rs | 113 +++++++++++++++++++++++++++++-- wasm/tests/graph.rs | 2 +- 10 files changed, 167 insertions(+), 57 deletions(-) diff --git a/package.json b/package.json index 7242d880..a0e586e1 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,10 @@ "version:beta": "node version-bump-beta.mjs && git add manifest-beta.json versions.json package.json", "release:beta": "npm run version:beta && git commit -m 'release:beta' && git push origin master:master && git tag -a $npm_package_version -m \"$npm_package_version\" && git push --tags", "test": "vitest", - "coverage:ui": "vitest run --coverage --coverage.include 'src/**' --coverage.reporter html && npx vite preview --outDir ./coverage --open" + "coverage:ui": "vitest run --coverage --coverage.include 'src/**' --coverage.reporter html && npx vite preview --outDir ./coverage --open", + "wasm:build": "cd wasm && wasm-pack build --target web", + "wasm:fmt": "cd wasm && cargo fmt", + "wasm:test": "cd wasm && wasm-pack test --node --features test" }, "keywords": [], "author": "SkepticMystic", diff --git a/src/commands/list_index/index.ts b/src/commands/list_index/index.ts index 8758be3b..be0eda52 100644 --- a/src/commands/list_index/index.ts +++ b/src/commands/list_index/index.ts @@ -82,6 +82,7 @@ export namespace ListIndex { ); return edge_tree_to_list_index( + // TODO Traverse.sort_edge_tree( // Traverse.build_tree(graph, start_node, options, (e) => // has_edge_attrs(e, { $or_fields: options.fields }), diff --git a/src/graph/builders/index.ts b/src/graph/builders/index.ts index b874595a..145d21fc 100644 --- a/src/graph/builders/index.ts +++ b/src/graph/builders/index.ts @@ -79,6 +79,7 @@ const get_initial_nodes = (all_files: AllFiles) => { export const rebuild_graph = async (plugin: BreadcrumbsPlugin) => { const timer = new Timer(); + const timer2 = new Timer(); // Get once, send to all builders const all_files = get_all_files(plugin.app); @@ -105,6 +106,7 @@ export const rebuild_graph = async (plugin: BreadcrumbsPlugin) => { } log.debug(timer.elapsedMessage("Collecting edges and nodes")); + timer.reset(); const transitive_rules = plugin.settings.implied_relations.transitive.map((rule) => { return new TransitiveGraphRule( @@ -122,6 +124,8 @@ export const rebuild_graph = async (plugin: BreadcrumbsPlugin) => { ); plugin.graph.build_graph(nodes, edges); + log.debug(timer.elapsedMessage("WASM call")); + log.debug(timer2.elapsedMessage("Total")); // log.debug(timer.elapsedMessage("Adding initial edges")); // timer.reset(); diff --git a/wasm/Cargo.toml b/wasm/Cargo.toml index 4e139628..459a19e5 100644 --- a/wasm/Cargo.toml +++ b/wasm/Cargo.toml @@ -2,13 +2,14 @@ name = "breadcrumbs_graph_wasm" version = "0.1.0" authors = ["Moritz Jung ", "SkepticMystic"] -edition = "2018" +edition = "2021" [lib] crate-type = ["cdylib", "rlib"] [features] default = ["console_error_panic_hook"] +test = [] [dependencies] wasm-bindgen = "0.2.84" @@ -23,7 +24,7 @@ web-time = "1.1.0" js-sys = "0.3.69" itertools = "0.12.1" serde = { version = "1.0", features = ["derive"] } -serde-wasm-bindgen = "0.4" +serde-wasm-bindgen = "0.6.5" smallvec = "1.13.2" vec-collections = "0.4.3" diff --git a/wasm/README.md b/wasm/README.md index 62ceebae..a7b8cc1e 100644 --- a/wasm/README.md +++ b/wasm/README.md @@ -15,5 +15,5 @@ cargo fmt ## Test ```bash -wasm-pack test --node +wasm-pack test --node --features test ``` \ No newline at end of file diff --git a/wasm/src/graph.rs b/wasm/src/graph.rs index 7c3a076b..9455d731 100644 --- a/wasm/src/graph.rs +++ b/wasm/src/graph.rs @@ -7,7 +7,6 @@ use petgraph::{ }; use vec_collections::{AbstractVecSet, VecSet}; use wasm_bindgen::prelude::*; -use web_time::Instant; use crate::{ graph_construction::{GraphConstructionEdgeData, GraphConstructionNodeData}, @@ -16,7 +15,7 @@ use crate::{ }, graph_rules::TransitiveGraphRule, graph_update::BatchGraphUpdate, - utils::{self, NoteGraphError, Result}, + utils::{NoteGraphError, PerfLogger, Result, LOGGER}, }; pub fn edge_matches_edge_filter(edge: &EdgeData, edge_types: Option<&Vec>) -> bool { @@ -64,7 +63,10 @@ impl NoteGraph { match &self.update_callback { Some(callback) => match callback.call0(&JsValue::NULL) { Ok(_) => {} - Err(e) => utils::log(format!("Error calling function: {:?}", e)), + Err(e) => LOGGER.warn(&format!( + "Error calling update notification function: {:?}", + e + )), }, None => {} } @@ -75,7 +77,8 @@ impl NoteGraph { nodes: Vec, edges: Vec, ) { - let now = Instant::now(); + let mut perf_logger = PerfLogger::new("Building Graph".to_owned()); + perf_logger.start_split("Adding initial nodes and edges".to_owned()); self.graph = StableGraph::::default(); self.edge_types = VecSet::empty(); @@ -86,7 +89,10 @@ impl NoteGraph { for info_node in nodes.as_slice() { if self.node_hash.contains_key(&info_node.path) { - utils::log(format!("Node already exists: {}", info_node.path)); + LOGGER.debug(&format!( + "Duplicate note path in graph construction data: {}", + info_node.path + )); continue; } @@ -106,28 +112,38 @@ impl NoteGraph { ); } - let elapsed = now.elapsed(); - utils::log(format!("Building initial graph took {:.2?}", elapsed)); + self.int_build_implied_edges(&mut perf_logger); - self.int_build_implied_edges(); + perf_logger.start_split("Update notification callback".to_owned()); self.notify_update(); + + perf_logger.log(); } pub fn apply_update(&mut self, update: BatchGraphUpdate) -> Result<()> { - let now = Instant::now(); + let mut perf_logger = PerfLogger::new("Applying Update".to_owned()); + perf_logger.start_split("Removing implied edges".to_owned()); + self.int_remove_implied_edges(); + perf_logger.start_split("Applying updates".to_owned()); + // self.log(); update.apply(self)?; // self.log(); + perf_logger.start_split("Rebuilding edge type tracker".to_owned()); + self.int_rebuild_edge_type_tracker(); - self.int_build_implied_edges(); - utils::log(format!("Applying update took {:.2?}", now.elapsed())); + self.int_build_implied_edges(&mut perf_logger); + + perf_logger.start_split("Update notification callback".to_owned()); self.notify_update(); + perf_logger.log(); + Ok(()) } @@ -137,7 +153,7 @@ impl NoteGraph { self.graph.node_references().for_each(|node| { match f.call1(&this, &node.weight().clone().into()) { Ok(_) => {} - Err(e) => utils::log(format!("Error calling function: {:?}", e)), + Err(e) => LOGGER.warn(&format!("Error calling node iteration callback: {:?}", e)), } }); } @@ -148,7 +164,7 @@ impl NoteGraph { self.graph.edge_references().for_each(|edge| { match f.call1(&this, &edge.weight().clone().into()) { Ok(_) => {} - Err(e) => utils::log(format!("Error calling function: {:?}", e)), + Err(e) => LOGGER.warn(&format!("Error calling edge iteration callback: {:?}", e)), } }); } @@ -204,7 +220,7 @@ impl NoteGraph { } pub fn log(&self) { - utils::log(format!("{:#?}", self.graph)); + LOGGER.info(&format!("{:#?}", self.graph)); } } @@ -218,8 +234,8 @@ impl Default for NoteGraph { /// All of these methods are prefixed with `int_`. impl NoteGraph { /// Builds the implied edges based on the transitive rules. - pub fn int_build_implied_edges(&mut self) { - let now = Instant::now(); + pub fn int_build_implied_edges(&mut self, perf_logger: &mut PerfLogger) { + let perf_split = perf_logger.start_split("Building Implied Edges".to_owned()); let max_rounds = self .transitive_rules @@ -240,6 +256,8 @@ impl NoteGraph { let mut edge_type_tracker = self.edge_types.clone(); for i in 1..(max_rounds + 1) { + let round_perf_split = perf_split.start_split(format!("Round {}", i)); + if edge_type_tracker.is_empty() { break; } @@ -315,8 +333,7 @@ impl NoteGraph { let mut current_edge_type_tracker: VecSet<[String; 16]> = VecSet::empty(); - let now2 = Instant::now(); - utils::log(format!("Adding {} Edges ", edges_to_add.len())); + round_perf_split.start_split(format!("Adding {} Edges", edges_to_add.len())); for (from, to, edge_data) in edges_to_add { self.int_add_edge( @@ -327,14 +344,12 @@ impl NoteGraph { ); } - let elapsed2 = now2.elapsed(); - utils::log(format!("Adding Implied Edges took {:.2?}", elapsed2)); + round_perf_split.stop(); edge_type_tracker = current_edge_type_tracker; } - let elapsed = now.elapsed(); - utils::log(format!("Building Implied Edges took {:.2?}", elapsed)); + perf_split.stop(); } pub fn int_rebuild_edge_type_tracker(&mut self) { @@ -464,9 +479,7 @@ impl NoteGraph { } pub fn int_remove_implied_edges(&mut self) { - let now = Instant::now(); - - // let mut edges_to_remove: Vec = Vec::new(); + let edge_count = self.graph.edge_count(); self.graph.retain_edges(|frozen_graph, edge| { let weight = frozen_graph.edge_weight(edge).unwrap(); @@ -474,24 +487,11 @@ impl NoteGraph { !weight.implied }); - // for edge in self.graph.edge_references() { - // let edge_data = edge.weight(); - - // if edge_data.implied { - // edges_to_remove.push(edge.id()); - // } - // } - - // utils::log(format!("Removing {} of {} Implied Edges", edges_to_remove.len(), self.graph.edge_count())); - - // for edge in edges_to_remove { - // self.graph.remove_edge(edge); - // } - - utils::log(format!("{} edges remain", self.graph.edge_count())); - - let elapsed = now.elapsed(); - utils::log(format!("Removing Implied Edges took {:.2?}", elapsed)); + LOGGER.debug(&format!( + "Removed {} implied edges, {} explicit edges remain", + edge_count - self.graph.edge_count(), + self.graph.edge_count() + )); } fn int_add_node(&mut self, node: &GraphConstructionNodeData) -> Result<()> { diff --git a/wasm/src/graph_mermaid.rs b/wasm/src/graph_mermaid.rs index e4793ead..60f6c9aa 100644 --- a/wasm/src/graph_mermaid.rs +++ b/wasm/src/graph_mermaid.rs @@ -297,6 +297,8 @@ impl NoteGraph { ) -> AccumulatedEdgeHashMap { let mut accumulated_edges: AccumulatedEdgeHashMap = HashMap::new(); + // sorting the two node indices in the edge tuple could be a speedup, since then only one lookup is needed + for (_, edge_ref) in edges { let forward_dir = (edge_ref.source(), edge_ref.target()); diff --git a/wasm/src/lib.rs b/wasm/src/lib.rs index a8b1f04e..aa077993 100644 --- a/wasm/src/lib.rs +++ b/wasm/src/lib.rs @@ -12,9 +12,9 @@ use wasm_bindgen::prelude::*; #[wasm_bindgen] pub fn create_graph() -> graph::NoteGraph { - // utils::log_str("Hello, from WASM!"); - console_error_panic_hook::set_once(); + utils::LOGGER.debug("Hello, from WASM!".into()); + graph::NoteGraph::new() } diff --git a/wasm/src/utils.rs b/wasm/src/utils.rs index c34b8e66..b38c3fde 100644 --- a/wasm/src/utils.rs +++ b/wasm/src/utils.rs @@ -1,16 +1,115 @@ use petgraph::visit::{EdgeRef, IntoEdgeReferences, IntoNodeReferences}; use std::{collections::VecDeque, error::Error, fmt}; use wasm_bindgen::prelude::*; +use web_time::Instant; +#[cfg(not(feature = "test"))] +#[wasm_bindgen(module = "src/logger/index.ts")] +extern "C" { + #[wasm_bindgen(js_name = log)] + pub static LOGGER: Logger; + + pub type Logger; + #[wasm_bindgen(method)] + pub fn debug(this: &Logger, message: &str); + #[wasm_bindgen(method)] + pub fn info(this: &Logger, message: &str); + #[wasm_bindgen(method)] + pub fn warn(this: &Logger, message: &str); + #[wasm_bindgen(method)] + pub fn error(this: &Logger, message: &str); +} + +#[cfg(feature = "test")] #[wasm_bindgen] extern "C" { - fn alert(s: &str); - // Use `js_namespace` here to bind `console.log(..)` instead of just - // `log(..)` - #[wasm_bindgen(js_namespace = console, js_name = log)] - pub fn log_str(s: &str); - #[wasm_bindgen(js_namespace = console)] - pub fn log(s: String); + #[wasm_bindgen(js_name = console)] + pub static LOGGER: Console; + + pub type Console; + #[wasm_bindgen(method)] + pub fn debug(this: &Console, message: &str); + #[wasm_bindgen(method, js_name = log)] + pub fn info(this: &Console, message: &str); + #[wasm_bindgen(method)] + pub fn warn(this: &Console, message: &str); + #[wasm_bindgen(method)] + pub fn error(this: &Console, message: &str); +} + +pub struct PerfLogger { + name: String, + start: Instant, + elapsed: Option, + splits: Vec, +} + +impl PerfLogger { + pub fn new(name: String) -> Self { + PerfLogger { + name, + start: Instant::now(), + splits: Vec::new(), + elapsed: None, + } + } + + pub fn start_split(&mut self, name: String) -> &mut PerfLogger { + // stop the last split + self.stop_split(); + + // create a new split + self.splits.push(PerfLogger::new(name)); + self.splits.last_mut().unwrap() + } + + pub fn stop_split(&mut self) { + self.splits.last_mut().map(|split| { + if !split.stopped() { + split.stop() + } + }); + } + + pub fn stop(&mut self) { + if self.stopped() { + LOGGER.warn(&format!("PerfLogger {} is already stopped", self.name)); + } else { + self.elapsed = Some(self.start.elapsed().as_micros()); + self.stop_split(); + } + } + + pub fn stopped(&self) -> bool { + self.elapsed.is_some() + } + + fn get_log_message(&mut self) -> Vec { + if !self.stopped() { + self.stop(); + } + + let mut message = vec![format!( + "{}ms > {}", + self.elapsed.unwrap() as f64 / 1000f64, + self.name + )]; + + for split in self.splits.iter_mut() { + let mut sub_message = split + .get_log_message() + .iter() + .map(|s| format!(" | {}", s)) + .collect::>(); + message.append(&mut sub_message); + } + + message + } + + pub fn log(&mut self) { + LOGGER.debug(&self.get_log_message().join("\n")); + } } pub type Result = std::result::Result; diff --git a/wasm/tests/graph.rs b/wasm/tests/graph.rs index 0afe6e2d..a10f0d2c 100644 --- a/wasm/tests/graph.rs +++ b/wasm/tests/graph.rs @@ -8,7 +8,7 @@ use breadcrumbs_graph_wasm::{ graph_update::{ AddEdgeGraphUpdate, AddNoteGraphUpdate, BatchGraphUpdate, RemoveNoteGraphUpdate, }, - utils::{graph_eq, log}, + utils::graph_eq, }; use wasm_bindgen_test::*; mod common; From 0596d966f3c1ba2924dde530f66577f4281e6edf Mon Sep 17 00:00:00 2001 From: Moritz Jung Date: Tue, 7 May 2024 22:10:08 +0200 Subject: [PATCH 31/65] adress some todos --- wasm/src/edge_sorting.rs | 6 +++--- wasm/src/graph.rs | 9 ++------- wasm/src/graph_data.rs | 9 +++++---- wasm/src/graph_rules.rs | 19 +++++++++++++++++++ 4 files changed, 29 insertions(+), 14 deletions(-) diff --git a/wasm/src/edge_sorting.rs b/wasm/src/edge_sorting.rs index 2e5ecbb1..68d0c2ed 100644 --- a/wasm/src/edge_sorting.rs +++ b/wasm/src/edge_sorting.rs @@ -24,9 +24,9 @@ impl SortField { "basename" => Some(SortField::Basename), "field" => Some(SortField::EdgeType), "explicit" => Some(SortField::Implied), - s if s.starts_with("neighbour-field:") => { - Some(SortField::Neighbour(s["neighbour-field:".len()..].to_string())) - } + s if s.starts_with("neighbour-field:") => Some(SortField::Neighbour( + s["neighbour-field:".len()..].to_string(), + )), _ => None, } } diff --git a/wasm/src/graph.rs b/wasm/src/graph.rs index f7e83af4..f4083c0f 100644 --- a/wasm/src/graph.rs +++ b/wasm/src/graph.rs @@ -309,13 +309,8 @@ impl NoteGraph { continue; } - let edge_data = EdgeData::new( - rule.edge_type.clone(), - // TODO(RUST): Use stringify_transitive_rule if !name - format!("transitive:{}", rule.name), - true, - i, - ); + let edge_data = + EdgeData::new(rule.edge_type.clone(), rule.get_name(), true, i); if rule.close_reversed { edges_to_add.push((end_node, start_node, edge_data)); diff --git a/wasm/src/graph_data.rs b/wasm/src/graph_data.rs index 16a4ce8a..e7099587 100644 --- a/wasm/src/graph_data.rs +++ b/wasm/src/graph_data.rs @@ -1,5 +1,6 @@ use std::{collections::HashMap, fmt::Debug}; +use itertools::Itertools; use petgraph::{ graph::{EdgeIndex, NodeIndex}, stable_graph::EdgeReference, @@ -79,7 +80,7 @@ impl EdgeData { // the mapping that exist on the JS side are as follows // "field" | "explicit" | "source" | "implied_kind" | "round" - // TODO(RUST): maybe change the attribute options so that the JS side better matches the data + // TODO(JS): maybe change the attribute options so that the JS side better matches the data for attribute in attributes { let data = match attribute.as_str() { "field" => Some(("field", self.edge_type.clone())), @@ -103,14 +104,14 @@ impl EdgeData { }; if let Some(data) = data { - result.push(format!("{}={}", data.0, data.1)); + result.push(data); } } if result.len() == 1 { - result[0].split('=').nth(1).unwrap().to_string() + result[0].1.clone() } else { - result.join(" ") + result.iter().map(|x| format!("{}={}", x.0, x.1)).join(" ") } } } diff --git a/wasm/src/graph_rules.rs b/wasm/src/graph_rules.rs index d54674dd..b773706d 100644 --- a/wasm/src/graph_rules.rs +++ b/wasm/src/graph_rules.rs @@ -52,6 +52,25 @@ impl TransitiveGraphRule { } } +impl TransitiveGraphRule { + pub fn stringify(&self) -> String { + format!( + "[{}] {} {}", + self.path.join(", "), + if self.close_reversed { "<-" } else { "->" }, + self.edge_type + ) + } + + pub fn get_name(&self) -> String { + if self.name.is_empty() { + return self.stringify(); + } else { + self.name.clone() + } + } +} + #[wasm_bindgen] pub fn create_graph_from_rule(rule: TransitiveGraphRule) -> NoteGraph { let mut graph = NoteGraph::new(); From 523793b13a95c086dfe87ffd1bb0e6088f03cc13 Mon Sep 17 00:00:00 2001 From: Moritz Jung Date: Tue, 7 May 2024 22:58:34 +0200 Subject: [PATCH 32/65] more minor fixes --- .../codeblocks/CodeblockMermaid.svelte | 8 +++-- wasm/src/edge_sorting.rs | 4 +-- wasm/src/graph.rs | 6 ++-- wasm/src/graph_data.rs | 18 +++++------ wasm/src/graph_mermaid.rs | 6 ++-- wasm/src/graph_update.rs | 30 ++++++++++++++++++- wasm/tests/graph.rs | 16 +++++----- 7 files changed, 59 insertions(+), 29 deletions(-) diff --git a/src/components/codeblocks/CodeblockMermaid.svelte b/src/components/codeblocks/CodeblockMermaid.svelte index d2e786ce..4a18a155 100644 --- a/src/components/codeblocks/CodeblockMermaid.svelte +++ b/src/components/codeblocks/CodeblockMermaid.svelte @@ -25,17 +25,19 @@ export let errors: BreadcrumbsError[]; export let file_path: string; + const DEFAULT_MAX_DEPTH = 100; + let code: string = ""; let error: NoteGraphError | undefined = undefined; export const update = () => { - const max_depth = options.depth[1] ?? 100; + const max_depth = options.depth[1] ?? DEFAULT_MAX_DEPTH; const traversal_options = new TraversalOptions( [file_path], options.fields, - max_depth === Infinity ? 100 : max_depth, - !options.collapse, + max_depth === Infinity ? DEFAULT_MAX_DEPTH : max_depth, + !options["merge-fields"], ); const flowchart_init = remove_nullish_keys({ diff --git a/wasm/src/edge_sorting.rs b/wasm/src/edge_sorting.rs index 68d0c2ed..beee2138 100644 --- a/wasm/src/edge_sorting.rs +++ b/wasm/src/edge_sorting.rs @@ -151,9 +151,9 @@ pub struct ImpliedOrdering; impl EdgeOrdering for ImpliedOrdering { fn compare(&self, a: &EdgeStruct, b: &EdgeStruct) -> std::cmp::Ordering { - if a.edge.implied == b.edge.implied { + if a.edge.explicit == b.edge.explicit { a.target.path.cmp(&b.target.path) - } else if a.edge.implied { + } else if a.edge.explicit { std::cmp::Ordering::Less } else { std::cmp::Ordering::Greater diff --git a/wasm/src/graph.rs b/wasm/src/graph.rs index f4083c0f..9cc8ea1d 100644 --- a/wasm/src/graph.rs +++ b/wasm/src/graph.rs @@ -310,7 +310,7 @@ impl NoteGraph { } let edge_data = - EdgeData::new(rule.edge_type.clone(), rule.get_name(), true, i); + EdgeData::new(rule.edge_type.clone(), rule.get_name(), false, i); if rule.close_reversed { edges_to_add.push((end_node, start_node, edge_data)); @@ -480,7 +480,7 @@ impl NoteGraph { self.graph.retain_edges(|frozen_graph, edge| { let weight = frozen_graph.edge_weight(edge).unwrap(); - !weight.implied + weight.explicit }); LOGGER.debug(&format!( @@ -739,7 +739,7 @@ impl NoteGraph { self.int_add_edge( source_index, target_index, - EdgeData::new(edge_type.to_owned(), edge_source.to_owned(), false, 0), + EdgeData::new(edge_type.to_owned(), edge_source.to_owned(), true, 0), &mut None, ); } diff --git a/wasm/src/graph_data.rs b/wasm/src/graph_data.rs index e7099587..1ead0da4 100644 --- a/wasm/src/graph_data.rs +++ b/wasm/src/graph_data.rs @@ -26,7 +26,7 @@ pub struct EdgeData { #[wasm_bindgen(skip)] pub edge_source: String, #[wasm_bindgen(skip)] - pub implied: bool, + pub explicit: bool, #[wasm_bindgen(skip)] pub round: u8, } @@ -34,11 +34,11 @@ pub struct EdgeData { #[wasm_bindgen] impl EdgeData { #[wasm_bindgen(constructor)] - pub fn new(edge_type: String, edge_source: String, implied: bool, round: u8) -> EdgeData { + pub fn new(edge_type: String, edge_source: String, explicit: bool, round: u8) -> EdgeData { EdgeData { edge_type, edge_source, - implied, + explicit, round, } } @@ -54,8 +54,8 @@ impl EdgeData { } #[wasm_bindgen(getter)] - pub fn implied(&self) -> bool { - self.implied + pub fn explicit(&self) -> bool { + self.explicit } #[wasm_bindgen(getter)] @@ -84,16 +84,16 @@ impl EdgeData { for attribute in attributes { let data = match attribute.as_str() { "field" => Some(("field", self.edge_type.clone())), - "explicit" => Some(("explicit", (!self.implied).to_string())), + "explicit" => Some(("explicit", (!self.explicit).to_string())), "source" => { - if !self.implied { + if !self.explicit { Some(("source", self.edge_source.clone())) } else { None } } "implied_kind" => { - if self.implied { + if self.explicit { Some(("implied_kind", self.edge_source.clone())) } else { None @@ -250,7 +250,7 @@ impl EdgeStruct { #[wasm_bindgen(getter)] pub fn implied(&self) -> bool { - self.edge.implied + self.edge.explicit } #[wasm_bindgen(getter)] diff --git a/wasm/src/graph_mermaid.rs b/wasm/src/graph_mermaid.rs index ccc403c7..0744a892 100644 --- a/wasm/src/graph_mermaid.rs +++ b/wasm/src/graph_mermaid.rs @@ -245,9 +245,9 @@ impl NoteGraph { .iter() .zip_longest(backward.iter()) .all(|pair| match pair { - EitherOrBoth::Both(a, b) => a.implied && b.implied, - EitherOrBoth::Left(a) => a.implied, - EitherOrBoth::Right(b) => b.implied, + EitherOrBoth::Both(a, b) => a.explicit && b.explicit, + EitherOrBoth::Left(a) => a.explicit, + EitherOrBoth::Right(b) => b.explicit, }); let arrow_type = match (backward.is_empty(), all_implied) { diff --git a/wasm/src/graph_update.rs b/wasm/src/graph_update.rs index c6d0c2dc..c98a0554 100644 --- a/wasm/src/graph_update.rs +++ b/wasm/src/graph_update.rs @@ -1,4 +1,7 @@ -use crate::{graph::NoteGraph, graph_construction::GraphConstructionNodeData, utils::Result}; +use crate::{ + graph::NoteGraph, graph_construction::GraphConstructionNodeData, + graph_rules::TransitiveGraphRule, utils::Result, +}; use wasm_bindgen::prelude::*; pub trait GraphUpdate { @@ -180,3 +183,28 @@ impl GraphUpdate for RemoveEdgeGraphUpdate { graph.int_safe_delete_edge(&self.from, &self.to, &self.edge_type) } } + +#[wasm_bindgen] +#[derive(Clone)] +pub struct TransitiveRulesGraphUpdate { + new_rules: Vec, +} + +#[wasm_bindgen] +impl TransitiveRulesGraphUpdate { + #[wasm_bindgen(constructor)] + pub fn new(new_rules: Vec) -> Self { + Self { new_rules } + } + + pub fn add_to_batch(&self, batch: &mut BatchGraphUpdate) { + batch.add_update(Box::new(self.clone())); + } +} + +impl GraphUpdate for TransitiveRulesGraphUpdate { + fn apply(&self, graph: &mut NoteGraph) -> Result<()> { + graph.set_transitive_rules(self.new_rules.clone()); + Ok(()) + } +} diff --git a/wasm/tests/graph.rs b/wasm/tests/graph.rs index a10f0d2c..fd3760ce 100644 --- a/wasm/tests/graph.rs +++ b/wasm/tests/graph.rs @@ -60,8 +60,8 @@ fn test_implied_edge_rules_reverse_direction() { .int_get_edge_by_name(&"1".to_string(), &"root".to_string(), &"up".to_string()) .unwrap(); - assert_eq!(up_edge_1.weight().implied, true); - assert_eq!(up_edge_2.weight().implied, true); + assert_eq!(up_edge_1.weight().explicit, true); + assert_eq!(up_edge_2.weight().explicit, true); graph.assert_correct_trackers(); } @@ -111,8 +111,8 @@ fn test_implied_edge_rules_sibling() { let same_edge_4 = graph.int_get_edge_by_name(&"1".to_string(), &"1".to_string(), &"same".to_string()); - assert_eq!(same_edge_1.weight().implied, true); - assert_eq!(same_edge_2.weight().implied, true); + assert_eq!(same_edge_1.weight().explicit, true); + assert_eq!(same_edge_2.weight().explicit, true); assert_eq!(same_edge_3.is_none(), true); assert_eq!(same_edge_4.is_none(), true); graph.assert_correct_trackers(); @@ -166,10 +166,10 @@ fn test_implied_edge_rules_sibling_can_loop() { .int_get_edge_by_name(&"1".to_string(), &"1".to_string(), &"same".to_string()) .unwrap(); - assert_eq!(same_edge_1.weight().implied, true); - assert_eq!(same_edge_2.weight().implied, true); - assert_eq!(same_edge_3.weight().implied, true); - assert_eq!(same_edge_4.weight().implied, true); + assert_eq!(same_edge_1.weight().explicit, true); + assert_eq!(same_edge_2.weight().explicit, true); + assert_eq!(same_edge_3.weight().explicit, true); + assert_eq!(same_edge_4.weight().explicit, true); graph.assert_correct_trackers(); } From 73eb30908081c02ee225e636508681843667a991 Mon Sep 17 00:00:00 2001 From: SkepticMystic Date: Wed, 8 May 2024 11:31:26 +0200 Subject: [PATCH 33/65] refactor: TraversalOptions.fields being undefined is the same as allowing all fields --- src/components/codeblocks/CodeblockTree.svelte | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/components/codeblocks/CodeblockTree.svelte b/src/components/codeblocks/CodeblockTree.svelte index e5e9c2ae..228288b8 100644 --- a/src/components/codeblocks/CodeblockTree.svelte +++ b/src/components/codeblocks/CodeblockTree.svelte @@ -36,9 +36,6 @@ export const update = () => { const max_depth = options.depth[1] ?? DEFAULT_MAX_DEPTH; - const fields = - options.fields ?? plugin.settings.edge_fields.map((f) => f.label); - const source_path = file_path ? file_path : $active_file_store @@ -47,7 +44,7 @@ const traversal_options = new TraversalOptions( [source_path], - fields, + options.fields, max_depth === Infinity ? DEFAULT_MAX_DEPTH : max_depth, !options["merge-fields"], ); From b1dc8bc0c364d1bea6af25677ad58e0c0b6c3f68 Mon Sep 17 00:00:00 2001 From: Moritz Jung Date: Wed, 8 May 2024 11:35:16 +0200 Subject: [PATCH 34/65] fix review comments --- wasm/src/graph_data.rs | 8 ++++---- wasm/src/graph_mermaid.rs | 6 +++--- wasm/src/utils.rs | 16 ++++++++++------ wasm/tests/graph.rs | 16 ++++++++-------- 4 files changed, 25 insertions(+), 21 deletions(-) diff --git a/wasm/src/graph_data.rs b/wasm/src/graph_data.rs index 1ead0da4..0b9e2901 100644 --- a/wasm/src/graph_data.rs +++ b/wasm/src/graph_data.rs @@ -84,16 +84,16 @@ impl EdgeData { for attribute in attributes { let data = match attribute.as_str() { "field" => Some(("field", self.edge_type.clone())), - "explicit" => Some(("explicit", (!self.explicit).to_string())), + "explicit" => Some(("explicit", self.explicit.to_string())), "source" => { - if !self.explicit { + if self.explicit { Some(("source", self.edge_source.clone())) } else { None } } "implied_kind" => { - if self.explicit { + if !self.explicit { Some(("implied_kind", self.edge_source.clone())) } else { None @@ -249,7 +249,7 @@ impl EdgeStruct { } #[wasm_bindgen(getter)] - pub fn implied(&self) -> bool { + pub fn explicit(&self) -> bool { self.edge.explicit } diff --git a/wasm/src/graph_mermaid.rs b/wasm/src/graph_mermaid.rs index 0744a892..f9b4b1e4 100644 --- a/wasm/src/graph_mermaid.rs +++ b/wasm/src/graph_mermaid.rs @@ -241,11 +241,11 @@ impl NoteGraph { .iter() .zip(backward.iter()) .all(|(a, b)| a.edge_type == b.edge_type); - let all_implied = forward + let all_implied = !forward .iter() .zip_longest(backward.iter()) - .all(|pair| match pair { - EitherOrBoth::Both(a, b) => a.explicit && b.explicit, + .any(|pair| match pair { + EitherOrBoth::Both(a, b) => a.explicit || b.explicit, EitherOrBoth::Left(a) => a.explicit, EitherOrBoth::Right(b) => b.explicit, }); diff --git a/wasm/src/utils.rs b/wasm/src/utils.rs index b38c3fde..d99b5ac2 100644 --- a/wasm/src/utils.rs +++ b/wasm/src/utils.rs @@ -11,6 +11,8 @@ extern "C" { pub type Logger; #[wasm_bindgen(method)] + pub fn feat(this: &Logger, message: &str); + #[wasm_bindgen(method)] pub fn debug(this: &Logger, message: &str); #[wasm_bindgen(method)] pub fn info(this: &Logger, message: &str); @@ -24,17 +26,19 @@ extern "C" { #[wasm_bindgen] extern "C" { #[wasm_bindgen(js_name = console)] - pub static LOGGER: Console; + pub static LOGGER: Logger; - pub type Console; + pub type Logger; + #[wasm_bindgen(method, js_name = log)] + pub fn feat(this: &Logger, message: &str); #[wasm_bindgen(method)] - pub fn debug(this: &Console, message: &str); + pub fn debug(this: &Logger, message: &str); #[wasm_bindgen(method, js_name = log)] - pub fn info(this: &Console, message: &str); + pub fn info(this: &Logger, message: &str); #[wasm_bindgen(method)] - pub fn warn(this: &Console, message: &str); + pub fn warn(this: &Logger, message: &str); #[wasm_bindgen(method)] - pub fn error(this: &Console, message: &str); + pub fn error(this: &Logger, message: &str); } pub struct PerfLogger { diff --git a/wasm/tests/graph.rs b/wasm/tests/graph.rs index fd3760ce..fe876c58 100644 --- a/wasm/tests/graph.rs +++ b/wasm/tests/graph.rs @@ -60,8 +60,8 @@ fn test_implied_edge_rules_reverse_direction() { .int_get_edge_by_name(&"1".to_string(), &"root".to_string(), &"up".to_string()) .unwrap(); - assert_eq!(up_edge_1.weight().explicit, true); - assert_eq!(up_edge_2.weight().explicit, true); + assert_eq!(up_edge_1.weight().explicit, false); + assert_eq!(up_edge_2.weight().explicit, false); graph.assert_correct_trackers(); } @@ -111,8 +111,8 @@ fn test_implied_edge_rules_sibling() { let same_edge_4 = graph.int_get_edge_by_name(&"1".to_string(), &"1".to_string(), &"same".to_string()); - assert_eq!(same_edge_1.weight().explicit, true); - assert_eq!(same_edge_2.weight().explicit, true); + assert_eq!(same_edge_1.weight().explicit, false); + assert_eq!(same_edge_2.weight().explicit, false); assert_eq!(same_edge_3.is_none(), true); assert_eq!(same_edge_4.is_none(), true); graph.assert_correct_trackers(); @@ -166,10 +166,10 @@ fn test_implied_edge_rules_sibling_can_loop() { .int_get_edge_by_name(&"1".to_string(), &"1".to_string(), &"same".to_string()) .unwrap(); - assert_eq!(same_edge_1.weight().explicit, true); - assert_eq!(same_edge_2.weight().explicit, true); - assert_eq!(same_edge_3.weight().explicit, true); - assert_eq!(same_edge_4.weight().explicit, true); + assert_eq!(same_edge_1.weight().explicit, false); + assert_eq!(same_edge_2.weight().explicit, false); + assert_eq!(same_edge_3.weight().explicit, false); + assert_eq!(same_edge_4.weight().explicit, false); graph.assert_correct_trackers(); } From 33199aa4101d7b94a2b21557145e019980da1dbc Mon Sep 17 00:00:00 2001 From: SkepticMystic Date: Thu, 9 May 2024 20:26:43 +0200 Subject: [PATCH 35/65] feat: Implement basic flatten rec_traversal_data func + switch to breadth_first --- wasm/src/graph_traversal.rs | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/wasm/src/graph_traversal.rs b/wasm/src/graph_traversal.rs index a4fc6ede..8aec15a7 100644 --- a/wasm/src/graph_traversal.rs +++ b/wasm/src/graph_traversal.rs @@ -217,6 +217,24 @@ impl RecTraversalData { } } +pub fn flatten_traversal_data(mut data: Vec) -> Vec { + let mut result = Vec::new(); + + for datum in data.drain(..) { + rec_flatten_traversal_data(datum, &mut result); + } + + result +} + +fn rec_flatten_traversal_data(mut data: RecTraversalData, result: &mut Vec) { + for child in data.children.drain(..) { + rec_flatten_traversal_data(child, result); + } + + result.push(data); +} + #[wasm_bindgen] #[derive(Clone, Debug)] pub struct RecTraversalResult { @@ -469,7 +487,7 @@ impl NoteGraph { let edge_types: &Vec = options.edge_types.as_ref().unwrap_or(&all_edge_types); for edge_type in edge_types { - let (nodes, edges) = self.int_traverse_depth_first( + let (nodes, edges) = self.int_traverse_breadth_first( entry_nodes.clone(), Some(&vec![edge_type.clone()]), options.max_depth, @@ -483,7 +501,7 @@ impl NoteGraph { Ok((node_list, edge_list)) } else { - Ok(self.int_traverse_depth_first( + Ok(self.int_traverse_breadth_first( entry_nodes, options.edge_types.as_ref(), options.max_depth, From 62678a7007cf578f3a472506f5db9209ec4a4717 Mon Sep 17 00:00:00 2001 From: SkepticMystic Date: Thu, 9 May 2024 21:57:00 +0200 Subject: [PATCH 36/65] Reimplement `flat` option on CodeblockTree --- .../codeblocks/CodeblockMarkmap.svelte | 13 +- .../codeblocks/CodeblockMermaid.svelte | 1 + .../codeblocks/CodeblockTree.svelte | 46 ++---- src/components/side_views/Matrix.svelte | 43 ++--- .../side_views/MatrixEdgeField.svelte | 13 +- src/graph/utils.ts | 154 +----------------- wasm/src/graph_traversal.rs | 41 ++--- 7 files changed, 65 insertions(+), 246 deletions(-) diff --git a/src/components/codeblocks/CodeblockMarkmap.svelte b/src/components/codeblocks/CodeblockMarkmap.svelte index b803fd1c..02249fee 100644 --- a/src/components/codeblocks/CodeblockMarkmap.svelte +++ b/src/components/codeblocks/CodeblockMarkmap.svelte @@ -1,4 +1,4 @@ -
@@ -121,16 +111,16 @@
{:else if error} -

{error.message}

+

{error}

{:else}

No paths found.

diff --git a/src/components/side_views/Matrix.svelte b/src/components/side_views/Matrix.svelte index 9476484c..bb442290 100644 --- a/src/components/side_views/Matrix.svelte +++ b/src/components/side_views/Matrix.svelte @@ -1,23 +1,20 @@
@@ -99,7 +72,11 @@ {#if grouped_out_edges}
{#each plugin.settings.edge_fields as field} - {@const edges = grouped_out_edges.get_sorted_edges(field.label, plugin.graph, sort)} + {@const edges = grouped_out_edges.get_sorted_edges( + field.label, + plugin.graph, + sort, + )} {#if edges?.length}
diff --git a/src/graph/utils.ts b/src/graph/utils.ts index 31fa6f43..5a38cf89 100644 --- a/src/graph/utils.ts +++ b/src/graph/utils.ts @@ -1,18 +1,7 @@ -import { - COMPLEX_EDGE_SORT_FIELD_PREFIXES, - type EdgeSortId, -} from "src/const/graph"; import type { ShowNodeOptions } from "src/interfaces/settings"; import { Paths } from "src/utils/paths"; -import type { - // BCEdge, - BCEdgeAttributes, - // BCNodeAttributes, -} from "./MyMultiGraph"; -import type { EdgeStruct, NodeData } from "wasm/pkg/breadcrumbs_graph_wasm"; - -// export const is_self_loop = (edge: Pick) => -// edge.source_id === edge.target_id; +import type { NodeData } from "wasm/pkg/breadcrumbs_graph_wasm"; +import type { BCEdgeAttributes } from "./MyMultiGraph"; export const stringify_node = ( node: NodeData, @@ -34,132 +23,6 @@ export const stringify_node = ( } }; -// UNUSED -// export const stringify_edge = ( -// edge: BCEdge, -// options?: { -// rtl?: boolean; -// edge_id?: boolean; -// show_node_options?: ShowNodeOptions; -// }, -// ) => { -// const source_id = Paths.show(edge.source_id, options?.show_node_options); -// const target_id = Paths.show(edge.target_id, options?.show_node_options); - -// const list = options?.rtl -// ? [target_id, `<-${edge.attr.field}-`, source_id] -// : [source_id, `-${edge.attr.field}->`, target_id]; - -// return list.join(" "); -// }; - -export type EdgeSorter = (a: EdgeStruct, b: EdgeStruct) => number; - -const sorters = { - path: (order) => (a, b) => - a.target.path.localeCompare(b.target.path) * order, - - basename: (order) => (a, b) => { - const [a_field, b_field] = [ - Paths.drop_folder(a.target.path), - Paths.drop_folder(b.target.path), - ]; - - return a_field.localeCompare(b_field) * order; - }, - - field: (order) => (a, b) => { - const [a_field, b_field] = [ - a.edge_type ?? "null", - b.edge_type ?? "null", - ]; - - return a_field.localeCompare(b_field) * order; - }, -} satisfies Partial EdgeSorter>>; - -export const get_edge_sorter: (sort: EdgeSortId) => EdgeSorter = (sort) => { - switch (sort.field) { - case "path": { - return sorters.path(sort.order); - } - - case "basename": { - return sorters.basename(sort.order); - } - - case "field": { - return sorters.field(sort.order); - } - - case "explicit": { - return (a, b) => { - if (a.implied === b.implied) { - return ( - a.edge_source.localeCompare(b.edge_source) * sort.order - ); - } else { - return a.implied ? -sort.order : sort.order; - } - }; - } - - default: { - // Rather check externally, so this should never happen - if ( - !COMPLEX_EDGE_SORT_FIELD_PREFIXES.some((f) => - sort.field.startsWith(f + ":"), - ) - ) { - throw new Error(`Invalid sort field: ${sort.field}`); - } - - switch (sort.field.split(":")[0]) { - // TODO(RUST): I think this has actually been reimplemented though - // BREAKING: Deprecate in favour of neighbour-field - // case "neighbour": - // case "neighbour-field": { - // const field = sort.field.split(":", 2).at(1); - // const cache: Record = {}; - - // return (a, b) => { - // const [a_neighbour, b_neighbour] = [ - // (cache[a.target_id] ??= graph - // .get_out_edges(a.target_id) - // .filter((e) => has_edge_attrs(e, { field })) - // .at(0)), - - // (cache[b.target_id] ??= graph - // .get_out_edges(b.target_id) - // .filter((e) => has_edge_attrs(e, { field })) - // .at(0)), - // ]; - - // if (!a_neighbour || !b_neighbour) { - // // NOTE: This puts the node with no neighbours last - // // Which makes sense, I think. It simulates a traversal, where the node with no neighbours is the end of the path - // return a_neighbour - // ? -sort.order - // : b_neighbour - // ? sort.order - // : 0; - // } else { - // return sorters.path(sort.order)( - // a_neighbour, - // b_neighbour, - // ); - // } - // }; - // } - - default: { - return (_a, _b) => sort.order; - } - } - } - } -}; - export type EdgeAttrFilters = Partial< Pick > & @@ -167,16 +30,3 @@ export type EdgeAttrFilters = Partial< $or_fields: string[]; $or_target_ids: string[]; }>; - -// // NOTE: Technically the source and implied_kind fields could be implemented here, but missions for now -// export const has_edge_attrs = (edge: BCEdge, attrs?: EdgeAttrFilters) => -// attrs === undefined || -// [ -// attrs.field === undefined || edge.attr.field === attrs.field, -// attrs.explicit === undefined || edge.attr.explicit === attrs.explicit, - -// attrs.$or_fields === undefined || -// attrs.$or_fields.includes(edge.attr.field ?? "null"), -// attrs.$or_target_ids === undefined || -// attrs.$or_target_ids.includes(edge.target_id), -// ].every(Boolean); diff --git a/wasm/src/graph_traversal.rs b/wasm/src/graph_traversal.rs index 8aec15a7..dd8840b6 100644 --- a/wasm/src/graph_traversal.rs +++ b/wasm/src/graph_traversal.rs @@ -10,7 +10,7 @@ use crate::{ graph_data::{EdgeStruct, NGEdgeIndex, NGEdgeRef, NGNodeIndex}, utils::{ BreadthFirstTraversalDataStructure, DepthFirstTraversalDataStructure, - GraphTraversalDataStructure, NoteGraphError, Result, + GraphTraversalDataStructure, NoteGraphError, Result, LOGGER, }, }; @@ -217,24 +217,6 @@ impl RecTraversalData { } } -pub fn flatten_traversal_data(mut data: Vec) -> Vec { - let mut result = Vec::new(); - - for datum in data.drain(..) { - rec_flatten_traversal_data(datum, &mut result); - } - - result -} - -fn rec_flatten_traversal_data(mut data: RecTraversalData, result: &mut Vec) { - for child in data.children.drain(..) { - rec_flatten_traversal_data(child, result); - } - - result.push(data); -} - #[wasm_bindgen] #[derive(Clone, Debug)] pub struct RecTraversalResult { @@ -307,6 +289,19 @@ impl RecTraversalResult { paths } + /// Flattens the traversal data by removing the tree structure and deduplicating the edges by their target_path + pub fn flatten(&mut self) { + let mut data = Vec::new(); + + for datum in self.data.drain(..) { + rec_flatten_traversal_data(datum, &mut data); + } + + data.dedup_by(|a, b| a.edge.target.path == b.edge.target.path); + + self.data = data; + } + pub fn sort(&mut self, graph: &NoteGraph, sorter: &EdgeSorter) { for datum in &mut self.data { datum.rec_sort_children(graph, sorter); @@ -316,6 +311,14 @@ impl RecTraversalResult { } } +fn rec_flatten_traversal_data(mut data: RecTraversalData, result: &mut Vec) { + for child in data.children.drain(..) { + rec_flatten_traversal_data(child, result); + } + + result.push(data); +} + #[wasm_bindgen] impl NoteGraph { pub fn rec_traverse(&self, options: TraversalOptions) -> Result { From 25e118b5060dab65db30037489ff870e3dad209f Mon Sep 17 00:00:00 2001 From: Moritz Jung Date: Fri, 10 May 2024 17:47:04 +0200 Subject: [PATCH 37/65] make clippy happy; work on todos --- package.json | 1 + src/commands/freeze_edges/index.ts | 2 +- src/commands/list_index/index.ts | 14 +++---- .../codeblocks/CodeblockMermaid.svelte | 27 +++++++++--- .../codeblocks/CodeblockTree.svelte | 2 - src/components/page_views/TrailView.svelte | 13 ------ wasm/.cargo/config.toml | 2 - wasm/src/edge_sorting.rs | 41 ++++++++++--------- wasm/src/graph.rs | 10 ++++- wasm/src/graph_data.rs | 21 +++++++++- wasm/src/graph_mermaid.rs | 2 + wasm/src/graph_rules.rs | 2 +- wasm/src/graph_traversal.rs | 4 +- wasm/src/lib.rs | 2 +- wasm/src/utils.rs | 4 +- 15 files changed, 85 insertions(+), 62 deletions(-) diff --git a/package.json b/package.json index 8250b960..4e94f5a4 100644 --- a/package.json +++ b/package.json @@ -18,6 +18,7 @@ "coverage:ui": "vitest run --coverage --coverage.include 'src/**' --coverage.reporter html && npx vite preview --outDir ./coverage --open", "wasm:build": "cd wasm && wasm-pack build --target web", "wasm:fmt": "cd wasm && cargo fmt", + "wasm:lint": "cd wasm && cargo clippy", "wasm:test": "cd wasm && wasm-pack test --node --features test" }, "keywords": [], diff --git a/src/commands/freeze_edges/index.ts b/src/commands/freeze_edges/index.ts index ada96549..e7b728dd 100644 --- a/src/commands/freeze_edges/index.ts +++ b/src/commands/freeze_edges/index.ts @@ -12,7 +12,7 @@ export const freeze_implied_edges_to_note = async ( ) => { const implied_edges = plugin.graph.get_outgoing_edges(source_file.path).filter( // Don't freeze a note to itself (self_is_sibling) - (e) => !e.is_self_loop() && e.implied, + (e) => !e.is_self_loop() && !e.explicit, ); await drop_crumbs(plugin, source_file, implied_edges, options); diff --git a/src/commands/list_index/index.ts b/src/commands/list_index/index.ts index be0eda52..470a81f6 100644 --- a/src/commands/list_index/index.ts +++ b/src/commands/list_index/index.ts @@ -81,16 +81,12 @@ export namespace ListIndex { false, ); + const traversal_result = graph.rec_traverse(traversal_options); + const edge_sorter = create_edge_sorter(options.edge_sort_id.field, options.edge_sort_id.order === -1); + traversal_result.sort(graph, edge_sorter); + return edge_tree_to_list_index( - // TODO - Traverse.sort_edge_tree( - // Traverse.build_tree(graph, start_node, options, (e) => - // has_edge_attrs(e, { $or_fields: options.fields }), - // ), - graph, - graph.rec_traverse(traversal_options).data, - create_edge_sorter(options.edge_sort_id.field, options.edge_sort_id.order === -1), - ), + traversal_result.data, options, ); } diff --git a/src/components/codeblocks/CodeblockMermaid.svelte b/src/components/codeblocks/CodeblockMermaid.svelte index fe8bf6a0..6ff04278 100644 --- a/src/components/codeblocks/CodeblockMermaid.svelte +++ b/src/components/codeblocks/CodeblockMermaid.svelte @@ -19,6 +19,7 @@ import { remove_nullish_keys } from "src/utils/objects"; import { Paths } from "src/utils/paths"; import { Links } from "src/utils/links"; + import { active_file_store } from "src/stores/active_file"; export let plugin: BreadcrumbsPlugin; export let options: ICodeblock["Options"]; @@ -28,12 +29,23 @@ const DEFAULT_MAX_DEPTH = 100; let code: string = ""; - let error: NoteGraphError | undefined = undefined; + let error: string | undefined = undefined; - // TODO: has_node export const update = () => { const max_depth = options.depth[1] ?? DEFAULT_MAX_DEPTH; + const source_path = file_path + ? file_path + : $active_file_store + ? $active_file_store.path + : ""; + + if (!plugin.graph.has_node(source_path)) { + code = ""; + error = "The file does not exist in the graph."; + return; + } + const traversal_options = new TraversalOptions( [file_path], options.fields, @@ -83,11 +95,16 @@ } catch (e) { log.error("Error generating mermaid graph", e); + code = ""; if (e instanceof NoteGraphError) { - code = ""; - error = e; + error = e.message; + } else { + error = + "An error occurred while updating the codeblock tree. Check the console for more information (Ctrl + Shift + I)."; } } + + code = code; }; onMount(() => { @@ -229,7 +246,7 @@ />
{:else if error} -

{error.message}

+

{error}

{:else}

No paths found.

diff --git a/src/components/codeblocks/CodeblockTree.svelte b/src/components/codeblocks/CodeblockTree.svelte index f3048577..562f1f54 100644 --- a/src/components/codeblocks/CodeblockTree.svelte +++ b/src/components/codeblocks/CodeblockTree.svelte @@ -83,8 +83,6 @@ log.debug(timer.elapsedMessage("CodeblockTree initial traversal")); }); - - // TODO(RUST): reimplement all this logic
diff --git a/src/components/page_views/TrailView.svelte b/src/components/page_views/TrailView.svelte index d0b40428..36c7a76b 100644 --- a/src/components/page_views/TrailView.svelte +++ b/src/components/page_views/TrailView.svelte @@ -25,8 +25,6 @@ plugin.settings.views.page.trail.field_group_labels, ); - // $: log.debug("edge_field_labels", edge_field_labels); - $: traversal_options = new TraversalOptions( [file_path], edge_field_labels, @@ -34,20 +32,9 @@ !plugin.settings.views.page.trail.merge_fields, ); - // $: log.debug("traversal_options", traversal_options.toString()); - $: traversal_data = plugin.graph.rec_traverse(traversal_options); - $: all_paths = traversal_data.to_paths(); - // $: all_paths.forEach((path) => log.debug(path.toString())); - - // $: all_paths = plugin.graph.hasNode(file_path) - // ? plugin.settings.views.page.trail.merge_fields - // ? base_traversal({ $or_fields: edge_field_labels }) - // : edge_field_labels.flatMap((field) => base_traversal({ field })) - // : []; - $: selected_paths = plugin.settings.views.page.trail.selection === "all" ? all_paths diff --git a/wasm/.cargo/config.toml b/wasm/.cargo/config.toml index 18eb756e..e69de29b 100644 --- a/wasm/.cargo/config.toml +++ b/wasm/.cargo/config.toml @@ -1,2 +0,0 @@ -[build] -rustflags = ["-C", "target-feature=+simd128"] \ No newline at end of file diff --git a/wasm/src/edge_sorting.rs b/wasm/src/edge_sorting.rs index beee2138..033b3e7b 100644 --- a/wasm/src/edge_sorting.rs +++ b/wasm/src/edge_sorting.rs @@ -1,3 +1,5 @@ +use std::str::FromStr; + use petgraph::visit::EdgeRef; use wasm_bindgen::prelude::*; @@ -17,25 +19,26 @@ pub enum SortField { Neighbour(String), } -impl SortField { - pub fn from_str(s: &str) -> Option { +impl FromStr for SortField { + type Err = NoteGraphError; + + fn from_str(s: &str) -> Result { match s { - "path" => Some(SortField::Path), - "basename" => Some(SortField::Basename), - "field" => Some(SortField::EdgeType), - "explicit" => Some(SortField::Implied), - s if s.starts_with("neighbour-field:") => Some(SortField::Neighbour( + "path" => Ok(SortField::Path), + "basename" => Ok(SortField::Basename), + "field" => Ok(SortField::EdgeType), + "explicit" => Ok(SortField::Implied), + s if s.starts_with("neighbour-field:") => Ok(SortField::Neighbour( s["neighbour-field:".len()..].to_string(), )), - _ => None, + _ => Err(NoteGraphError::new("Invalid sort field")), } } } #[wasm_bindgen] pub fn create_edge_sorter(field: String, reverse: bool) -> Result { - let sort_field = - SortField::from_str(&field).ok_or(NoteGraphError::new("Invalid sort field"))?; + let sort_field = SortField::from_str(&field)?; Ok(EdgeSorter::new(sort_field, reverse)) } @@ -76,16 +79,16 @@ impl EdgeSorter { EdgeSorter { field, reverse } } - pub fn sort_edges<'a>(&self, graph: &'a NoteGraph, edges: &mut Vec) { + pub fn sort_edges(&self, graph: &NoteGraph, edges: &mut [EdgeStruct]) { let ordering = self.get_edge_ordering(graph); - edges.sort_by(|a, b| self.apply_edge_ordering(&ordering, a, b)); + edges.sort_by(|a, b| self.apply_edge_ordering(ordering.as_ref(), a, b)); } - pub fn sort_traversal_data<'a>(&self, graph: &'a NoteGraph, edges: &mut Vec) { + pub fn sort_traversal_data(&self, graph: &NoteGraph, edges: &mut [RecTraversalData]) { let ordering = self.get_edge_ordering(graph); - edges.sort_by(|a, b| self.apply_edge_ordering(&ordering, &a.edge, &b.edge)); + edges.sort_by(|a, b| self.apply_edge_ordering(ordering.as_ref(), &a.edge, &b.edge)); } fn get_edge_ordering<'a>(&self, graph: &'a NoteGraph) -> Box { @@ -102,7 +105,7 @@ impl EdgeSorter { fn apply_edge_ordering<'a>( &self, - ordering: &Box, + ordering: &(dyn EdgeOrdering + 'a), a: &EdgeStruct, b: &EdgeStruct, ) -> std::cmp::Ordering { @@ -135,7 +138,7 @@ impl EdgeOrdering for BasenameOrdering { let a_basename = a.target.path.split('/').last().unwrap(); let b_basename = b.target.path.split('/').last().unwrap(); - a_basename.cmp(&b_basename) + a_basename.cmp(b_basename) } } @@ -182,15 +185,13 @@ impl<'a> EdgeOrdering for NeighbourOrdering<'a> { let a_neighbour = self .graph .int_iter_outgoing_edges(a.target_index) - .filter(|edge| edge.weight().matches_edge_filter(Some(&neighbour_field))) - .next() + .find(|edge| edge.weight().matches_edge_filter(Some(&neighbour_field))) .and_then(|x| self.graph.int_get_node_weight(x.target()).ok()); let b_neighbour = self .graph .int_iter_outgoing_edges(b.target_index) - .filter(|edge| edge.weight().matches_edge_filter(Some(&neighbour_field))) - .next() + .find(|edge| edge.weight().matches_edge_filter(Some(&neighbour_field))) .and_then(|x| self.graph.int_get_node_weight(x.target()).ok()); match (a_neighbour, b_neighbour) { diff --git a/wasm/src/graph.rs b/wasm/src/graph.rs index 9cc8ea1d..0bc8ddb3 100644 --- a/wasm/src/graph.rs +++ b/wasm/src/graph.rs @@ -608,8 +608,14 @@ impl NoteGraph { )); } - // TODO(RUST): also update the other things like aliases - self.int_set_node_resolved(node_index, true)?; + let node = self.graph.node_weight_mut(node_index); + + match node { + Some(node) => { + node.override_with_construction_data(construction_data); + } + None => return Err(NoteGraphError::new("Node not found")), + } } None => { self.int_add_node(construction_data)?; diff --git a/wasm/src/graph_data.rs b/wasm/src/graph_data.rs index 0b9e2901..357f5ea7 100644 --- a/wasm/src/graph_data.rs +++ b/wasm/src/graph_data.rs @@ -71,7 +71,7 @@ impl EdgeData { impl EdgeData { pub fn matches_edge_filter(&self, edge_types: Option<&Vec>) -> bool { - edge_matches_edge_filter(&self, edge_types) + edge_matches_edge_filter(self, edge_types) } pub fn get_attribute_label(&self, attributes: &Vec) -> String { @@ -207,6 +207,17 @@ impl NodeData { ignore_out_edges: false, } } + + pub fn override_with_construction_data(&mut self, data: &GraphConstructionNodeData) { + assert_eq!( + self.path, data.path, + "Can not override with data for another node." + ); + self.aliases = data.aliases.clone(); + self.resolved = data.resolved; + self.ignore_in_edges = data.ignore_in_edges; + self.ignore_out_edges = data.ignore_out_edges; + } } #[wasm_bindgen] @@ -335,7 +346,7 @@ impl EdgeStruct { } #[wasm_bindgen] -#[derive(Clone, Debug, PartialEq)] +#[derive(Clone, Debug, PartialEq, Default)] pub struct EdgeList { #[wasm_bindgen(skip)] pub edges: Vec, @@ -382,6 +393,7 @@ impl EdgeList { } #[wasm_bindgen] +#[derive(Clone, Debug, PartialEq, Default)] pub struct GroupedEdgeList { #[wasm_bindgen(skip)] pub edges: HashMap, @@ -431,4 +443,9 @@ impl GroupedEdgeList { .get(edge_type) .map(|edge_list| edge_list.get_sorted_edges(graph, sorter)) } + + #[wasm_bindgen(js_name = toString)] + pub fn to_fancy_string(&self) -> String { + format!("{:#?}", self) + } } diff --git a/wasm/src/graph_mermaid.rs b/wasm/src/graph_mermaid.rs index f9b4b1e4..286239f6 100644 --- a/wasm/src/graph_mermaid.rs +++ b/wasm/src/graph_mermaid.rs @@ -116,6 +116,8 @@ impl NoteGraph { let (nodes, edges) = self.int_traverse_basic(&traversal_options)?; + // TODO(RUST): option to sort edges + // utils::log(format!("{:#?}", nodes)); // utils::log(format!("{:#?}", edges)); diff --git a/wasm/src/graph_rules.rs b/wasm/src/graph_rules.rs index b773706d..c4461580 100644 --- a/wasm/src/graph_rules.rs +++ b/wasm/src/graph_rules.rs @@ -64,7 +64,7 @@ impl TransitiveGraphRule { pub fn get_name(&self) -> String { if self.name.is_empty() { - return self.stringify(); + self.stringify() } else { self.name.clone() } diff --git a/wasm/src/graph_traversal.rs b/wasm/src/graph_traversal.rs index dd8840b6..4b36bbbd 100644 --- a/wasm/src/graph_traversal.rs +++ b/wasm/src/graph_traversal.rs @@ -10,7 +10,7 @@ use crate::{ graph_data::{EdgeStruct, NGEdgeIndex, NGEdgeRef, NGNodeIndex}, utils::{ BreadthFirstTraversalDataStructure, DepthFirstTraversalDataStructure, - GraphTraversalDataStructure, NoteGraphError, Result, LOGGER, + GraphTraversalDataStructure, NoteGraphError, Result, }, }; @@ -76,7 +76,7 @@ impl TraversalOptions { } #[wasm_bindgen] -#[derive(Clone, Debug)] +#[derive(Clone, Debug, PartialEq)] pub struct Path { edges: Vec, } diff --git a/wasm/src/lib.rs b/wasm/src/lib.rs index aa077993..0865bb5e 100644 --- a/wasm/src/lib.rs +++ b/wasm/src/lib.rs @@ -14,7 +14,7 @@ use wasm_bindgen::prelude::*; pub fn create_graph() -> graph::NoteGraph { console_error_panic_hook::set_once(); - utils::LOGGER.debug("Hello, from WASM!".into()); + utils::LOGGER.debug("Hello, from WASM!"); graph::NoteGraph::new() } diff --git a/wasm/src/utils.rs b/wasm/src/utils.rs index d99b5ac2..539310f6 100644 --- a/wasm/src/utils.rs +++ b/wasm/src/utils.rs @@ -68,11 +68,11 @@ impl PerfLogger { } pub fn stop_split(&mut self) { - self.splits.last_mut().map(|split| { + if let Some(split) = self.splits.last_mut() { if !split.stopped() { split.stop() } - }); + } } pub fn stop(&mut self) { From 134eda276980ace2fd519c02d39c12d9cac5738e Mon Sep 17 00:00:00 2001 From: Moritz Jung Date: Fri, 10 May 2024 19:58:32 +0200 Subject: [PATCH 38/65] mermaid edge sorting --- src/commands/stats/index.ts | 4 +- .../codeblocks/CodeblockMermaid.svelte | 13 +++--- .../codeblocks/CodeblockTree.svelte | 6 +-- src/utils/mermaid.ts | 1 + wasm/src/edge_sorting.rs | 1 + wasm/src/graph_mermaid.rs | 43 +++++++++++-------- 6 files changed, 38 insertions(+), 30 deletions(-) diff --git a/src/commands/stats/index.ts b/src/commands/stats/index.ts index 755b625a..923f4e3f 100644 --- a/src/commands/stats/index.ts +++ b/src/commands/stats/index.ts @@ -71,11 +71,11 @@ export const get_graph_stats = ( } }); - const explicit = String(!edge.implied); + const explicit = String(edge.explicit); stats.edges.explicit[explicit] = (stats.edges.explicit[explicit] || 0) + 1; - if (!edge.implied) { + if (edge.explicit) { stats.edges.source[edge.edge_source] = (stats.edges.source[edge.edge_source] || 0) + 1; } else { diff --git a/src/components/codeblocks/CodeblockMermaid.svelte b/src/components/codeblocks/CodeblockMermaid.svelte index 6ff04278..c7db802d 100644 --- a/src/components/codeblocks/CodeblockMermaid.svelte +++ b/src/components/codeblocks/CodeblockMermaid.svelte @@ -15,6 +15,7 @@ NodeData, NoteGraphError, TraversalOptions, + create_edge_sorter, } from "wasm/pkg/breadcrumbs_graph_wasm"; import { remove_nullish_keys } from "src/utils/objects"; import { Paths } from "src/utils/paths"; @@ -34,11 +35,7 @@ export const update = () => { const max_depth = options.depth[1] ?? DEFAULT_MAX_DEPTH; - const source_path = file_path - ? file_path - : $active_file_store - ? $active_file_store.path - : ""; + const source_path = options["start-note"] || file_path || $active_file_store?.path || ""; if (!plugin.graph.has_node(source_path)) { code = ""; @@ -58,6 +55,11 @@ defaultRenderer: options["mermaid-renderer"], }); + const sort = create_edge_sorter( + options.sort.field, + options.sort.order === -1, + ); + const mermaid_options = new MermaidGraphOptions( file_path, `%%{ init: { "flowchart": ${JSON.stringify(flowchart_init)} } }%%`, @@ -65,6 +67,7 @@ options["mermaid-direction"] ?? "LR", true, options["show-attributes"] ?? [], + sort, (node: NodeData) => { const node_path = node.path; const file = plugin.app.vault.getFileByPath(node_path); diff --git a/src/components/codeblocks/CodeblockTree.svelte b/src/components/codeblocks/CodeblockTree.svelte index 562f1f54..643517f7 100644 --- a/src/components/codeblocks/CodeblockTree.svelte +++ b/src/components/codeblocks/CodeblockTree.svelte @@ -36,11 +36,7 @@ export const update = () => { const max_depth = options.depth[1] ?? DEFAULT_MAX_DEPTH; - const source_path = file_path - ? file_path - : $active_file_store - ? $active_file_store.path - : ""; + const source_path = options["start-note"] || file_path || $active_file_store?.path || ""; if (!plugin.graph.has_node(source_path)) { tree = undefined; diff --git a/src/utils/mermaid.ts b/src/utils/mermaid.ts index 0c7240c6..95238d1e 100644 --- a/src/utils/mermaid.ts +++ b/src/utils/mermaid.ts @@ -312,6 +312,7 @@ const from_transitive_rule = ( "LR", false, ["field"], + undefined, (node: NodeData) => node.path, false, ) diff --git a/wasm/src/edge_sorting.rs b/wasm/src/edge_sorting.rs index 033b3e7b..cf3f883f 100644 --- a/wasm/src/edge_sorting.rs +++ b/wasm/src/edge_sorting.rs @@ -69,6 +69,7 @@ pub fn sort_edges( } #[wasm_bindgen] +#[derive(Clone, Debug)] pub struct EdgeSorter { field: SortField, reverse: bool, diff --git a/wasm/src/graph_mermaid.rs b/wasm/src/graph_mermaid.rs index 286239f6..15ce8f88 100644 --- a/wasm/src/graph_mermaid.rs +++ b/wasm/src/graph_mermaid.rs @@ -1,17 +1,15 @@ use std::collections::HashMap; use itertools::{EitherOrBoth, Itertools}; -use petgraph::{ - stable_graph::{EdgeReference, NodeIndex}, - visit::EdgeRef, -}; +use petgraph::stable_graph::NodeIndex; use wasm_bindgen::prelude::*; use web_time::Instant; use crate::{ + edge_sorting::EdgeSorter, graph::NoteGraph, - graph_data::EdgeData, - graph_traversal::{EdgeVec, TraversalOptions}, + graph_data::{EdgeData, EdgeStruct}, + graph_traversal::TraversalOptions, utils::{NoteGraphError, Result}, }; @@ -29,6 +27,7 @@ pub struct MermaidGraphOptions { direction: String, collapse_opposing_edges: bool, edge_label_attributes: Vec, + edge_sorter: Option, node_label_fn: Option, link_nodes: bool, } @@ -43,6 +42,7 @@ impl MermaidGraphOptions { direction: String, collapse_opposing_edges: bool, edge_label_attributes: Vec, + edge_sorter: Option, node_label_fn: Option, link_nodes: bool, ) -> MermaidGraphOptions { @@ -53,6 +53,7 @@ impl MermaidGraphOptions { direction, collapse_opposing_edges, edge_label_attributes, + edge_sorter, node_label_fn, link_nodes, } @@ -115,6 +116,14 @@ impl NoteGraph { let now = Instant::now(); let (nodes, edges) = self.int_traverse_basic(&traversal_options)?; + let mut edge_structs = edges + .iter() + .filter_map(|edge| EdgeStruct::from_edge_ref(edge.1, self)) + .collect::>(); + + if let Some(edge_sorter) = &diagram_options.edge_sorter { + edge_sorter.sort_edges(self, &mut edge_structs); + } // TODO(RUST): option to sort edges @@ -136,7 +145,7 @@ impl NoteGraph { ); // accumulate edges by direction, so that we can collapse them in the next step - let accumulated_edges = NoteGraph::int_accumulate_edges(edges); + let accumulated_edges = NoteGraph::int_accumulate_edges(edge_structs); // utils::log(format!("{:#?}", accumulated_edges)); @@ -293,36 +302,34 @@ impl NoteGraph { } } - pub fn int_accumulate_edges( - edges: EdgeVec>, - ) -> AccumulatedEdgeHashMap { + pub fn int_accumulate_edges(edges: Vec) -> AccumulatedEdgeHashMap { let mut accumulated_edges: AccumulatedEdgeHashMap = HashMap::new(); // sorting the two node indices in the edge tuple could be a speedup, since then only one lookup is needed - for (_, edge_ref) in edges { - let forward_dir = (edge_ref.source(), edge_ref.target()); + for edge_struct in edges { + let forward_dir = (edge_struct.source_index, edge_struct.target_index); let entry1 = accumulated_edges.get_mut(&forward_dir); match entry1 { Some((_, _, forward, _)) => { - forward.push(edge_ref.weight().clone()); + forward.push(edge_struct.edge); } None => { - let backward_dir = (edge_ref.target(), edge_ref.source()); + let backward_dir = (edge_struct.target_index, edge_struct.source_index); let entry2 = accumulated_edges.get_mut(&backward_dir); match entry2 { Some((_, _, _, backward)) => { - backward.push(edge_ref.weight().clone()); + backward.push(edge_struct.edge); } None => { accumulated_edges.insert( forward_dir, ( - edge_ref.source(), - edge_ref.target(), - vec![edge_ref.weight().clone()], + edge_struct.source_index, + edge_struct.target_index, + vec![edge_struct.edge], Vec::new(), ), ); From 8f3ec65a8174141b8a40a0c0f08a3ca3ce12707b Mon Sep 17 00:00:00 2001 From: Moritz Jung Date: Mon, 13 May 2024 14:57:13 +0200 Subject: [PATCH 39/65] Update src/components/side_views/MatrixEdgeField.svelte Co-authored-by: Ross <70717676+SkepticMystic@users.noreply.github.com> --- src/components/side_views/MatrixEdgeField.svelte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/side_views/MatrixEdgeField.svelte b/src/components/side_views/MatrixEdgeField.svelte index 698e763c..5c3f37a5 100644 --- a/src/components/side_views/MatrixEdgeField.svelte +++ b/src/components/side_views/MatrixEdgeField.svelte @@ -58,7 +58,7 @@ Date: Mon, 13 May 2024 14:58:00 +0200 Subject: [PATCH 40/65] Update wasm/src/graph_mermaid.rs Co-authored-by: Ross <70717676+SkepticMystic@users.noreply.github.com> --- wasm/src/graph_mermaid.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/wasm/src/graph_mermaid.rs b/wasm/src/graph_mermaid.rs index 15ce8f88..fb2ce366 100644 --- a/wasm/src/graph_mermaid.rs +++ b/wasm/src/graph_mermaid.rs @@ -125,7 +125,6 @@ impl NoteGraph { edge_sorter.sort_edges(self, &mut edge_structs); } - // TODO(RUST): option to sort edges // utils::log(format!("{:#?}", nodes)); // utils::log(format!("{:#?}", edges)); From 52f6b2fd4807761a22665b73e9b89d7888ec144e Mon Sep 17 00:00:00 2001 From: Moritz Jung Date: Wed, 15 May 2024 14:15:30 +0200 Subject: [PATCH 41/65] performance improvements to views --- package.json | 2 + src/commands/jump/index.ts | 4 +- src/commands/list_index/index.ts | 2 +- src/commands/thread/index.ts | 2 +- src/components/EdgeLink.svelte | 2 +- src/components/NestedEdgeList.svelte | 27 +- .../codeblocks/CodeblockTree.svelte | 10 +- src/components/page_views/TrailView.svelte | 87 +++--- .../page_views/TrailViewGrid.svelte | 4 +- src/components/side_views/TreeView.svelte | 13 +- wasm/src/graph.rs | 6 +- wasm/src/graph_data.rs | 72 +---- wasm/src/graph_mermaid.rs | 25 +- wasm/src/graph_traversal.rs | 291 +++++++++++++----- 14 files changed, 320 insertions(+), 227 deletions(-) diff --git a/package.json b/package.json index 4e94f5a4..9169a076 100644 --- a/package.json +++ b/package.json @@ -17,6 +17,8 @@ "test": "vitest", "coverage:ui": "vitest run --coverage --coverage.include 'src/**' --coverage.reporter html && npx vite preview --outDir ./coverage --open", "wasm:build": "cd wasm && wasm-pack build --target web", + "wasm:dev": "cd wasm && wasm-pack build --dev --target web", + "wasm:profile": "cd wasm && wasm-pack build --profiling --target web", "wasm:fmt": "cd wasm && cargo fmt", "wasm:lint": "cd wasm && cargo clippy", "wasm:test": "cd wasm && wasm-pack test --node --features test" diff --git a/src/commands/jump/index.ts b/src/commands/jump/index.ts index bfae2c52..5cce2833 100644 --- a/src/commands/jump/index.ts +++ b/src/commands/jump/index.ts @@ -15,7 +15,7 @@ export const jump_to_neighbour = async ( .filter( (e) => e.matches_edge_filter(options.fields) && - e.target.path !== active_file.path, + e.target_path !== active_file.path, ); if (!matches.length) { @@ -25,7 +25,7 @@ export const jump_to_neighbour = async ( return; } else { await plugin.app.workspace.openLinkText( - matches[0].target.path, + matches[0].target_path, active_file.path, ); } diff --git a/src/commands/list_index/index.ts b/src/commands/list_index/index.ts index 470a81f6..13a2d445 100644 --- a/src/commands/list_index/index.ts +++ b/src/commands/list_index/index.ts @@ -55,7 +55,7 @@ export namespace ListIndex { show_node_options: options.show_node_options, }); - const link = Links.ify(edge.target.path, display, { + const link = Links.ify(edge.target_path, display, { link_kind: options.link_kind, }); diff --git a/src/commands/thread/index.ts b/src/commands/thread/index.ts index 26df7bb3..68be5a59 100644 --- a/src/commands/thread/index.ts +++ b/src/commands/thread/index.ts @@ -74,7 +74,7 @@ export const thread = async ( const edge = plugin.graph .get_outgoing_edges(source_file.path) .find( - (e) => e.edge_type === field && e.target.path === target_file!.path, + (e) => e.edge_type === field && e.target_path === target_file!.path, ); if (!edge) return; diff --git a/src/components/EdgeLink.svelte b/src/components/EdgeLink.svelte index 2d249285..40273df7 100644 --- a/src/components/EdgeLink.svelte +++ b/src/components/EdgeLink.svelte @@ -28,7 +28,7 @@ {display} path={target_node.path} resolved={target_node.resolved} - cls="{cls} BC-edge {!edge.implied + cls="{cls} BC-edge {edge.explicit ? 'BC-edge-explicit' : `BC-edge-implied BC-edge-implied-${edge.edge_source}`}" /> diff --git a/src/components/NestedEdgeList.svelte b/src/components/NestedEdgeList.svelte index de4c7a02..4893a140 100644 --- a/src/components/NestedEdgeList.svelte +++ b/src/components/NestedEdgeList.svelte @@ -5,31 +5,35 @@ import EdgeLink from "./EdgeLink.svelte"; import ChevronOpener from "./button/ChevronOpener.svelte"; import TreeItemFlair from "./obsidian/TreeItemFlair.svelte"; - import { EdgeSorter, sort_traversal_data, type RecTraversalData } from "wasm/pkg/breadcrumbs_graph_wasm"; + import { FlatRecTraversalData } from "wasm/pkg/breadcrumbs_graph_wasm"; export let plugin: BreadcrumbsPlugin; - export let tree: RecTraversalData[]; + // export let tree: RecTraversalData[]; + export let data: FlatRecTraversalData[]; + export let items: Uint32Array; export let open_signal: boolean | null; export let show_node_options: ShowNodeOptions; export let show_attributes: EdgeAttribute[] | undefined; - let opens = tree.map(() => true); + let opens = Array(items.length).fill(true); $: if (open_signal === true) { - opens = opens.map(() => true); + opens = Array(items.length).fill(true); open_signal = null; } else if (open_signal === false) { - opens = opens.map(() => false); + opens = Array(items.length).fill(false); open_signal = null; } -{#each tree as item, i} +{#each items as item, i} + {@const datum = data[item]} + {@const children = datum.children}
- {#if item.children.length} + {#if children.length}
@@ -38,7 +42,7 @@
@@ -46,18 +50,19 @@ {#if show_attributes?.length} {/if}
- {#if item.children.length} + {#if children.length}
{/if} diff --git a/src/components/codeblocks/CodeblockTree.svelte b/src/components/codeblocks/CodeblockTree.svelte index 643517f7..da83874b 100644 --- a/src/components/codeblocks/CodeblockTree.svelte +++ b/src/components/codeblocks/CodeblockTree.svelte @@ -8,6 +8,7 @@ import { Timer } from "src/utils/timer"; import { onMount } from "svelte"; import { + FlatRecTraversalResult, NoteGraphError, RecTraversalResult, TraversalOptions, @@ -31,6 +32,7 @@ const DEFAULT_MAX_DEPTH = 100; let tree: RecTraversalResult | undefined = undefined; + let data: FlatRecTraversalResult | undefined = undefined; let error: string | undefined = undefined; export const update = () => { @@ -55,12 +57,14 @@ tree = plugin.graph.rec_traverse(traversal_options); if (options.flat) tree.flatten(); tree.sort(plugin.graph, sort); + data = tree.to_flat(); error = undefined; } catch (e) { log.error("Error updating codeblock tree", e); tree = undefined; + data = undefined; if (e instanceof NoteGraphError) { error = e.message; } else { @@ -70,6 +74,7 @@ } tree = tree; + data = data; }; onMount(() => { @@ -90,7 +95,7 @@ {/if} - {#if tree && !tree.is_empty()} + {#if tree && !tree.is_empty() && data}
diff --git a/src/components/page_views/TrailView.svelte b/src/components/page_views/TrailView.svelte index 36c7a76b..47122a6e 100644 --- a/src/components/page_views/TrailView.svelte +++ b/src/components/page_views/TrailView.svelte @@ -35,51 +35,58 @@ $: traversal_data = plugin.graph.rec_traverse(traversal_options); $: all_paths = traversal_data.to_paths(); - $: selected_paths = - plugin.settings.views.page.trail.selection === "all" - ? all_paths - : plugin.settings.views.page.trail.selection === "shortest" - ? all_paths.slice(0, 1) - : plugin.settings.views.page.trail.selection === "longest" - ? all_paths.slice(-1) - : []; - - $: MAX_DEPTH = Math.max(0, ...selected_paths.map((p) => p.length())); + // $: selected_paths = + // plugin.settings.views.page.trail.selection === "all" + // ? all_paths + // : plugin.settings.views.page.trail.selection === "shortest" + // ? all_paths.slice(0, 1) + // : plugin.settings.views.page.trail.selection === "longest" + // ? all_paths.slice(-1) + // : []; + + $: selected_paths = all_paths.select( + plugin.settings.views.page.trail.selection, + ); + + + $: MAX_DEPTH = Math.max(0, selected_paths.max_depth()); $: depth = Math.min( MAX_DEPTH, plugin.settings.views.page.trail.default_depth, ); - // Slice the paths to the chosen max depth. - $: truncated_paths = selected_paths.map((path) => path.truncate(depth)); - - // Remove duplicates by the target_ids of the path. - $: deduped_paths = - // There are no duplicates if the depth is the max depth. - // The traversal wouldn't add them in the first place. - depth === MAX_DEPTH - ? truncated_paths - : remove_duplicates_by_equals(truncated_paths, (a, b) => a.equals(b)); - - // NOTE: Only sort after slicing, so that the depth is taken into account. - $: sorted_paths = deduped_paths.sort((a, b) => { - const len_diff = b.length() - a.length(); - - // Focus on run-length first - if (len_diff !== 0) { - return len_diff; - } - // Then focus on the alphabetical order of the target_ids - else { - const a_target = a.get_first_target(); - const b_target = b.get_first_target(); - - if (a_target === undefined && b_target === undefined) return 0; - if (a_target === undefined) return -1; - if (b_target === undefined) return 1; - else return a_target.localeCompare(b_target); - } - }); + // // Slice the paths to the chosen max depth. + // $: truncated_paths = selected_paths.map((path) => path.truncate(depth)); + + // // Remove duplicates by the target_ids of the path. + // $: deduped_paths = + // // There are no duplicates if the depth is the max depth. + // // The traversal wouldn't add them in the first place. + // depth === MAX_DEPTH + // ? truncated_paths + // : remove_duplicates_by_equals(truncated_paths, (a, b) => a.equals(b)); + + // // NOTE: Only sort after slicing, so that the depth is taken into account. + // $: sorted_paths = deduped_paths.sort((a, b) => { + // const len_diff = b.length() - a.length(); + + // // Focus on run-length first + // if (len_diff !== 0) { + // return len_diff; + // } + // // Then focus on the alphabetical order of the target_ids + // else { + // const a_target = a.get_first_target(); + // const b_target = b.get_first_target(); + + // if (a_target === undefined && b_target === undefined) return 0; + // if (a_target === undefined) return -1; + // if (b_target === undefined) return 1; + // else return a_target.localeCompare(b_target); + // } + // }); + + $: sorted_paths = selected_paths.process(depth);
diff --git a/src/components/page_views/TrailViewGrid.svelte b/src/components/page_views/TrailViewGrid.svelte index 0e3c08cd..97c18793 100644 --- a/src/components/page_views/TrailViewGrid.svelte +++ b/src/components/page_views/TrailViewGrid.svelte @@ -13,10 +13,12 @@ const reversed = all_paths.map((path) => path.reverse_edges); + // this should happen in wasm const square = ensure_square_array(reversed, null, true); + // this as well const col_runs = transpose(square).map((col) => - gather_by_runs(col, (e) => (e ? e.target.path : null)), + gather_by_runs(col, (e) => (e ? e.target_path : null)), ); diff --git a/src/components/side_views/TreeView.svelte b/src/components/side_views/TreeView.svelte index 31cf8f75..5a342b36 100644 --- a/src/components/side_views/TreeView.svelte +++ b/src/components/side_views/TreeView.svelte @@ -30,6 +30,8 @@ field_group_labels, ); + $: console.log(edge_field_labels); + $: tree = $active_file_store && plugin.graph.has_node($active_file_store.path) ? plugin.graph.rec_traverse( @@ -44,7 +46,9 @@ $: sort = create_edge_sorter(edge_sort_id.field, edge_sort_id.order === -1); - $: tree?.sort(plugin.graph, sort); + $: {tree?.sort(plugin.graph, sort); tree = tree}; + + $: data = tree?.to_flat();
@@ -85,13 +89,14 @@
- {#key tree || sort} - {#if tree && !tree.is_empty()} + {#key data} + {#if data && !data.is_empty()} {:else} diff --git a/wasm/src/graph.rs b/wasm/src/graph.rs index 0bc8ddb3..77953aa3 100644 --- a/wasm/src/graph.rs +++ b/wasm/src/graph.rs @@ -602,7 +602,7 @@ impl NoteGraph { // if it does, we assert that it is not resolved match self.int_get_node_index(&construction_data.path) { Some(node_index) => { - if self.int_get_node_weight(node_index)?.resolved() { + if self.int_get_node_weight(node_index)?.resolved { return Err(NoteGraphError::new( "There already exists a resolved node with the same name.", )); @@ -628,7 +628,7 @@ impl NoteGraph { pub fn int_safe_remove_node(&mut self, node: &String) -> Result<()> { match self.int_get_node_index(node) { Some(index) => { - if !self.int_get_node_weight(index)?.resolved() { + if !self.int_get_node_weight(index)?.resolved { return Err(NoteGraphError::new("Cannot remove an unresolved node")); } @@ -669,7 +669,7 @@ impl NoteGraph { .edge_endpoints(edge) .ok_or(NoteGraphError::new("Edge not found"))?; let target_data = self.int_get_node_weight(target)?.clone(); - let target_resolved = target_data.resolved(); + let target_resolved = target_data.resolved; self.graph.remove_edge(edge); diff --git a/wasm/src/graph_data.rs b/wasm/src/graph_data.rs index 357f5ea7..1ad197f7 100644 --- a/wasm/src/graph_data.rs +++ b/wasm/src/graph_data.rs @@ -21,13 +21,11 @@ pub type NGEdgeRef<'a> = EdgeReference<'a, EdgeData, u32>; #[wasm_bindgen] #[derive(Clone, Debug, PartialEq)] pub struct EdgeData { - #[wasm_bindgen(skip)] + #[wasm_bindgen(getter_with_clone)] pub edge_type: String, - #[wasm_bindgen(skip)] + #[wasm_bindgen(getter_with_clone)] pub edge_source: String, - #[wasm_bindgen(skip)] pub explicit: bool, - #[wasm_bindgen(skip)] pub round: u8, } @@ -43,26 +41,6 @@ impl EdgeData { } } - #[wasm_bindgen(getter)] - pub fn edge_type(&self) -> String { - self.edge_type.clone() - } - - #[wasm_bindgen(getter)] - pub fn edge_source(&self) -> String { - self.edge_source.clone() - } - - #[wasm_bindgen(getter)] - pub fn explicit(&self) -> bool { - self.explicit - } - - #[wasm_bindgen(getter)] - pub fn round(&self) -> u8 { - self.round - } - #[wasm_bindgen(js_name = toString)] pub fn to_fancy_string(&self) -> String { format!("{:#?}", self) @@ -125,15 +103,12 @@ impl EdgeData { #[wasm_bindgen] #[derive(Clone, Debug, PartialEq)] pub struct NodeData { - #[wasm_bindgen(skip)] + #[wasm_bindgen(getter_with_clone)] pub path: String, - #[wasm_bindgen(skip)] + #[wasm_bindgen(getter_with_clone)] pub aliases: Vec, - #[wasm_bindgen(skip)] pub resolved: bool, - #[wasm_bindgen(skip)] pub ignore_in_edges: bool, - #[wasm_bindgen(skip)] pub ignore_out_edges: bool, } @@ -156,31 +131,6 @@ impl NodeData { } } - #[wasm_bindgen(getter)] - pub fn path(&self) -> String { - self.path.clone() - } - - #[wasm_bindgen(getter)] - pub fn aliases(&self) -> Vec { - self.aliases.clone() - } - - #[wasm_bindgen(getter)] - pub fn resolved(&self) -> bool { - self.resolved - } - - #[wasm_bindgen(getter)] - pub fn ignore_in_edges(&self) -> bool { - self.ignore_in_edges - } - - #[wasm_bindgen(getter)] - pub fn ignore_out_edges(&self) -> bool { - self.ignore_out_edges - } - #[wasm_bindgen(js_name = toString)] pub fn to_fancy_string(&self) -> String { format!("{:#?}", self) @@ -223,15 +173,15 @@ impl NodeData { #[wasm_bindgen] #[derive(Clone, Debug, PartialEq)] pub struct EdgeStruct { - #[wasm_bindgen(skip)] + #[wasm_bindgen(getter_with_clone)] pub source: NodeData, #[wasm_bindgen(skip)] pub source_index: NGNodeIndex, - #[wasm_bindgen(skip)] + #[wasm_bindgen(getter_with_clone)] pub target: NodeData, #[wasm_bindgen(skip)] pub target_index: NGNodeIndex, - #[wasm_bindgen(skip)] + #[wasm_bindgen(getter_with_clone)] pub edge: EdgeData, #[wasm_bindgen(skip)] pub edge_index: NGEdgeIndex, @@ -240,13 +190,13 @@ pub struct EdgeStruct { #[wasm_bindgen] impl EdgeStruct { #[wasm_bindgen(getter)] - pub fn source(&self) -> NodeData { - self.source.clone() + pub fn source_path(&self) -> String { + self.source.path.clone() } #[wasm_bindgen(getter)] - pub fn target(&self) -> NodeData { - self.target.clone() + pub fn target_path(&self) -> String { + self.target.path.clone() } #[wasm_bindgen(getter)] diff --git a/wasm/src/graph_mermaid.rs b/wasm/src/graph_mermaid.rs index fb2ce366..5cfa1739 100644 --- a/wasm/src/graph_mermaid.rs +++ b/wasm/src/graph_mermaid.rs @@ -68,28 +68,14 @@ impl MermaidGraphOptions { #[wasm_bindgen] #[derive(Clone, Debug)] pub struct MermaidGraphData { - mermaid: String, - traversal_time: u64, - total_time: u64, + #[wasm_bindgen(getter_with_clone)] + pub mermaid: String, + pub traversal_time: u64, + pub total_time: u64, } #[wasm_bindgen] impl MermaidGraphData { - #[wasm_bindgen(getter)] - pub fn mermaid(&self) -> String { - self.mermaid.clone() - } - - #[wasm_bindgen(getter)] - pub fn traversal_time(&self) -> u64 { - self.traversal_time - } - - #[wasm_bindgen(getter)] - pub fn total_time(&self) -> u64 { - self.total_time - } - #[wasm_bindgen(js_name = toString)] pub fn to_fancy_string(&self) -> String { format!("{:#?}", self) @@ -120,12 +106,11 @@ impl NoteGraph { .iter() .filter_map(|edge| EdgeStruct::from_edge_ref(edge.1, self)) .collect::>(); - + if let Some(edge_sorter) = &diagram_options.edge_sorter { edge_sorter.sort_edges(self, &mut edge_structs); } - // utils::log(format!("{:#?}", nodes)); // utils::log(format!("{:#?}", edges)); diff --git a/wasm/src/graph_traversal.rs b/wasm/src/graph_traversal.rs index 4b36bbbd..c253f00a 100644 --- a/wasm/src/graph_traversal.rs +++ b/wasm/src/graph_traversal.rs @@ -1,5 +1,6 @@ use std::collections::HashSet; +use itertools::Itertools; use petgraph::visit::EdgeRef; use wasm_bindgen::prelude::*; use web_time::Instant; @@ -23,13 +24,15 @@ pub type NodeEdgeVec = (NodeVec, EdgeVec); #[wasm_bindgen] #[derive(Clone, Debug)] pub struct TraversalOptions { - entry_nodes: Vec, + #[wasm_bindgen(getter_with_clone)] + pub entry_nodes: Vec, /// if this is None, all edge types will be traversed - edge_types: Option>, - max_depth: u32, + #[wasm_bindgen(getter_with_clone)] + pub edge_types: Option>, + pub max_depth: u32, /// if true, multiple traversals - one for each edge type - will be performed and the results will be combined /// if false, one traversal over all edge types will be performed - separate_edges: bool, + pub separate_edges: bool, } #[wasm_bindgen] @@ -49,26 +52,6 @@ impl TraversalOptions { } } - #[wasm_bindgen(getter)] - pub fn entry_nodes(&self) -> Vec { - self.entry_nodes.clone() - } - - #[wasm_bindgen(getter)] - pub fn edge_types(&self) -> Option> { - self.edge_types.clone() - } - - #[wasm_bindgen(getter)] - pub fn max_depth(&self) -> u32 { - self.max_depth - } - - #[wasm_bindgen(getter)] - pub fn separate_edges(&self) -> bool { - self.separate_edges - } - #[wasm_bindgen(js_name = toString)] pub fn to_fancy_string(&self) -> String { format!("{:#?}", self) @@ -78,7 +61,8 @@ impl TraversalOptions { #[wasm_bindgen] #[derive(Clone, Debug, PartialEq)] pub struct Path { - edges: Vec, + #[wasm_bindgen(getter_with_clone)] + pub edges: Vec, } #[wasm_bindgen] @@ -93,11 +77,6 @@ impl Path { copy } - #[wasm_bindgen(getter)] - pub fn edges(&self) -> Vec { - self.edges.clone() - } - #[wasm_bindgen(getter)] pub fn reverse_edges(&self) -> Vec { self.edges.iter().rev().cloned().collect() @@ -127,20 +106,91 @@ impl Path { } } +#[wasm_bindgen] +#[derive(Clone, Debug, PartialEq)] +pub struct PathList { + paths: Vec, +} + +#[wasm_bindgen] +impl PathList { + pub fn to_paths(&self) -> Vec { + self.paths.clone() + } + + #[wasm_bindgen(js_name = toString)] + pub fn to_fancy_string(&self) -> String { + format!("{:#?}", self) + } + + pub fn select(&self, selection: String) -> PathList { + match selection.as_str() { + "shortest" => self.shortest(), + "longest" => self.longest(), + _ => self.clone(), + } + } + + pub fn max_depth(&self) -> usize { + self.paths + .iter() + .map(|path| path.length()) + .max() + .unwrap_or(0) + } + + pub fn process(&self, depth: usize) -> Vec { + self.paths + .iter() + .map(|path| path.truncate(depth)) + .sorted_by(|a, b| { + let a_len = a.edges.len(); + let b_len = b.edges.len(); + + a_len + .cmp(&b_len) + .then_with(|| a.get_first_target().cmp(&b.get_first_target())) + }) + .dedup() + .collect_vec() + } +} + +impl PathList { + /// creates new path list, assumes that the paths are already sorted by length + pub fn new(paths: Vec) -> PathList { + PathList { paths } + } + + pub fn shortest(&self) -> PathList { + if let Some(shortest) = self.paths.first() { + PathList::new(vec![shortest.clone()]) + } else { + PathList::new(Vec::new()) + } + } + + pub fn longest(&self) -> PathList { + if let Some(longest) = self.paths.last() { + PathList::new(vec![longest.clone()]) + } else { + PathList::new(Vec::new()) + } + } +} + #[wasm_bindgen] #[derive(Clone, Debug)] pub struct RecTraversalData { /// the edge struct that was traversed - #[wasm_bindgen(skip)] + #[wasm_bindgen(getter_with_clone)] pub edge: EdgeStruct, /// the depth of the node in the traversal - #[wasm_bindgen(skip)] pub depth: u32, /// the number of total children of the node, so also children of children - #[wasm_bindgen(skip)] pub number_of_children: u32, /// the children of the node - #[wasm_bindgen(skip)] + #[wasm_bindgen(getter_with_clone)] pub children: Vec, } @@ -161,26 +211,6 @@ impl RecTraversalData { } } - #[wasm_bindgen(getter)] - pub fn edge(&self) -> EdgeStruct { - self.edge.clone() - } - - #[wasm_bindgen(getter)] - pub fn depth(&self) -> u32 { - self.depth - } - - #[wasm_bindgen(getter)] - pub fn children(&self) -> Vec { - self.children.clone() - } - - #[wasm_bindgen(setter)] - pub fn set_children(&mut self, children: Vec) { - self.children = children; - } - pub fn rec_sort_children(&mut self, graph: &NoteGraph, sorter: &EdgeSorter) { for child in &mut self.children { child.rec_sort_children(graph, sorter); @@ -220,10 +250,11 @@ impl RecTraversalData { #[wasm_bindgen] #[derive(Clone, Debug)] pub struct RecTraversalResult { - data: Vec, - node_count: u32, - max_depth: u32, - traversal_time: u64, + #[wasm_bindgen(getter_with_clone)] + pub data: Vec, + pub node_count: u32, + pub max_depth: u32, + pub traversal_time: u64, } #[wasm_bindgen] @@ -243,26 +274,6 @@ impl RecTraversalResult { } } - #[wasm_bindgen(getter)] - pub fn data(&self) -> Vec { - self.data.clone() - } - - #[wasm_bindgen(getter)] - pub fn node_count(&self) -> u32 { - self.node_count - } - - #[wasm_bindgen(getter)] - pub fn max_depth(&self) -> u32 { - self.max_depth - } - - #[wasm_bindgen(getter)] - pub fn traversal_time(&self) -> u64 { - self.traversal_time - } - pub fn is_empty(&self) -> bool { self.data.is_empty() } @@ -272,7 +283,7 @@ impl RecTraversalResult { format!("{:#?}", self) } - pub fn to_paths(&self) -> Vec { + pub fn to_paths(&self) -> PathList { let mut paths = Vec::new(); for datum in &self.data { @@ -286,7 +297,7 @@ impl RecTraversalResult { a_len.cmp(&b_len) }); - paths + PathList::new(paths) } /// Flattens the traversal data by removing the tree structure and deduplicating the edges by their target_path @@ -309,6 +320,10 @@ impl RecTraversalResult { sorter.sort_traversal_data(graph, &mut self.data); } + + pub fn to_flat(&self) -> FlatRecTraversalResult { + FlatRecTraversalResult::from_rec_traversal_result(self.clone()) + } } fn rec_flatten_traversal_data(mut data: RecTraversalData, result: &mut Vec) { @@ -319,6 +334,122 @@ fn rec_flatten_traversal_data(mut data: RecTraversalData, result: &mut Vec, +} + +impl FlatRecTraversalData { + pub fn new( + edge: EdgeStruct, + depth: u32, + number_of_children: u32, + children: Vec, + ) -> FlatRecTraversalData { + FlatRecTraversalData { + edge, + depth, + number_of_children, + children, + } + } +} + +#[wasm_bindgen] +impl FlatRecTraversalData { + pub fn get_attribute_label(&self, attributes: Vec) -> String { + self.edge.get_attribute_label(attributes) + } +} + +#[wasm_bindgen] +#[derive(Clone, Debug)] +pub struct FlatRecTraversalResult { + #[wasm_bindgen(getter_with_clone)] + pub data: Vec, + pub node_count: u32, + pub max_depth: u32, + pub traversal_time: u64, + #[wasm_bindgen(getter_with_clone)] + pub entry_nodes: Vec, +} + +impl FlatRecTraversalResult { + pub fn new( + data: Vec, + node_count: u32, + max_depth: u32, + traversal_time: u64, + entry_nodes: Vec, + ) -> FlatRecTraversalResult { + FlatRecTraversalResult { + data, + node_count, + max_depth, + traversal_time, + entry_nodes, + } + } + + pub fn from_rec_traversal_result(result: RecTraversalResult) -> FlatRecTraversalResult { + let mut flat_data = Vec::new(); + let mut entry_nodes = Vec::new(); + + for datum in result.data { + entry_nodes.push(rec_flatten_traversal_data_to_flat(datum, &mut flat_data)); + } + + FlatRecTraversalResult::new( + flat_data, + result.node_count, + result.max_depth, + result.traversal_time, + entry_nodes, + ) + } +} + +#[wasm_bindgen] +impl FlatRecTraversalResult { + #[wasm_bindgen(js_name = toString)] + pub fn to_fancy_string(&self) -> String { + format!("{:#?}", self) + } + + pub fn is_empty(&self) -> bool { + self.data.is_empty() + } +} + +fn rec_flatten_traversal_data_to_flat( + mut data: RecTraversalData, + result: &mut Vec, +) -> usize { + let children = data + .children + .drain(..) + .map(|datum| rec_flatten_traversal_data_to_flat(datum, result)) + .collect(); + + result.push(FlatRecTraversalData::new( + data.edge, + data.depth, + data.number_of_children, + children, + )); + result.len() - 1 +} + #[wasm_bindgen] impl NoteGraph { pub fn rec_traverse(&self, options: TraversalOptions) -> Result { From aecbf93744c6f18a2bb3ee8a09a1f3c71cec3812 Mon Sep 17 00:00:00 2001 From: Moritz Jung Date: Sat, 25 May 2024 23:21:57 +0200 Subject: [PATCH 42/65] some small improvements --- src/components/EdgeLink.svelte | 6 ++- src/components/page_views/TrailView.svelte | 45 ++++++++++++---------- wasm/src/graph.rs | 30 ++++----------- wasm/src/graph_data.rs | 10 +++++ 4 files changed, 47 insertions(+), 44 deletions(-) diff --git a/src/components/EdgeLink.svelte b/src/components/EdgeLink.svelte index 40273df7..8def16a9 100644 --- a/src/components/EdgeLink.svelte +++ b/src/components/EdgeLink.svelte @@ -7,6 +7,8 @@ export let edge: EdgeStruct; export let plugin: BreadcrumbsPlugin; + // TODO(RUST): make this into a rust struct that we pass. + // Then we can make stringify_node a method of the edge struct with the options struct as a parameter. export let show_node_options: ShowNodeOptions; export let cls = ""; @@ -26,8 +28,8 @@
{#key sorted_paths} - {#if sorted_paths.length} + {#if sorted_paths && sorted_paths.length}
bool { + self.source.resolved + } + + #[wasm_bindgen(getter)] + pub fn target_resolved(&self) -> bool { + self.target.resolved + } + #[wasm_bindgen(getter)] pub fn edge_type(&self) -> String { self.edge.edge_type.clone() From d668311d2b57950de6810996335f0ce2b8f70428 Mon Sep 17 00:00:00 2001 From: Moritz Jung Date: Mon, 27 May 2024 20:41:02 +0200 Subject: [PATCH 43/65] do node stringification in rust --- esbuild.config.mjs | 2 +- src/components/EdgeLink.svelte | 18 +----- src/components/NestedEdgeList.svelte | 17 ++++- .../page_views/TrailViewGrid.svelte | 17 ++++- .../page_views/TrailViewPath.svelte | 17 ++++- src/components/side_views/TreeView.svelte | 37 ++++++----- wasm/src/graph_data.rs | 63 ++++++++++++++++++- 7 files changed, 130 insertions(+), 41 deletions(-) diff --git a/esbuild.config.mjs b/esbuild.config.mjs index a0fde384..6de62f80 100644 --- a/esbuild.config.mjs +++ b/esbuild.config.mjs @@ -70,7 +70,7 @@ const context = await esbuild.context({ outfile: "main.js", plugins: [ esbuildSvelte({ - compilerOptions: { css: true }, + compilerOptions: { css: true, dev: !prod }, preprocess: sveltePreprocess(), }), wasmPlugin, diff --git a/src/components/EdgeLink.svelte b/src/components/EdgeLink.svelte index 8def16a9..bfba5385 100644 --- a/src/components/EdgeLink.svelte +++ b/src/components/EdgeLink.svelte @@ -3,26 +3,14 @@ import { stringify_node } from "src/graph/utils"; import type { ShowNodeOptions } from "src/interfaces/settings"; import BreadcrumbsPlugin from "src/main"; - import type { EdgeStruct } from "wasm/pkg/breadcrumbs_graph_wasm"; + import type { EdgeStruct, NodeStringifyOptions } from "wasm/pkg/breadcrumbs_graph_wasm"; export let edge: EdgeStruct; export let plugin: BreadcrumbsPlugin; - // TODO(RUST): make this into a rust struct that we pass. - // Then we can make stringify_node a method of the edge struct with the options struct as a parameter. - export let show_node_options: ShowNodeOptions; + export let node_stringify_options: NodeStringifyOptions; export let cls = ""; - let target_node = edge.target; - - const { dendron_note } = plugin.settings.explicit_edge_sources; - - const display = stringify_node(target_node, { - show_node_options, - trim_basename_delimiter: - dendron_note.enabled && dendron_note.display_trimmed - ? dendron_note.delimiter - : undefined, - }); + const display = edge.stringify_target(node_stringify_options); {#each items as item, i} @@ -43,7 +56,7 @@
diff --git a/src/components/page_views/TrailViewGrid.svelte b/src/components/page_views/TrailViewGrid.svelte index 97c18793..ce136ad1 100644 --- a/src/components/page_views/TrailViewGrid.svelte +++ b/src/components/page_views/TrailViewGrid.svelte @@ -6,7 +6,7 @@ transpose, } from "src/utils/arrays"; import EdgeLink from "../EdgeLink.svelte"; - import type { Path } from "wasm/pkg/breadcrumbs_graph_wasm"; + import { NodeStringifyOptions, type Path } from "wasm/pkg/breadcrumbs_graph_wasm"; export let plugin: BreadcrumbsPlugin; export let all_paths: Path[]; @@ -20,6 +20,18 @@ const col_runs = transpose(square).map((col) => gather_by_runs(col, (e) => (e ? e.target_path : null)), ); + + const { dendron_note } = plugin.settings.explicit_edge_sources; + + const show_node_options = plugin.settings.views.page.trail.show_node_options + const node_stringify_options = new NodeStringifyOptions( + show_node_options.ext, + show_node_options.folder, + show_node_options.alias, + dendron_note.enabled && dendron_note.display_trimmed + ? dendron_note.delimiter + : undefined, + ); |sibling-in-law| spouse -// /** Find all paths of nodes connected by edges that pair-wise match the attrs in the chain */ -// const get_transitive_chain_target_ids = ( -// graph: BCGraph, -// start_node: string, -// chain: Partial[], -// edge_filter?: (item: TraversalStackItem) => boolean, -// ) => { -// const target_ids: string[] = []; - -// Traverse.breadth_first( -// graph, -// start_node, -// (item) => { -// // Only push the target_id if we're at the end of the chain -// if (item.depth === chain.length - 1) { -// target_ids.push(item.edge.target_id); -// } -// }, -// (item) => -// // Ensures we don't go over the chain length ("max_depth") -// chain[item.depth] && -// // Check if the edge has the attrs we're looking for -// has_edge_attrs(item.edge, chain[item.depth]) && -// (!edge_filter || edge_filter(item)), -// ); - -// return target_ids; -// }; - -export const Traverse = { -// breadth_first, -// gather_items, -// build_tree, -// flatten_tree, -// tree_to_all_paths, - - sort_edge_tree, - -// get_transitive_chain_target_ids, -}; diff --git a/src/graph/utils.ts b/src/graph/utils.ts index 5a38cf89..60d7340e 100644 --- a/src/graph/utils.ts +++ b/src/graph/utils.ts @@ -1,27 +1,19 @@ +import type BreadcrumbsPlugin from "src/main"; import type { ShowNodeOptions } from "src/interfaces/settings"; -import { Paths } from "src/utils/paths"; -import type { NodeData } from "wasm/pkg/breadcrumbs_graph_wasm"; -import type { BCEdgeAttributes } from "./MyMultiGraph"; +import { NodeStringifyOptions } from "wasm/pkg/breadcrumbs_graph_wasm"; -export const stringify_node = ( - node: NodeData, - options?: { - show_node_options?: ShowNodeOptions; - trim_basename_delimiter?: string; - }, -) => { - if (options?.show_node_options?.alias && node.aliases?.length) { - return node.aliases.at(0)!; - } else if (options?.trim_basename_delimiter) { - return Paths.drop_ext(node.path) - .split("/") - .pop()! - .split(options.trim_basename_delimiter) - .last()!; - } else { - return Paths.show(node.path, options?.show_node_options); - } -}; +export function toNodeStringifyOptions(plugin: BreadcrumbsPlugin, options: ShowNodeOptions): NodeStringifyOptions { + const { dendron_note } = plugin.settings.explicit_edge_sources; + + return new NodeStringifyOptions( + options.ext, + options.folder, + options.alias, + dendron_note.enabled && dendron_note.display_trimmed + ? dendron_note.delimiter + : undefined, + ); +} export type EdgeAttrFilters = Partial< Pick @@ -30,3 +22,34 @@ export type EdgeAttrFilters = Partial< $or_fields: string[]; $or_target_ids: string[]; }>; + + import type { ExplicitEdgeSource } from "src/const/graph"; + +export const EDGE_ATTRIBUTES = [ + "field", + "explicit", + "source", + "implied_kind", + "round", +] as const; + +export type EdgeAttribute = (typeof EDGE_ATTRIBUTES)[number]; + +export type BCEdgeAttributes = { + field: string; +} & ( + | { + explicit: true; + source: ExplicitEdgeSource; + } + | { + explicit: false; + implied_kind: `transitive:${string}`; + /** Which round of implied_building this edge got added in. + * Starts at 1 - you can think of real edges as being added in round 0. + * The way {@link BCGraph.safe_add_directed_edge} works, currently only the first instance of an edge will be added. + * If the same edge tries again in a future round, _that_ one will be blocked. + */ + round: number; + } +); \ No newline at end of file diff --git a/src/interfaces/settings.ts b/src/interfaces/settings.ts index 4c205952..d99c5c47 100644 --- a/src/interfaces/settings.ts +++ b/src/interfaces/settings.ts @@ -1,6 +1,6 @@ import type { ListIndex } from "src/commands/list_index"; import type { EdgeSortId } from "src/const/graph"; -import type { BCEdgeAttributes, EdgeAttribute } from "src/graph/MyMultiGraph"; +import type { BCEdgeAttributes, EdgeAttribute } from "src/graph/utils"; import type { EdgeAttrFilters } from "src/graph/utils"; import type { LogLevels } from "src/logger"; diff --git a/src/main.ts b/src/main.ts index 52f37ffa..b0e91972 100644 --- a/src/main.ts +++ b/src/main.ts @@ -43,16 +43,6 @@ export default class BreadcrumbsPlugin extends Plugin { graph!: NoteGraph; api!: BCAPI; events!: Events; - /** - * @deprecated - */ - debounced_refresh!: (options?: { - rebuild_graph?: boolean; - active_file_store?: boolean; - redraw_page_views?: boolean; - redraw_side_views?: true; - redraw_codeblocks?: boolean; - }) => void; async onload() { // Settings @@ -86,10 +76,6 @@ export default class BreadcrumbsPlugin extends Plugin { queueMicrotask(() => this.events.trigger(BCEvent.GRAPH_UPDATE)); }); - // ten milliseconds debounce to prevent multiple refreshes in quick succession - // not perfect, but i can't think of a better way to do this rn - this.debounced_refresh = debounce((options) => this.refresh(options), 10, true); - /// Migrations this.settings = migrate_old_settings(this.settings); await this.saveSettings(); @@ -354,50 +340,6 @@ export default class BreadcrumbsPlugin extends Plugin { this.events.trigger(BCEvent.REDRAW_SIDE_VIEWS); } - /** rebuild_graph, then react by updating active_file_store and redrawing page_views. - * Optionally disable any of these steps. - * - * @deprecated - */ - async refresh(options?: { - rebuild_graph?: boolean; - active_file_store?: boolean; - redraw_page_views?: boolean; - redraw_side_views?: true; - redraw_codeblocks?: boolean; - }) { - // Rebuild the graph - if (options?.rebuild_graph !== false) { - this.rebuildGraph(); - } - - // _Then_ react - if (options?.active_file_store !== false) { - active_file_store.refresh(this.app); - } - - if (options?.redraw_page_views !== false) { - - } - - // if (options?.redraw_codeblocks !== false) { - // Codeblocks.update_all(); - // } - - if (options?.redraw_side_views === true) { - this.app.workspace - .getLeavesOfType(VIEW_IDS.matrix) - .forEach((leaf) => { - (leaf.view as MatrixView).onOpen(); - }); - this.app.workspace - .getLeavesOfType(VIEW_IDS.tree) - .forEach((leaf) => { - (leaf.view as TreeView).onOpen(); - }); - } - } - // SOURCE: https://docs.obsidian.md/Plugins/User+interface/Views async activateView(view_id: string, options?: { side?: "left" | "right" }) { const { workspace } = this.app; diff --git a/src/menus/ShowAttributesMenu.ts b/src/menus/ShowAttributesMenu.ts index 27f9f030..090f94d4 100644 --- a/src/menus/ShowAttributesMenu.ts +++ b/src/menus/ShowAttributesMenu.ts @@ -1,5 +1,5 @@ import { Menu } from "obsidian"; -import { EDGE_ATTRIBUTES, type EdgeAttribute } from "src/graph/MyMultiGraph"; +import { EDGE_ATTRIBUTES, type EdgeAttribute } from "src/graph/utils"; export const ShowAttributesSelectorMenu = ({ cb, diff --git a/src/modals/CreateListIndexModal.ts b/src/modals/CreateListIndexModal.ts index 59d60fd0..6719b665 100644 --- a/src/modals/CreateListIndexModal.ts +++ b/src/modals/CreateListIndexModal.ts @@ -105,7 +105,7 @@ export class CreateListIndexModal extends Modal { log.debug("build_list_index options", this.options); const list_index = ListIndex.build( - plugin.graph, + plugin, this.active_file!.path, this.options, ); diff --git a/src/utils/drop_crumb.ts b/src/utils/drop_crumb.ts index 1b7918f3..a67cdb24 100644 --- a/src/utils/drop_crumb.ts +++ b/src/utils/drop_crumb.ts @@ -13,19 +13,21 @@ import { Paths } from "./paths"; const linkify_edge = ( plugin: BreadcrumbsPlugin, - { source, target }: EdgeStruct, + struct: EdgeStruct, ) => { + const target_path = struct.target_path(plugin.graph); + // target_id is a full path - const target_file = plugin.app.vault.getFileByPath(target.path); + const target_file = plugin.app.vault.getFileByPath(target_path); if (!target_file) { - return `[[${Paths.drop_ext(target.path)}]]`; + return `[[${Paths.drop_ext(target_path)}]]`; } else { return plugin.app.fileManager.generateMarkdownLink( target_file, - source.path, + struct.source_path(plugin.graph), undefined, - target.aliases?.at(0), + struct.target_data(plugin.graph).aliases?.at(0), ); } }; diff --git a/wasm/src/edge_sorting.rs b/wasm/src/edge_sorting.rs index a6c366c3..e9e039a2 100644 --- a/wasm/src/edge_sorting.rs +++ b/wasm/src/edge_sorting.rs @@ -83,13 +83,13 @@ impl EdgeSorter { pub fn sort_edges(&self, graph: &NoteGraph, edges: &mut [EdgeStruct]) { let ordering = self.get_edge_ordering(graph); - edges.sort_by(|a, b| self.apply_edge_ordering(ordering.as_ref(), a, b)); + edges.sort_by(|a, b| self.apply_edge_ordering(graph, ordering.as_ref(), a, b)); } pub fn sort_traversal_data(&self, graph: &NoteGraph, edges: &mut [RecTraversalData]) { let ordering = self.get_edge_ordering(graph); - edges.sort_by(|a, b| self.apply_edge_ordering(ordering.as_ref(), &a.edge, &b.edge)); + edges.sort_by(|a, b| self.apply_edge_ordering(graph, ordering.as_ref(), &a.edge, &b.edge)); } fn get_edge_ordering<'a>(&self, graph: &'a NoteGraph) -> Box { @@ -106,11 +106,12 @@ impl EdgeSorter { fn apply_edge_ordering<'a>( &self, + graph: &NoteGraph, ordering: &(dyn EdgeOrdering + 'a), a: &EdgeStruct, b: &EdgeStruct, ) -> std::cmp::Ordering { - let ordering = ordering.compare(a, b); + let ordering = ordering.compare(graph, a, b); if self.reverse { ordering.reverse() @@ -121,23 +122,25 @@ impl EdgeSorter { } pub trait EdgeOrdering { - fn compare(&self, a: &EdgeStruct, b: &EdgeStruct) -> std::cmp::Ordering; + fn compare(&self, graph: &NoteGraph, a: &EdgeStruct, b: &EdgeStruct) -> std::cmp::Ordering; } pub struct PathOrdering; impl EdgeOrdering for PathOrdering { - fn compare(&self, a: &EdgeStruct, b: &EdgeStruct) -> std::cmp::Ordering { - a.target.path.cmp(&b.target.path) + fn compare(&self, graph: &NoteGraph, a: &EdgeStruct, b: &EdgeStruct) -> std::cmp::Ordering { + a.target_path_ref(graph).cmp(b.target_path_ref(graph)) } } pub struct BasenameOrdering; impl EdgeOrdering for BasenameOrdering { - fn compare(&self, a: &EdgeStruct, b: &EdgeStruct) -> std::cmp::Ordering { - let a_basename = a.target.path.split('/').last().unwrap(); - let b_basename = b.target.path.split('/').last().unwrap(); + fn compare(&self, graph: &NoteGraph, a: &EdgeStruct, b: &EdgeStruct) -> std::cmp::Ordering { + let a_target = a.target_path_ref(graph); + let b_target = b.target_path_ref(graph); + let a_basename = a_target.split('/').last().unwrap(); + let b_basename = b_target.split('/').last().unwrap(); a_basename.cmp(b_basename) } @@ -146,18 +149,18 @@ impl EdgeOrdering for BasenameOrdering { pub struct EdgeTypeOrdering; impl EdgeOrdering for EdgeTypeOrdering { - fn compare(&self, a: &EdgeStruct, b: &EdgeStruct) -> std::cmp::Ordering { - a.edge.edge_type.cmp(&b.edge.edge_type) + fn compare(&self, _graph: &NoteGraph, a: &EdgeStruct, b: &EdgeStruct) -> std::cmp::Ordering { + a.edge_type.cmp(&b.edge_type) } } pub struct ImpliedOrdering; impl EdgeOrdering for ImpliedOrdering { - fn compare(&self, a: &EdgeStruct, b: &EdgeStruct) -> std::cmp::Ordering { - if a.edge.explicit == b.edge.explicit { - a.target.path.cmp(&b.target.path) - } else if a.edge.explicit { + fn compare(&self, graph: &NoteGraph, a: &EdgeStruct, b: &EdgeStruct) -> std::cmp::Ordering { + if a.explicit(graph) == b.explicit(graph) { + a.target_path_ref(graph).cmp(&b.target_path_ref(graph)) + } else if a.explicit(graph) { std::cmp::Ordering::Less } else { std::cmp::Ordering::Greater @@ -180,7 +183,7 @@ impl<'a> NeighbourOrdering<'a> { } impl EdgeOrdering for NeighbourOrdering<'_> { - fn compare(&self, a: &EdgeStruct, b: &EdgeStruct) -> std::cmp::Ordering { + fn compare(&self, graph: &NoteGraph, a: &EdgeStruct, b: &EdgeStruct) -> std::cmp::Ordering { let neighbour_field = vec![self.neighbour_field.clone()]; let a_neighbour = self @@ -199,7 +202,7 @@ impl EdgeOrdering for NeighbourOrdering<'_> { (Some(a_neighbour), Some(b_neighbour)) => a_neighbour.path.cmp(&b_neighbour.path), (Some(_), None) => std::cmp::Ordering::Less, (None, Some(_)) => std::cmp::Ordering::Greater, - (None, None) => a.target.path.cmp(&b.target.path), + (None, None) => a.target_path_ref(graph).cmp(&b.target_path_ref(graph)), } } } diff --git a/wasm/src/graph.rs b/wasm/src/graph.rs index 73960c8f..9478631a 100644 --- a/wasm/src/graph.rs +++ b/wasm/src/graph.rs @@ -51,14 +51,19 @@ impl NoteGraph { } } + /// Sets the transitive rules for the graph. + /// Does not rebuild the graph. pub fn set_transitive_rules(&mut self, rules: Vec) { self.transitive_rules = rules; } + /// Set the update callback. + /// This will be called after every update to the graph. pub fn set_update_callback(&mut self, callback: js_sys::Function) { self.update_callback = Some(callback); } + /// Call the update callback. pub fn notify_update(&self) { if let Some(callback) = &self.update_callback { match callback.call0(&JsValue::NULL) { @@ -77,6 +82,8 @@ impl NoteGraph { } } + /// Builds the graph from a list of nodes and edges. + /// Will delete all existing data. pub fn build_graph(&mut self, nodes: Vec, edges: Vec) { let mut perf_logger = PerfLogger::new("Building Graph".to_owned()); perf_logger.start_split("Adding initial nodes and edges".to_owned()); @@ -123,11 +130,13 @@ impl NoteGraph { perf_logger.log(); } + /// Applies a batch update to the graph. + /// Throws an error if the update fails. pub fn apply_update(&mut self, update: BatchGraphUpdate) -> Result<()> { let mut perf_logger = PerfLogger::new("Applying Update".to_owned()); perf_logger.start_split("Removing implied edges".to_owned()); - self.int_remove_implied_edges(); + self.int_remove_implied_edges_unchecked(); perf_logger.start_split("Applying updates".to_owned()); @@ -149,6 +158,7 @@ impl NoteGraph { Ok(()) } + /// Iterate all nodes in the graph and call the provided function with the [NodeData] of each node. pub fn iterate_nodes(&self, f: &js_sys::Function) { let this = JsValue::NULL; @@ -161,6 +171,7 @@ impl NoteGraph { }); } + /// Iterate all edges in the graph and call the provided function with the [EdgeData] of each edge. pub fn iterate_edges(&self, f: &js_sys::Function) { let this = JsValue::NULL; @@ -173,18 +184,20 @@ impl NoteGraph { }); } + /// Get all outgoing edges from a node. pub fn get_outgoing_edges(&self, node: String) -> Vec { let node_index = self.int_get_node_index(&node); match node_index { Some(node_index) => self .int_iter_outgoing_edges(node_index) - .filter_map(|edge| self.int_edge_ref_to_struct(edge)) + .map(|edge| self.int_edge_ref_to_struct(edge)) .collect(), None => Vec::new(), } } + /// Get all outgoing edges from a node, filtered and grouped by edge type. pub fn get_filtered_grouped_outgoing_edges( &self, node: String, @@ -195,29 +208,45 @@ impl NoteGraph { GroupedEdgeList::from_vec(match node_index { Some(node_index) => self .int_iter_outgoing_edges(node_index) - .filter_map(|edge| self.int_edge_ref_to_struct(edge)) - .filter(|edge| edge_matches_edge_filter(&edge.edge, edge_types.as_ref())) + .filter(|edge_ref| edge_matches_edge_filter(edge_ref.weight(), edge_types.as_ref())) + .map(|edge| self.int_edge_ref_to_struct(edge)) .collect(), None => Vec::new(), }) } + /// Get all incoming edges to a node. pub fn get_incoming_edges(&self, node: String) -> Vec { let node_index = self.int_get_node_index(&node); match node_index { Some(node_index) => self .int_iter_incoming_edges(node_index) - .filter_map(|edge| self.int_edge_ref_to_struct(edge)) + .map(|edge| self.int_edge_ref_to_struct(edge)) .collect(), None => Vec::new(), } } + /// Checks if a node exists in the graph. pub fn has_node(&self, node: String) -> bool { self.node_hash.contains_key(&node) } + /// Checks if a node is resolved. + /// Returns false if the node is not found. + pub fn is_node_resolved(&self, node: String) -> bool { + self.int_get_node_index(&node) + .and_then(|node_index| self.graph.node_weight(node_index)) + .map(|node| node.resolved) + .unwrap_or(false) + } + + pub fn get_node(&self, node: String) -> Option { + self.int_get_node_index(&node) + .and_then(|node_index| self.graph.node_weight(node_index).cloned()) + } + /// Returns all edge types that are present in the graph. pub fn edge_types(&self) -> Vec { self.edge_types.iter().cloned().collect() @@ -341,7 +370,7 @@ impl NoteGraph { from, to, edge_data, - &mut Some(&mut current_edge_type_tracker), + Some(&mut current_edge_type_tracker), ); } @@ -365,26 +394,23 @@ impl NoteGraph { pub fn int_add_to_edge_type_tracker( &mut self, edge_type: &str, - edge_type_tracker: &mut Option<&mut VecSet<[String; 16]>>, - add_to_global: bool, + edge_type_tracker: Option<&mut VecSet<[String; 16]>>, ) { - if add_to_global { - self.edge_types.insert(edge_type.to_owned()); - } + self.edge_types.insert(edge_type.to_owned()); if let Some(inner) = edge_type_tracker { inner.insert(edge_type.to_owned()); } } - pub fn int_edge_ref_to_struct(&self, edge: NGEdgeRef) -> Option { - EdgeStruct::from_edge_ref(edge, self) + pub fn int_edge_ref_to_struct(&self, edge: NGEdgeRef) -> EdgeStruct { + EdgeStruct::from_edge_ref(edge) } pub fn int_edge_to_struct( &self, edge_index: NGEdgeIndex, - edge_data: EdgeData, + edge_data: &EdgeData, ) -> Option { EdgeStruct::from_edge_data(edge_index, edge_data, self) } @@ -413,18 +439,6 @@ impl NoteGraph { .edges_directed(node, petgraph::Direction::Outgoing) } - pub fn int_remove_outgoing_edges(&mut self, node: NGNodeIndex) { - let mut edges_to_remove: Vec = Vec::new(); - - for edge in self.graph.edges(node) { - edges_to_remove.push(edge.id()); - } - - for edge in edges_to_remove { - self.graph.remove_edge(edge); - } - } - pub fn int_node_count(&self) -> usize { self.graph.node_count() } @@ -450,7 +464,9 @@ impl NoteGraph { edge_matches_edge_filter(edge, edge_types) } - pub fn int_remove_implied_edges(&mut self) { + /// Removes all implied edges from the graph. + /// UNCHECKED: This does not update the edge type tracker. + pub fn int_remove_implied_edges_unchecked(&mut self) { let edge_count = self.graph.edge_count(); self.graph.retain_edges(|frozen_graph, edge| { @@ -486,24 +502,24 @@ impl NoteGraph { from: NGNodeIndex, to: NGNodeIndex, edge_data: EdgeData, - edge_type_tracker: &mut Option<&mut VecSet<[String; 16]>>, + edge_type_tracker: Option<&mut VecSet<[String; 16]>>, ) { if self.int_has_edge(from, to, &edge_data.edge_type) { return; } - self.int_add_to_edge_type_tracker(&edge_data.edge_type, edge_type_tracker, true); + self.int_add_to_edge_type_tracker(&edge_data.edge_type, edge_type_tracker); self.graph.add_edge(from, to, edge_data); } - fn int_remove_node(&mut self, node: &str) -> Result<()> { - let node_index = self - .int_get_node_index(node) - .ok_or(NoteGraphError::new(&format!("Node {:?} not found", node)))?; + fn int_remove_node(&mut self, node_index: NGNodeIndex) -> Result<()> { + let node_path = &self.graph.node_weight(node_index).ok_or(NoteGraphError::new( + "failed to remove node, node not found", + ))?.path; + self.node_hash.remove(node_path); self.graph.remove_node(node_index); - self.node_hash.remove(node); Ok(()) } @@ -567,7 +583,8 @@ impl NoteGraph { // Edge Methods // --------------------- - fn int_remove_edge( + /// UNCHECKED: This does not rebuild the edge type tracker. + fn int_remove_edge_unchecked( &mut self, from: NGNodeIndex, to: NGNodeIndex, @@ -628,6 +645,8 @@ impl NoteGraph { // Safe Methods // ---------------- + /// Adds a node to the graph. + /// Throws an error if the node already exists and is resolved. pub fn int_safe_add_node(&mut self, construction_data: &GCNodeData) -> Result<()> { // we check if the node already exists in the graph // if it does, we assert that it is not resolved @@ -660,6 +679,8 @@ impl NoteGraph { Ok(()) } + /// Safely remove a resolved node from the graph. + /// Will also safely remove all outgoing edges. pub fn int_safe_remove_node(&mut self, node: &str) -> Result<()> { match self.int_get_node_index(node) { Some(index) => { @@ -700,6 +721,8 @@ impl NoteGraph { Ok(()) } + /// Safely deletes an edge by reference. + /// Will remove the target node if it is unresolved and has no other incoming edges. fn int_safe_delete_edge_ref(&mut self, edge: NGEdgeIndex) -> Result<()> { let (_, target) = self .graph @@ -711,7 +734,8 @@ impl NoteGraph { self.graph.remove_edge(edge); if !target_resolved && self.int_iter_incoming_edges(target).count() == 0 { - self.int_remove_node(&target_data.path)?; + // INVARIANT: target node is unresolved and has no incoming edges + self.int_remove_node(target)?; } Ok(()) @@ -722,7 +746,7 @@ impl NoteGraph { (Some(from_index), Some(to_index)) => { match self.int_get_edge(from_index, to_index, edge_type) { Some(edge) => self.int_safe_delete_edge_ref(edge.id()), - None => Err(NoteGraphError::new("Edge not found")), + None => Err(NoteGraphError::new("failed to delete edge, edge not found")), } } _ => Err(NoteGraphError::new( @@ -739,7 +763,7 @@ impl NoteGraph { source, target, construction_data.to_explicit_edge(), - &mut None, + None, ); } diff --git a/wasm/src/graph_data.rs b/wasm/src/graph_data.rs index 4e1a0f30..54977ec3 100644 --- a/wasm/src/graph_data.rs +++ b/wasm/src/graph_data.rs @@ -173,80 +173,76 @@ impl NodeData { #[wasm_bindgen] #[derive(Clone, Debug, PartialEq)] pub struct EdgeStruct { - #[wasm_bindgen(getter_with_clone)] - pub source: NodeData, #[wasm_bindgen(skip)] pub source_index: NGNodeIndex, - #[wasm_bindgen(getter_with_clone)] - pub target: NodeData, #[wasm_bindgen(skip)] pub target_index: NGNodeIndex, - #[wasm_bindgen(getter_with_clone)] - pub edge: EdgeData, #[wasm_bindgen(skip)] pub edge_index: NGEdgeIndex, + #[wasm_bindgen(getter_with_clone)] + pub edge_type: String } #[wasm_bindgen] impl EdgeStruct { - #[wasm_bindgen(getter)] - pub fn source_path(&self) -> String { - self.source.path.clone() + pub fn source_data(&self, graph: &NoteGraph) -> NodeData { + self.source_data_ref(graph).clone() + } + + pub fn target_data(&self, graph: &NoteGraph) -> NodeData { + self.target_data_ref(graph).clone() + } + + pub fn source_path(&self, graph: &NoteGraph) -> String { + self.source_data_ref(graph).path.clone() } - #[wasm_bindgen(getter)] - pub fn target_path(&self) -> String { - self.target.path.clone() + pub fn target_path(&self, graph: &NoteGraph) -> String { + self.target_data_ref(graph).path.clone() } - #[wasm_bindgen(getter)] - pub fn source_resolved(&self) -> bool { - self.source.resolved + pub fn source_resolved(&self, graph: &NoteGraph) -> bool { + self.source_data_ref(graph).resolved.clone() } - #[wasm_bindgen(getter)] - pub fn target_resolved(&self) -> bool { - self.target.resolved + pub fn target_resolved(&self, graph: &NoteGraph) -> bool { + self.target_data_ref(graph).resolved.clone() } - pub fn stringify_target(&self, options: &NodeStringifyOptions) -> String { - options.stringify_node(&self.target) + pub fn stringify_target(&self, graph: &NoteGraph, options: &NodeStringifyOptions) -> String { + options.stringify_node(self.target_data_ref(graph)) } - pub fn stringify_source(&self, options: &NodeStringifyOptions) -> String { - options.stringify_node(&self.source) + pub fn stringify_source(&self, graph: &NoteGraph, options: &NodeStringifyOptions) -> String { + options.stringify_node(self.source_data_ref(graph)) } - #[wasm_bindgen(getter)] - pub fn edge_type(&self) -> String { - self.edge.edge_type.clone() + pub fn edge_data(&self, graph: &NoteGraph) -> EdgeData { + graph.graph.edge_weight(self.edge_index).unwrap().clone() } - #[wasm_bindgen(getter)] - pub fn edge_source(&self) -> String { - self.edge.edge_source.clone() + pub fn edge_source(&self, graph: &NoteGraph) -> String { + self.edge_data_ref(graph).edge_source.clone() } - #[wasm_bindgen(getter)] - pub fn explicit(&self) -> bool { - self.edge.explicit + pub fn explicit(&self, graph: &NoteGraph) -> bool { + self.edge_data_ref(graph).explicit } - #[wasm_bindgen(getter)] - pub fn round(&self) -> u8 { - self.edge.round + pub fn round(&self, graph: &NoteGraph) -> u8 { + self.edge_data_ref(graph).round } - pub fn get_attribute_label(&self, attributes: Vec) -> String { - self.edge.get_attribute_label(&attributes) + pub fn get_attribute_label(&self, graph: &NoteGraph, attributes: Vec) -> String { + self.edge_data_ref(graph).get_attribute_label(&attributes) } - pub fn matches_edge_filter(&self, edge_types: Option>) -> bool { - edge_matches_edge_filter(&self.edge, edge_types.as_ref()) + pub fn matches_edge_filter(&self, graph: &NoteGraph, edge_types: Option>) -> bool { + edge_matches_edge_filter(self.edge_data_ref(graph), edge_types.as_ref()) } pub fn is_self_loop(&self) -> bool { - self.source.path == self.target.path + self.source_index == self.target_index } #[wasm_bindgen(js_name = toString)] @@ -257,60 +253,68 @@ impl EdgeStruct { impl EdgeStruct { pub fn new( - source: NodeData, source_index: NGNodeIndex, - target: NodeData, target_index: NGNodeIndex, - edge: EdgeData, edge_index: NGEdgeIndex, + edge_type: String, ) -> EdgeStruct { EdgeStruct { - source, source_index, - target, target_index, - edge, edge_index, + edge_type, } } pub fn from_edge_ref( - edge_ref: NGEdgeRef, - graph: &crate::graph::NoteGraph, - ) -> Option { + edge_ref: NGEdgeRef + ) -> EdgeStruct { let source_index = edge_ref.source(); let target_index = edge_ref.target(); - let source = graph.graph.node_weight(source_index)?.clone(); - let target = graph.graph.node_weight(target_index)?.clone(); - Some(EdgeStruct::new( - source, + EdgeStruct::new( source_index, - target, target_index, - edge_ref.weight().clone(), edge_ref.id(), - )) + edge_ref.weight().edge_source.clone(), + ) } pub fn from_edge_data( edge_index: NGEdgeIndex, - edge_data: EdgeData, + edge_data: &EdgeData, graph: &crate::graph::NoteGraph, ) -> Option { let (source_index, target_index) = graph.graph.edge_endpoints(edge_index)?; - let source = graph.graph.node_weight(source_index)?.clone(); - let target = graph.graph.node_weight(target_index)?.clone(); Some(EdgeStruct::new( - source, source_index, - target, target_index, - edge_data, edge_index, + edge_data.edge_source.clone(), )) } + + pub fn edge_data_ref<'a>(&self, graph: &'a NoteGraph) -> &'a EdgeData { + graph.graph.edge_weight(self.edge_index).unwrap() + } + + pub fn source_data_ref<'a>(&self, graph: &'a NoteGraph) -> &'a NodeData { + graph.graph.node_weight(self.source_index).unwrap() + } + + pub fn target_data_ref<'a>(&self, graph: &'a NoteGraph) -> &'a NodeData { + graph.graph.node_weight(self.target_index).unwrap() + } + + pub fn target_path_ref<'a>(&self, graph: &'a NoteGraph) -> &'a str { + &self.target_data_ref(graph).path + } + + pub fn source_path_ref<'a>(&self, graph: &'a NoteGraph) -> &'a str { + &self.source_data_ref(graph).path + } + } #[wasm_bindgen] @@ -322,11 +326,6 @@ pub struct EdgeList { #[wasm_bindgen] impl EdgeList { - #[wasm_bindgen(constructor)] - pub fn new() -> EdgeList { - EdgeList { edges: Vec::new() } - } - pub fn get_edges(&self) -> Vec { self.edges.clone() } @@ -355,6 +354,10 @@ impl EdgeList { } impl EdgeList { + pub fn new() -> EdgeList { + EdgeList { edges: Vec::new() } + } + pub fn from_vec(edges: Vec) -> EdgeList { EdgeList { edges } } @@ -368,6 +371,30 @@ pub struct GroupedEdgeList { } #[wasm_bindgen] +impl GroupedEdgeList { + pub fn get_edges(&self, edge_type: &str) -> Option> { + self.edges + .get(edge_type) + .map(|edge_list| edge_list.get_edges()) + } + + pub fn get_sorted_edges( + &self, + edge_type: &str, + graph: &NoteGraph, + sorter: &EdgeSorter, + ) -> Option> { + self.edges + .get(edge_type) + .map(|edge_list| edge_list.get_sorted_edges(graph, sorter)) + } + + #[wasm_bindgen(js_name = toString)] + pub fn to_fancy_string(&self) -> String { + format!("{:#?}", self) + } +} + impl GroupedEdgeList { pub fn new() -> GroupedEdgeList { GroupedEdgeList { @@ -390,32 +417,10 @@ impl GroupedEdgeList { } pub fn add_edge(&mut self, edge_struct: EdgeStruct) { - let edge_type = edge_struct.edge.edge_type.clone(); + let edge_type = edge_struct.edge_type.clone(); let edge_list = self.edges.entry(edge_type).or_default(); edge_list.edges.push(edge_struct); } - - pub fn get_edges(&self, edge_type: &str) -> Option> { - self.edges - .get(edge_type) - .map(|edge_list| edge_list.get_edges()) - } - - pub fn get_sorted_edges( - &self, - edge_type: &str, - graph: &NoteGraph, - sorter: &EdgeSorter, - ) -> Option> { - self.edges - .get(edge_type) - .map(|edge_list| edge_list.get_sorted_edges(graph, sorter)) - } - - #[wasm_bindgen(js_name = toString)] - pub fn to_fancy_string(&self) -> String { - format!("{:#?}", self) - } } #[wasm_bindgen] diff --git a/wasm/src/graph_mermaid.rs b/wasm/src/graph_mermaid.rs index 5cfa1739..cbd8a325 100644 --- a/wasm/src/graph_mermaid.rs +++ b/wasm/src/graph_mermaid.rs @@ -13,10 +13,20 @@ use crate::{ utils::{NoteGraphError, Result}, }; -pub type AccumulatedEdgeHashMap = HashMap< - (NodeIndex, NodeIndex), - (NodeIndex, NodeIndex, Vec, Vec), ->; +pub struct AccumulatedEdgeHashMap<'a> { + map: HashMap< + (NodeIndex, NodeIndex), + (NodeIndex, NodeIndex, Vec<&'a EdgeData>, Vec<&'a EdgeData>), + > +} + +impl Default for AccumulatedEdgeHashMap<'_> { + fn default() -> Self { + AccumulatedEdgeHashMap { + map: HashMap::new(), + } + } +} #[wasm_bindgen] #[derive(Clone, Debug)] @@ -104,7 +114,7 @@ impl NoteGraph { let (nodes, edges) = self.int_traverse_basic(&traversal_options)?; let mut edge_structs = edges .iter() - .filter_map(|edge| EdgeStruct::from_edge_ref(edge.1, self)) + .map(|edge| EdgeStruct::from_edge_ref(edge.1)) .collect::>(); if let Some(edge_sorter) = &diagram_options.edge_sorter { @@ -129,7 +139,7 @@ impl NoteGraph { ); // accumulate edges by direction, so that we can collapse them in the next step - let accumulated_edges = NoteGraph::int_accumulate_edges(edge_structs); + let accumulated_edges = NoteGraph::int_accumulate_edges(self, edge_structs); // utils::log(format!("{:#?}", accumulated_edges)); @@ -160,7 +170,7 @@ impl NoteGraph { } // collapse edge data and add them to the graph - for (from, to, forward, backward) in accumulated_edges.values() { + for (from, to, forward, backward) in accumulated_edges.map.values() { if diagram_options.collapse_opposing_edges || backward.is_empty() { result.push_str(&self.generate_mermaid_edge( from, @@ -226,8 +236,8 @@ impl NoteGraph { &self, source: &NodeIndex, target: &NodeIndex, - forward: Vec, - backward: Vec, + forward: Vec<&EdgeData>, + backward: Vec<&EdgeData>, diagram_options: &MermaidGraphOptions, ) -> String { let mut label = String::new(); @@ -286,34 +296,34 @@ impl NoteGraph { } } - pub fn int_accumulate_edges(edges: Vec) -> AccumulatedEdgeHashMap { - let mut accumulated_edges: AccumulatedEdgeHashMap = HashMap::new(); + pub fn int_accumulate_edges<'a>(graph: &'a NoteGraph, edges: Vec) -> AccumulatedEdgeHashMap<'a> { + let mut accumulated_edges = AccumulatedEdgeHashMap::default(); // sorting the two node indices in the edge tuple could be a speedup, since then only one lookup is needed for edge_struct in edges { let forward_dir = (edge_struct.source_index, edge_struct.target_index); - let entry1 = accumulated_edges.get_mut(&forward_dir); + let entry1 = accumulated_edges.map.get_mut(&forward_dir); match entry1 { Some((_, _, forward, _)) => { - forward.push(edge_struct.edge); + forward.push(edge_struct.edge_data_ref(graph)); } None => { let backward_dir = (edge_struct.target_index, edge_struct.source_index); - let entry2 = accumulated_edges.get_mut(&backward_dir); + let entry2 = accumulated_edges.map.get_mut(&backward_dir); match entry2 { Some((_, _, _, backward)) => { - backward.push(edge_struct.edge); + backward.push(edge_struct.edge_data_ref(graph)); } None => { - accumulated_edges.insert( + accumulated_edges.map.insert( forward_dir, ( edge_struct.source_index, edge_struct.target_index, - vec![edge_struct.edge], + vec![edge_struct.edge_data_ref(graph)], Vec::new(), ), ); diff --git a/wasm/src/graph_traversal.rs b/wasm/src/graph_traversal.rs index c253f00a..0a91aa26 100644 --- a/wasm/src/graph_traversal.rs +++ b/wasm/src/graph_traversal.rs @@ -86,8 +86,8 @@ impl Path { self.edges == other.edges } - pub fn get_first_target(&self) -> Option { - self.edges.first().map(|edge| edge.target.path.clone()) + pub fn get_first_target(&self, graph: &NoteGraph) -> Option { + self.edges.first().map(|edge| edge.target_path(graph)) } #[wasm_bindgen(js_name = toString)] @@ -139,7 +139,7 @@ impl PathList { .unwrap_or(0) } - pub fn process(&self, depth: usize) -> Vec { + pub fn process(&self, graph: &NoteGraph, depth: usize) -> Vec { self.paths .iter() .map(|path| path.truncate(depth)) @@ -149,7 +149,7 @@ impl PathList { a_len .cmp(&b_len) - .then_with(|| a.get_first_target().cmp(&b.get_first_target())) + .then_with(|| a.get_first_target(graph).cmp(&b.get_first_target(graph))) }) .dedup() .collect_vec() @@ -301,14 +301,14 @@ impl RecTraversalResult { } /// Flattens the traversal data by removing the tree structure and deduplicating the edges by their target_path - pub fn flatten(&mut self) { + pub fn flatten(&mut self, graph: &NoteGraph) { let mut data = Vec::new(); for datum in self.data.drain(..) { rec_flatten_traversal_data(datum, &mut data); } - data.dedup_by(|a, b| a.edge.target.path == b.edge.target.path); + data.dedup_by(|a, b| a.edge.target_path(graph) == b.edge.target_path(graph)); self.data = data; } @@ -367,8 +367,8 @@ impl FlatRecTraversalData { #[wasm_bindgen] impl FlatRecTraversalData { - pub fn get_attribute_label(&self, attributes: Vec) -> String { - self.edge.get_attribute_label(attributes) + pub fn get_attribute_label(&self, graph: &NoteGraph, attributes: Vec) -> String { + self.edge.get_attribute_label(graph, attributes) } } @@ -466,8 +466,6 @@ impl NoteGraph { .int_get_node_index(entry_node) .ok_or(NoteGraphError::new("Node not found"))?; - let start_node_weight = self.int_get_node_weight(start_node)?; - for edge in self.graph.edges(start_node) { if !self.int_edge_matches_edge_filter(edge.weight(), Some(&edge_types)) { continue; @@ -476,12 +474,10 @@ impl NoteGraph { let target = edge.target(); let edge_struct = EdgeStruct::new( - start_node_weight.clone(), start_node, - self.int_get_node_weight(target)?.clone(), target, - edge.weight().clone(), edge.id(), + edge.weight().edge_type.clone(), ); traversal_count += 1; @@ -490,7 +486,7 @@ impl NoteGraph { result.push(self.int_rec_traverse( target, edge_struct.clone(), - Some(&vec![edge_struct.edge_type()]), + Some(&vec![edge_struct.edge_type]), 0, options.max_depth, &mut traversal_count, @@ -547,12 +543,10 @@ impl NoteGraph { // assert!(*self.int_get_node_weight(node).unwrap() == edge.target); let edge_struct = EdgeStruct::new( - edge.target.clone(), edge.target_index, - self.int_get_node_weight(target)?.clone(), target, - edge_data.clone(), outgoing_edge.id(), + edge_data.edge_type.clone(), ); *traversal_count += 1; From 0e9265c79c78d11b7afa5fa7254befeea284ed4d Mon Sep 17 00:00:00 2001 From: Moritz Jung Date: Fri, 3 Jan 2025 20:47:03 +0100 Subject: [PATCH 48/65] optimizations, use Rc instead of String --- src/components/page_views/TrailView.svelte | 2 +- src/components/side_views/TreeView.svelte | 2 +- wasm/Cargo.toml | 1 + wasm/src/edge_sorting.rs | 81 +++++++++++++-------- wasm/src/graph.rs | 85 +++++++++++----------- wasm/src/graph_construction.rs | 4 +- wasm/src/graph_data.rs | 79 +++++++++++--------- wasm/src/graph_mermaid.rs | 45 ++++++------ wasm/src/graph_rules.rs | 71 ++++++++++-------- wasm/src/graph_traversal.rs | 38 +++++++--- wasm/src/graph_update.rs | 62 +++++++++------- 11 files changed, 274 insertions(+), 196 deletions(-) diff --git a/src/components/page_views/TrailView.svelte b/src/components/page_views/TrailView.svelte index 5d6d41ff..efaacf04 100644 --- a/src/components/page_views/TrailView.svelte +++ b/src/components/page_views/TrailView.svelte @@ -31,7 +31,7 @@ let traversal_options = new TraversalOptions( [file_path], edge_field_labels, - 100, + 5, !plugin.settings.views.page.trail.merge_fields, ); diff --git a/src/components/side_views/TreeView.svelte b/src/components/side_views/TreeView.svelte index 8a5f2d52..e5fd4e90 100644 --- a/src/components/side_views/TreeView.svelte +++ b/src/components/side_views/TreeView.svelte @@ -40,7 +40,7 @@ new TraversalOptions( [$active_file_store!.path], edge_field_labels, - 20, + 5, !merge_fields, ), ) diff --git a/wasm/Cargo.toml b/wasm/Cargo.toml index 75bcc65f..c20c929c 100644 --- a/wasm/Cargo.toml +++ b/wasm/Cargo.toml @@ -27,6 +27,7 @@ serde = { version = "1.0", features = ["derive"] } serde-wasm-bindgen = "0.6.5" smallvec = "1.13.2" vec-collections = "0.4.3" +enum_dispatch = "0.3.13" [dev-dependencies] wasm-bindgen-test = "0.3.34" diff --git a/wasm/src/edge_sorting.rs b/wasm/src/edge_sorting.rs index e9e039a2..a57d7f9a 100644 --- a/wasm/src/edge_sorting.rs +++ b/wasm/src/edge_sorting.rs @@ -1,5 +1,6 @@ use std::str::FromStr; +use enum_dispatch::enum_dispatch; use petgraph::visit::EdgeRef; use wasm_bindgen::prelude::*; @@ -81,37 +82,37 @@ impl EdgeSorter { } pub fn sort_edges(&self, graph: &NoteGraph, edges: &mut [EdgeStruct]) { - let ordering = self.get_edge_ordering(graph); + let comparer = self.get_edge_comparer(graph); - edges.sort_by(|a, b| self.apply_edge_ordering(graph, ordering.as_ref(), a, b)); + edges.sort_by(|a, b| self.apply_edge_ordering(graph, &comparer, a, b)); } pub fn sort_traversal_data(&self, graph: &NoteGraph, edges: &mut [RecTraversalData]) { - let ordering = self.get_edge_ordering(graph); + let comparer = self.get_edge_comparer(graph); - edges.sort_by(|a, b| self.apply_edge_ordering(graph, ordering.as_ref(), &a.edge, &b.edge)); + edges.sort_by(|a, b| self.apply_edge_ordering(graph, &comparer, &a.edge, &b.edge)); } - fn get_edge_ordering<'a>(&self, graph: &'a NoteGraph) -> Box { + fn get_edge_comparer<'a>(&self, graph: &'a NoteGraph) -> Comparer<'a> { match self.field.clone() { - SortField::Path => Box::from(PathOrdering), - SortField::Basename => Box::from(BasenameOrdering), - SortField::EdgeType => Box::from(EdgeTypeOrdering), - SortField::Implied => Box::from(ImpliedOrdering), + SortField::Path => PathComparer.into(), + SortField::Basename => BasenameComparer.into(), + SortField::EdgeType => EdgeTypeComparer.into(), + SortField::Implied => ImpliedComparer.into(), SortField::Neighbour(neighbour_field) => { - Box::from(NeighbourOrdering::new(neighbour_field, graph)) + NeighbourComparer::new(neighbour_field, graph).into() } } } - fn apply_edge_ordering<'a>( + fn apply_edge_ordering( &self, graph: &NoteGraph, - ordering: &(dyn EdgeOrdering + 'a), + comparer: &impl EdgeComparer, a: &EdgeStruct, b: &EdgeStruct, ) -> std::cmp::Ordering { - let ordering = ordering.compare(graph, a, b); + let ordering = comparer.compare(graph, a, b); if self.reverse { ordering.reverse() @@ -121,21 +122,33 @@ impl EdgeSorter { } } -pub trait EdgeOrdering { +#[enum_dispatch] +pub trait EdgeComparer { fn compare(&self, graph: &NoteGraph, a: &EdgeStruct, b: &EdgeStruct) -> std::cmp::Ordering; } -pub struct PathOrdering; +#[enum_dispatch(EdgeComparer)] +pub enum Comparer<'a> { + PathComparer, + BasenameComparer, + EdgeTypeComparer, + ImpliedComparer, + NeighbourOrdering(NeighbourComparer<'a>), +} + +#[derive(Default)] +pub struct PathComparer; -impl EdgeOrdering for PathOrdering { +impl EdgeComparer for PathComparer { fn compare(&self, graph: &NoteGraph, a: &EdgeStruct, b: &EdgeStruct) -> std::cmp::Ordering { a.target_path_ref(graph).cmp(b.target_path_ref(graph)) } } -pub struct BasenameOrdering; +#[derive(Default)] +pub struct BasenameComparer; -impl EdgeOrdering for BasenameOrdering { +impl EdgeComparer for BasenameComparer { fn compare(&self, graph: &NoteGraph, a: &EdgeStruct, b: &EdgeStruct) -> std::cmp::Ordering { let a_target = a.target_path_ref(graph); let b_target = b.target_path_ref(graph); @@ -146,20 +159,22 @@ impl EdgeOrdering for BasenameOrdering { } } -pub struct EdgeTypeOrdering; +#[derive(Default)] +pub struct EdgeTypeComparer; -impl EdgeOrdering for EdgeTypeOrdering { +impl EdgeComparer for EdgeTypeComparer { fn compare(&self, _graph: &NoteGraph, a: &EdgeStruct, b: &EdgeStruct) -> std::cmp::Ordering { a.edge_type.cmp(&b.edge_type) } } -pub struct ImpliedOrdering; +#[derive(Default)] +pub struct ImpliedComparer; -impl EdgeOrdering for ImpliedOrdering { +impl EdgeComparer for ImpliedComparer { fn compare(&self, graph: &NoteGraph, a: &EdgeStruct, b: &EdgeStruct) -> std::cmp::Ordering { if a.explicit(graph) == b.explicit(graph) { - a.target_path_ref(graph).cmp(&b.target_path_ref(graph)) + a.target_path_ref(graph).cmp(b.target_path_ref(graph)) } else if a.explicit(graph) { std::cmp::Ordering::Less } else { @@ -168,41 +183,47 @@ impl EdgeOrdering for ImpliedOrdering { } } -pub struct NeighbourOrdering<'a> { +pub struct NeighbourComparer<'a> { neighbour_field: String, graph: &'a NoteGraph, } -impl<'a> NeighbourOrdering<'a> { +impl<'a> NeighbourComparer<'a> { pub fn new(neighbour_field: String, graph: &'a NoteGraph) -> Self { - NeighbourOrdering { + NeighbourComparer { neighbour_field, graph, } } } -impl EdgeOrdering for NeighbourOrdering<'_> { +impl EdgeComparer for NeighbourComparer<'_> { fn compare(&self, graph: &NoteGraph, a: &EdgeStruct, b: &EdgeStruct) -> std::cmp::Ordering { let neighbour_field = vec![self.neighbour_field.clone()]; let a_neighbour = self .graph .int_iter_outgoing_edges(a.target_index) - .find(|edge| edge.weight().matches_edge_filter(Some(&neighbour_field))) + .find(|edge| { + edge.weight() + .matches_edge_filter_string(Some(&neighbour_field)) + }) .and_then(|x| self.graph.int_get_node_weight(x.target()).ok()); let b_neighbour = self .graph .int_iter_outgoing_edges(b.target_index) - .find(|edge| edge.weight().matches_edge_filter(Some(&neighbour_field))) + .find(|edge| { + edge.weight() + .matches_edge_filter_string(Some(&neighbour_field)) + }) .and_then(|x| self.graph.int_get_node_weight(x.target()).ok()); match (a_neighbour, b_neighbour) { (Some(a_neighbour), Some(b_neighbour)) => a_neighbour.path.cmp(&b_neighbour.path), (Some(_), None) => std::cmp::Ordering::Less, (None, Some(_)) => std::cmp::Ordering::Greater, - (None, None) => a.target_path_ref(graph).cmp(&b.target_path_ref(graph)), + (None, None) => a.target_path_ref(graph).cmp(b.target_path_ref(graph)), } } } diff --git a/wasm/src/graph.rs b/wasm/src/graph.rs index 9478631a..3a132d6b 100644 --- a/wasm/src/graph.rs +++ b/wasm/src/graph.rs @@ -1,4 +1,4 @@ -use std::collections::HashMap; +use std::{collections::HashMap, rc::Rc}; use petgraph::{ stable_graph::{Edges, StableGraph}, @@ -18,13 +18,21 @@ use crate::{ utils::{NoteGraphError, PerfLogger, Result, LOGGER}, }; -pub fn edge_matches_edge_filter(edge: &EdgeData, edge_types: Option<&Vec>) -> bool { +pub fn edge_matches_edge_filter(edge: &EdgeData, edge_types: Option<&Vec>>) -> bool { match edge_types { Some(types) => types.contains(&edge.edge_type), None => true, } } +pub fn edge_matches_edge_filter_string(edge: &EdgeData, edge_types: Option<&Vec>) -> bool { + match edge_types { + // sadly we can't use contains with Rc and String + Some(types) => types.iter().any(|t| t == edge.edge_type.as_ref()), + None => true, + } +} + #[wasm_bindgen] #[derive(Clone)] pub struct NoteGraph { @@ -33,7 +41,7 @@ pub struct NoteGraph { #[wasm_bindgen(skip)] pub transitive_rules: Vec, #[wasm_bindgen(skip)] - pub edge_types: VecSet<[String; 16]>, + pub edge_types: VecSet<[Rc; 16]>, #[wasm_bindgen(skip)] pub node_hash: HashMap, update_callback: Option, @@ -118,7 +126,7 @@ impl NoteGraph { } for edge in edges { - self.int_safe_add_edge(&edge); + self.int_safe_add_edge(edge); } self.int_build_implied_edges(&mut perf_logger); @@ -208,7 +216,9 @@ impl NoteGraph { GroupedEdgeList::from_vec(match node_index { Some(node_index) => self .int_iter_outgoing_edges(node_index) - .filter(|edge_ref| edge_matches_edge_filter(edge_ref.weight(), edge_types.as_ref())) + .filter(|edge_ref| { + edge_matches_edge_filter_string(edge_ref.weight(), edge_types.as_ref()) + }) .map(|edge| self.int_edge_ref_to_struct(edge)) .collect(), None => Vec::new(), @@ -249,7 +259,7 @@ impl NoteGraph { /// Returns all edge types that are present in the graph. pub fn edge_types(&self) -> Vec { - self.edge_types.iter().cloned().collect() + self.edge_types.iter().map(|x| x.to_string()).collect() } pub fn log(&self) { @@ -274,7 +284,7 @@ impl NoteGraph { let max_rounds = self .transitive_rules .iter() - .map(|rule| rule.rounds) + .map(|rule| rule.rounds()) .max() .unwrap_or(0); // utils::log(format!("Max rounds: {}", max_rounds)); @@ -301,8 +311,7 @@ impl NoteGraph { for rule in self.transitive_rules.iter() { // if there is any edge type that the graph doesn't have, we can skip the rule if rule - .path - .iter() + .iter_path() .any(|edge_type| !self.edge_types.contains(edge_type)) { // utils::log(format!("Skipping rule: {}", rule.edge_type)); @@ -311,8 +320,7 @@ impl NoteGraph { // if all edge types of a rule didn't see any changes in the last round, we can skip the rule if rule - .path - .iter() + .iter_path() .all(|edge_type| !edge_type_tracker.contains(edge_type)) { // utils::log(format!("Skipping rule: {}", rule.edge_type)); @@ -323,12 +331,12 @@ impl NoteGraph { // let path = rule.path.clone(); let mut current_nodes = vec![start_node]; - for edge_type in rule.path.iter() { + for edge_type in rule.iter_path() { let mut next_nodes = Vec::new(); for current_node in current_nodes { for edge in self.graph.edges(current_node) { - if *edge.weight().edge_type == *edge_type { + if edge.weight().edge_type == *edge_type { next_nodes.push(edge.target()); } } @@ -339,14 +347,13 @@ impl NoteGraph { for end_node in current_nodes { // if the rule can't loop and the start and end node are the same, we skip the edge - if !rule.can_loop && start_node == end_node { + if !rule.can_loop() && start_node == end_node { continue; } - let edge_data = - EdgeData::new(rule.edge_type.clone(), rule.get_name(), false, i); + let edge_data = EdgeData::new(rule.edge_type(), rule.name(), false, i); - if rule.close_reversed { + if rule.close_reversed() { edges_to_add.push((end_node, start_node, edge_data)); } else { edges_to_add.push((start_node, end_node, edge_data)); @@ -361,17 +368,12 @@ impl NoteGraph { // utils::log(format!("New edge count: {}", edges_to_add.len())); - let mut current_edge_type_tracker: VecSet<[String; 16]> = VecSet::empty(); + let mut current_edge_type_tracker: VecSet<[Rc; 16]> = VecSet::empty(); round_perf_split.start_split(format!("Adding {} Edges", edges_to_add.len())); for (from, to, edge_data) in edges_to_add { - self.int_add_edge( - from, - to, - edge_data, - Some(&mut current_edge_type_tracker), - ); + self.int_add_edge(from, to, edge_data, Some(&mut current_edge_type_tracker)); } round_perf_split.stop(); @@ -393,16 +395,20 @@ impl NoteGraph { /// Adds an edge type to the global edge type tracker and an optional local edge type tracker. pub fn int_add_to_edge_type_tracker( &mut self, - edge_type: &str, - edge_type_tracker: Option<&mut VecSet<[String; 16]>>, + edge_type: &Rc, + edge_type_tracker: Option<&mut VecSet<[Rc; 16]>>, ) { - self.edge_types.insert(edge_type.to_owned()); + self.edge_types.insert(Rc::clone(edge_type)); if let Some(inner) = edge_type_tracker { - inner.insert(edge_type.to_owned()); + inner.insert(Rc::clone(edge_type)); } } + pub fn int_edge_types(&self) -> Vec> { + self.edge_types.iter().cloned().collect() + } + pub fn int_edge_ref_to_struct(&self, edge: NGEdgeRef) -> EdgeStruct { EdgeStruct::from_edge_ref(edge) } @@ -459,7 +465,7 @@ impl NoteGraph { pub fn int_edge_matches_edge_filter( &self, edge: &EdgeData, - edge_types: Option<&Vec>, + edge_types: Option<&Vec>>, ) -> bool { edge_matches_edge_filter(edge, edge_types) } @@ -502,7 +508,7 @@ impl NoteGraph { from: NGNodeIndex, to: NGNodeIndex, edge_data: EdgeData, - edge_type_tracker: Option<&mut VecSet<[String; 16]>>, + edge_type_tracker: Option<&mut VecSet<[Rc; 16]>>, ) { if self.int_has_edge(from, to, &edge_data.edge_type) { return; @@ -514,9 +520,11 @@ impl NoteGraph { } fn int_remove_node(&mut self, node_index: NGNodeIndex) -> Result<()> { - let node_path = &self.graph.node_weight(node_index).ok_or(NoteGraphError::new( - "failed to remove node, node not found", - ))?.path; + let node_path = &self + .graph + .node_weight(node_index) + .ok_or(NoteGraphError::new("failed to remove node, node not found"))? + .path; self.node_hash.remove(node_path); self.graph.remove_node(node_index); @@ -755,16 +763,11 @@ impl NoteGraph { } } - pub fn int_safe_add_edge(&mut self, construction_data: &GCEdgeData) { + pub fn int_safe_add_edge(&mut self, construction_data: GCEdgeData) { let source = self.int_get_or_create_unresolved_node(&construction_data.source); let target = self.int_get_or_create_unresolved_node(&construction_data.target); - self.int_add_edge( - source, - target, - construction_data.to_explicit_edge(), - None, - ); + self.int_add_edge(source, target, construction_data.to_explicit_edge(), None); } // ---------------- @@ -772,7 +775,7 @@ impl NoteGraph { // ---------------- pub fn assert_correct_trackers(&self) { - let mut edge_types: VecSet<[String; 16]> = VecSet::empty(); + let mut edge_types: VecSet<[Rc; 16]> = VecSet::empty(); for edge in self.graph.edge_references() { edge_types.insert(edge.weight().edge_type.clone()); diff --git a/wasm/src/graph_construction.rs b/wasm/src/graph_construction.rs index 11ac951d..91257f15 100644 --- a/wasm/src/graph_construction.rs +++ b/wasm/src/graph_construction.rs @@ -91,7 +91,7 @@ impl GCEdgeData { } impl GCEdgeData { - pub fn to_explicit_edge(&self) -> EdgeData { - EdgeData::new(self.edge_type.clone(), self.edge_source.clone(), true, 0) + pub fn to_explicit_edge(self) -> EdgeData { + EdgeData::new(self.edge_type.into(), self.edge_source.into(), true, 0) } } diff --git a/wasm/src/graph_data.rs b/wasm/src/graph_data.rs index 54977ec3..a0d66915 100644 --- a/wasm/src/graph_data.rs +++ b/wasm/src/graph_data.rs @@ -1,4 +1,4 @@ -use std::{collections::HashMap, fmt::Debug, path::Path}; +use std::{collections::HashMap, fmt::Debug, path::Path, rc::Rc}; use itertools::Itertools; use petgraph::{ @@ -10,7 +10,7 @@ use wasm_bindgen::prelude::*; use crate::{ edge_sorting::EdgeSorter, - graph::{edge_matches_edge_filter, NoteGraph}, + graph::{edge_matches_edge_filter_string, NoteGraph}, graph_construction::GCNodeData, }; @@ -21,18 +21,34 @@ pub type NGEdgeRef<'a> = EdgeReference<'a, EdgeData, u32>; #[wasm_bindgen] #[derive(Clone, Debug, PartialEq)] pub struct EdgeData { - #[wasm_bindgen(getter_with_clone)] - pub edge_type: String, - #[wasm_bindgen(getter_with_clone)] - pub edge_source: String, + #[wasm_bindgen(skip)] + pub edge_type: Rc, + #[wasm_bindgen(skip)] + pub edge_source: Rc, pub explicit: bool, pub round: u8, } #[wasm_bindgen] impl EdgeData { - #[wasm_bindgen(constructor)] - pub fn new(edge_type: String, edge_source: String, explicit: bool, round: u8) -> EdgeData { + #[wasm_bindgen(js_name = toString)] + pub fn to_fancy_string(&self) -> String { + format!("{:#?}", self) + } + + #[wasm_bindgen(js_name = edge_type, getter)] + pub fn get_edge_type(&self) -> String { + self.edge_type.to_string() + } + + #[wasm_bindgen(js_name = edge_source, getter)] + pub fn get_edge_source(&self) -> String { + self.edge_source.to_string() + } +} + +impl EdgeData { + pub fn new(edge_type: Rc, edge_source: Rc, explicit: bool, round: u8) -> EdgeData { EdgeData { edge_type, edge_source, @@ -41,15 +57,8 @@ impl EdgeData { } } - #[wasm_bindgen(js_name = toString)] - pub fn to_fancy_string(&self) -> String { - format!("{:#?}", self) - } -} - -impl EdgeData { - pub fn matches_edge_filter(&self, edge_types: Option<&Vec>) -> bool { - edge_matches_edge_filter(self, edge_types) + pub fn matches_edge_filter_string(&self, edge_types: Option<&Vec>) -> bool { + edge_matches_edge_filter_string(self, edge_types) } pub fn get_attribute_label(&self, attributes: &Vec) -> String { @@ -61,18 +70,18 @@ impl EdgeData { // TODO(JS): maybe change the attribute options so that the JS side better matches the data for attribute in attributes { let data = match attribute.as_str() { - "field" => Some(("field", self.edge_type.clone())), + "field" => Some(("field", self.edge_type.to_string())), "explicit" => Some(("explicit", self.explicit.to_string())), "source" => { if self.explicit { - Some(("source", self.edge_source.clone())) + Some(("source", self.edge_source.to_string())) } else { None } } "implied_kind" => { if !self.explicit { - Some(("implied_kind", self.edge_source.clone())) + Some(("implied_kind", self.edge_source.to_string())) } else { None } @@ -179,8 +188,8 @@ pub struct EdgeStruct { pub target_index: NGNodeIndex, #[wasm_bindgen(skip)] pub edge_index: NGEdgeIndex, - #[wasm_bindgen(getter_with_clone)] - pub edge_type: String + #[wasm_bindgen(skip)] + pub edge_type: Rc, } #[wasm_bindgen] @@ -202,11 +211,11 @@ impl EdgeStruct { } pub fn source_resolved(&self, graph: &NoteGraph) -> bool { - self.source_data_ref(graph).resolved.clone() + self.source_data_ref(graph).resolved } pub fn target_resolved(&self, graph: &NoteGraph) -> bool { - self.target_data_ref(graph).resolved.clone() + self.target_data_ref(graph).resolved } pub fn stringify_target(&self, graph: &NoteGraph, options: &NodeStringifyOptions) -> String { @@ -221,8 +230,13 @@ impl EdgeStruct { graph.graph.edge_weight(self.edge_index).unwrap().clone() } + #[wasm_bindgen(getter)] + pub fn edge_type(&self) -> String { + self.edge_type.to_string() + } + pub fn edge_source(&self, graph: &NoteGraph) -> String { - self.edge_data_ref(graph).edge_source.clone() + self.edge_data_ref(graph).get_edge_source() } pub fn explicit(&self, graph: &NoteGraph) -> bool { @@ -238,7 +252,7 @@ impl EdgeStruct { } pub fn matches_edge_filter(&self, graph: &NoteGraph, edge_types: Option>) -> bool { - edge_matches_edge_filter(self.edge_data_ref(graph), edge_types.as_ref()) + edge_matches_edge_filter_string(self.edge_data_ref(graph), edge_types.as_ref()) } pub fn is_self_loop(&self) -> bool { @@ -256,7 +270,7 @@ impl EdgeStruct { source_index: NGNodeIndex, target_index: NGNodeIndex, edge_index: NGEdgeIndex, - edge_type: String, + edge_type: Rc, ) -> EdgeStruct { EdgeStruct { source_index, @@ -266,9 +280,7 @@ impl EdgeStruct { } } - pub fn from_edge_ref( - edge_ref: NGEdgeRef - ) -> EdgeStruct { + pub fn from_edge_ref(edge_ref: NGEdgeRef) -> EdgeStruct { let source_index = edge_ref.source(); let target_index = edge_ref.target(); @@ -276,7 +288,7 @@ impl EdgeStruct { source_index, target_index, edge_ref.id(), - edge_ref.weight().edge_source.clone(), + Rc::clone(&edge_ref.weight().edge_type), ) } @@ -291,7 +303,7 @@ impl EdgeStruct { source_index, target_index, edge_index, - edge_data.edge_source.clone(), + Rc::clone(&edge_data.edge_type), )) } @@ -314,7 +326,6 @@ impl EdgeStruct { pub fn source_path_ref<'a>(&self, graph: &'a NoteGraph) -> &'a str { &self.source_data_ref(graph).path } - } #[wasm_bindgen] @@ -367,7 +378,7 @@ impl EdgeList { #[derive(Clone, Debug, PartialEq, Default)] pub struct GroupedEdgeList { #[wasm_bindgen(skip)] - pub edges: HashMap, + pub edges: HashMap, EdgeList>, } #[wasm_bindgen] diff --git a/wasm/src/graph_mermaid.rs b/wasm/src/graph_mermaid.rs index cbd8a325..2b690a64 100644 --- a/wasm/src/graph_mermaid.rs +++ b/wasm/src/graph_mermaid.rs @@ -13,19 +13,19 @@ use crate::{ utils::{NoteGraphError, Result}, }; +type AccumulatedEdgeMap<'a> = HashMap< + (NodeIndex, NodeIndex), + ( + NodeIndex, + NodeIndex, + Vec<&'a EdgeData>, + Vec<&'a EdgeData>, + ), +>; + +#[derive(Default)] pub struct AccumulatedEdgeHashMap<'a> { - map: HashMap< - (NodeIndex, NodeIndex), - (NodeIndex, NodeIndex, Vec<&'a EdgeData>, Vec<&'a EdgeData>), - > -} - -impl Default for AccumulatedEdgeHashMap<'_> { - fn default() -> Self { - AccumulatedEdgeHashMap { - map: HashMap::new(), - } - } + map: AccumulatedEdgeMap<'a>, } #[wasm_bindgen] @@ -175,23 +175,23 @@ impl NoteGraph { result.push_str(&self.generate_mermaid_edge( from, to, - forward.clone(), - backward.clone(), + forward, + backward, &diagram_options, )); } else { result.push_str(&self.generate_mermaid_edge( from, to, - forward.clone(), - Vec::new(), + forward, + &Vec::new(), &diagram_options, )); result.push_str(&self.generate_mermaid_edge( to, from, - backward.clone(), - Vec::new(), + backward, + &Vec::new(), &diagram_options, )); } @@ -236,8 +236,8 @@ impl NoteGraph { &self, source: &NodeIndex, target: &NodeIndex, - forward: Vec<&EdgeData>, - backward: Vec<&EdgeData>, + forward: &[&EdgeData], + backward: &[&EdgeData], diagram_options: &MermaidGraphOptions, ) -> String { let mut label = String::new(); @@ -296,7 +296,10 @@ impl NoteGraph { } } - pub fn int_accumulate_edges<'a>(graph: &'a NoteGraph, edges: Vec) -> AccumulatedEdgeHashMap<'a> { + pub fn int_accumulate_edges( + graph: &NoteGraph, + edges: Vec, + ) -> AccumulatedEdgeHashMap<'_> { let mut accumulated_edges = AccumulatedEdgeHashMap::default(); // sorting the two node indices in the edge tuple could be a speedup, since then only one lookup is needed diff --git a/wasm/src/graph_rules.rs b/wasm/src/graph_rules.rs index 8754dfc8..4a34ae46 100644 --- a/wasm/src/graph_rules.rs +++ b/wasm/src/graph_rules.rs @@ -1,3 +1,5 @@ +use std::rc::Rc; + use wasm_bindgen::prelude::*; use crate::{ @@ -8,21 +10,15 @@ use crate::{ #[wasm_bindgen] #[derive(Clone, Debug)] pub struct TransitiveGraphRule { - #[wasm_bindgen(skip)] - pub name: String, + name: Rc, // the path by edge type - #[wasm_bindgen(skip)] - pub path: Vec, + path: Vec>, // the edge type to add - #[wasm_bindgen(skip)] - pub edge_type: String, + edge_type: Rc, // the edge type to add - #[wasm_bindgen(skip)] - pub rounds: u8, - #[wasm_bindgen(skip)] - pub can_loop: bool, - #[wasm_bindgen(skip)] - pub close_reversed: bool, + rounds: u8, + can_loop: bool, + close_reversed: bool, } #[wasm_bindgen] @@ -36,10 +32,20 @@ impl TransitiveGraphRule { can_loop: bool, close_reversed: bool, ) -> TransitiveGraphRule { + let mut name = name.trim().to_string(); + if name.is_empty() { + name = format!( + "[{}] {} {}", + path.join(", "), + if close_reversed { "<-" } else { "->" }, + edge_type + ); + } + TransitiveGraphRule { - name, - path, - edge_type, + name: Rc::from(name), + path: path.into_iter().map(Rc::from).collect(), + edge_type: Rc::from(edge_type), rounds, can_loop, close_reversed, @@ -53,21 +59,28 @@ impl TransitiveGraphRule { } impl TransitiveGraphRule { - pub fn stringify(&self) -> String { - format!( - "[{}] {} {}", - self.path.join(", "), - if self.close_reversed { "<-" } else { "->" }, - self.edge_type - ) + pub fn name(&self) -> Rc { + Rc::clone(&self.name) } - pub fn get_name(&self) -> String { - if self.name.is_empty() { - self.stringify() - } else { - self.name.clone() - } + pub fn edge_type(&self) -> Rc { + Rc::clone(&self.edge_type) + } + + pub fn iter_path(&self) -> impl Iterator> { + self.path.iter() + } + + pub fn rounds(&self) -> u8 { + self.rounds + } + + pub fn can_loop(&self) -> bool { + self.can_loop + } + + pub fn close_reversed(&self) -> bool { + self.close_reversed } } @@ -92,7 +105,7 @@ pub fn create_graph_from_rule(rule: TransitiveGraphRule) -> NoteGraph { edge_data.push(GCEdgeData::new( counter.to_string(), (counter + 1).to_string(), - element.clone(), + element.to_string(), "explicit".to_string(), )); diff --git a/wasm/src/graph_traversal.rs b/wasm/src/graph_traversal.rs index 0a91aa26..183f9c7b 100644 --- a/wasm/src/graph_traversal.rs +++ b/wasm/src/graph_traversal.rs @@ -1,4 +1,4 @@ -use std::collections::HashSet; +use std::{collections::HashSet, rc::Rc}; use itertools::Itertools; use petgraph::visit::EdgeRef; @@ -429,6 +429,10 @@ impl FlatRecTraversalResult { pub fn is_empty(&self) -> bool { self.data.is_empty() } + + pub fn data_at_index(&self, index: usize) -> Option { + self.data.get(index).cloned() + } } fn rec_flatten_traversal_data_to_flat( @@ -457,7 +461,12 @@ impl NoteGraph { let mut result = Vec::new(); - let edge_types = options.edge_types.unwrap_or(self.edge_types()); + let edge_types = options + .edge_types + .unwrap_or(self.edge_types()) + .into_iter() + .map(Rc::from) + .collect(); let mut traversal_count = 0; @@ -487,7 +496,7 @@ impl NoteGraph { target, edge_struct.clone(), Some(&vec![edge_struct.edge_type]), - 0, + 1, options.max_depth, &mut traversal_count, )?); @@ -496,7 +505,7 @@ impl NoteGraph { target, edge_struct, Some(&edge_types), - 0, + 1, options.max_depth, &mut traversal_count, )?); @@ -526,7 +535,7 @@ impl NoteGraph { &self, node: NGNodeIndex, edge: EdgeStruct, - edge_types: Option<&Vec>, + edge_types: Option<&Vec>>, depth: u32, max_depth: u32, traversal_count: &mut u32, @@ -607,17 +616,22 @@ impl NoteGraph { }) .collect::>>()?; + let opt_edge_types = options + .edge_types + .as_ref() + .map(|v| v.iter().map(|x| Rc::from(x.clone())).collect()); + if options.separate_edges { let mut node_list = Vec::new(); let mut edge_list = Vec::new(); - let all_edge_types = self.edge_types(); - let edge_types: &Vec = options.edge_types.as_ref().unwrap_or(&all_edge_types); + let all_edge_types = self.int_edge_types(); + let edge_types = opt_edge_types.unwrap_or(all_edge_types); for edge_type in edge_types { let (nodes, edges) = self.int_traverse_breadth_first( entry_nodes.clone(), - Some(&vec![edge_type.clone()]), + Some(&vec![edge_type]), options.max_depth, |_, depth| depth, |edge| edge, @@ -631,7 +645,7 @@ impl NoteGraph { } else { Ok(self.int_traverse_breadth_first( entry_nodes, - options.edge_types.as_ref(), + opt_edge_types.as_ref(), options.max_depth, |_, depth| depth, |edge| edge, @@ -648,7 +662,7 @@ impl NoteGraph { pub fn int_traverse_depth_first<'a, N, E>( &'a self, entry_nodes: Vec, - edge_types: Option<&Vec>, + edge_types: Option<&Vec>>, max_depth: u32, node_callback: fn(NGNodeIndex, u32) -> N, edge_callback: fn(NGEdgeRef<'a>) -> E, @@ -674,7 +688,7 @@ impl NoteGraph { pub fn int_traverse_breadth_first<'a, N, E>( &'a self, entry_nodes: Vec, - edge_types: Option<&Vec>, + edge_types: Option<&Vec>>, max_depth: u32, node_callback: fn(NGNodeIndex, u32) -> N, edge_callback: fn(NGEdgeRef<'a>) -> E, @@ -695,7 +709,7 @@ impl NoteGraph { &'a self, traversal_data_structure: &mut impl GraphTraversalDataStructure<(NGNodeIndex, u32)>, entry_nodes: Vec, - edge_types: Option<&Vec>, + edge_types: Option<&Vec>>, max_depth: u32, node_callback: fn(NGNodeIndex, u32) -> N, edge_callback: fn(NGEdgeRef<'a>) -> E, diff --git a/wasm/src/graph_update.rs b/wasm/src/graph_update.rs index bc4575ac..5c928837 100644 --- a/wasm/src/graph_update.rs +++ b/wasm/src/graph_update.rs @@ -4,15 +4,27 @@ use crate::{ graph_rules::TransitiveGraphRule, utils::Result, }; +use enum_dispatch::enum_dispatch; use wasm_bindgen::prelude::*; +#[enum_dispatch] pub trait GraphUpdate { - fn apply(&self, graph: &mut NoteGraph) -> Result<()>; + fn apply(self, graph: &mut NoteGraph) -> Result<()>; +} + +#[enum_dispatch(GraphUpdate)] +pub enum Update { + AddNoteGraphUpdate, + RemoveNoteGraphUpdate, + RenameNoteGraphUpdate, + AddEdgeGraphUpdate, + RemoveEdgeGraphUpdate, + TransitiveRulesGraphUpdate, } #[wasm_bindgen] pub struct BatchGraphUpdate { - updates: Vec>, + updates: Vec, } #[wasm_bindgen] @@ -32,12 +44,12 @@ impl Default for BatchGraphUpdate { } impl BatchGraphUpdate { - pub fn add_update(&mut self, update: Box) { + fn add_update(&mut self, update: Update) { self.updates.push(update); } - pub fn apply(&self, graph: &mut NoteGraph) -> Result<()> { - for update in &self.updates { + pub fn apply(self, graph: &mut NoteGraph) -> Result<()> { + for update in self.updates { update.apply(graph)?; } @@ -58,13 +70,13 @@ impl AddNoteGraphUpdate { Self { data } } - pub fn add_to_batch(&self, batch: &mut BatchGraphUpdate) { - batch.add_update(Box::new(self.clone())); + pub fn add_to_batch(self, batch: &mut BatchGraphUpdate) { + batch.add_update(self.into()); } } impl GraphUpdate for AddNoteGraphUpdate { - fn apply(&self, graph: &mut NoteGraph) -> Result<()> { + fn apply(self, graph: &mut NoteGraph) -> Result<()> { graph.int_safe_add_node(&self.data) } } @@ -82,13 +94,13 @@ impl RemoveNoteGraphUpdate { Self { data } } - pub fn add_to_batch(&self, batch: &mut BatchGraphUpdate) { - batch.add_update(Box::new(self.clone())); + pub fn add_to_batch(self, batch: &mut BatchGraphUpdate) { + batch.add_update(self.into()); } } impl GraphUpdate for RemoveNoteGraphUpdate { - fn apply(&self, graph: &mut NoteGraph) -> Result<()> { + fn apply(self, graph: &mut NoteGraph) -> Result<()> { graph.int_safe_remove_node(&self.data) } } @@ -107,13 +119,13 @@ impl RenameNoteGraphUpdate { Self { old_name, new_name } } - pub fn add_to_batch(&self, batch: &mut BatchGraphUpdate) { - batch.add_update(Box::new(self.clone())); + pub fn add_to_batch(self, batch: &mut BatchGraphUpdate) { + batch.add_update(self.into()); } } impl GraphUpdate for RenameNoteGraphUpdate { - fn apply(&self, graph: &mut NoteGraph) -> Result<()> { + fn apply(self, graph: &mut NoteGraph) -> Result<()> { graph.int_safe_rename_node(&self.old_name, &self.new_name) } } @@ -131,14 +143,14 @@ impl AddEdgeGraphUpdate { Self { data } } - pub fn add_to_batch(&self, batch: &mut BatchGraphUpdate) { - batch.add_update(Box::new(self.clone())); + pub fn add_to_batch(self, batch: &mut BatchGraphUpdate) { + batch.add_update(self.into()); } } impl GraphUpdate for AddEdgeGraphUpdate { - fn apply(&self, graph: &mut NoteGraph) -> Result<()> { - graph.int_safe_add_edge(&self.data); + fn apply(self, graph: &mut NoteGraph) -> Result<()> { + graph.int_safe_add_edge(self.data); Ok(()) } } @@ -162,13 +174,13 @@ impl RemoveEdgeGraphUpdate { } } - pub fn add_to_batch(&self, batch: &mut BatchGraphUpdate) { - batch.add_update(Box::new(self.clone())); + pub fn add_to_batch(self, batch: &mut BatchGraphUpdate) { + batch.add_update(self.into()); } } impl GraphUpdate for RemoveEdgeGraphUpdate { - fn apply(&self, graph: &mut NoteGraph) -> Result<()> { + fn apply(self, graph: &mut NoteGraph) -> Result<()> { graph.int_safe_delete_edge(&self.from, &self.to, &self.edge_type) } } @@ -186,14 +198,14 @@ impl TransitiveRulesGraphUpdate { Self { new_rules } } - pub fn add_to_batch(&self, batch: &mut BatchGraphUpdate) { - batch.add_update(Box::new(self.clone())); + pub fn add_to_batch(self, batch: &mut BatchGraphUpdate) { + batch.add_update(self.into()); } } impl GraphUpdate for TransitiveRulesGraphUpdate { - fn apply(&self, graph: &mut NoteGraph) -> Result<()> { - graph.set_transitive_rules(self.new_rules.clone()); + fn apply(self, graph: &mut NoteGraph) -> Result<()> { + graph.set_transitive_rules(self.new_rules); Ok(()) } } From 9589470d2fc1ec390ad633663740cbac6fa83b51 Mon Sep 17 00:00:00 2001 From: Moritz Jung Date: Sun, 5 Jan 2025 18:40:24 +0100 Subject: [PATCH 49/65] clean up rust code --- package.json | 2 +- src/graph/builders/index.ts | 6 +- wasm/Cargo.toml | 1 + wasm/src/graph.rs | 420 +++++++----------------------------- wasm/src/graph_data.rs | 10 +- wasm/src/graph_rules.rs | 9 +- wasm/src/graph_traversal.rs | 8 +- wasm/src/graph_update.rs | 176 ++++++++++++++- wasm/tests/common/mod.rs | 2 +- wasm/tests/graph.rs | 165 +++++++------- 10 files changed, 357 insertions(+), 442 deletions(-) diff --git a/package.json b/package.json index 9169a076..0c54681f 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,7 @@ "wasm:profile": "cd wasm && wasm-pack build --profiling --target web", "wasm:fmt": "cd wasm && cargo fmt", "wasm:lint": "cd wasm && cargo clippy", - "wasm:test": "cd wasm && wasm-pack test --node --features test" + "wasm:test": "cd wasm && wasm-pack test --node --features test --release" }, "keywords": [], "author": "SkepticMystic", diff --git a/src/graph/builders/index.ts b/src/graph/builders/index.ts index f6ea4c4e..2e1c36b1 100644 --- a/src/graph/builders/index.ts +++ b/src/graph/builders/index.ts @@ -117,11 +117,7 @@ export const rebuild_graph = async (plugin: BreadcrumbsPlugin) => { ) }); - plugin.graph.set_transitive_rules( - transitive_rules - ); - - plugin.graph.build_graph(nodes, edges); + plugin.graph.build_graph(nodes, edges, transitive_rules); log.debug(timer.elapsedMessage("WASM call")); log.debug(timer2.elapsedMessage("Total")); diff --git a/wasm/Cargo.toml b/wasm/Cargo.toml index c20c929c..d78b65ad 100644 --- a/wasm/Cargo.toml +++ b/wasm/Cargo.toml @@ -28,6 +28,7 @@ serde-wasm-bindgen = "0.6.5" smallvec = "1.13.2" vec-collections = "0.4.3" enum_dispatch = "0.3.13" +hashbrown = "0.15.2" [dev-dependencies] wasm-bindgen-test = "0.3.34" diff --git a/wasm/src/graph.rs b/wasm/src/graph.rs index 3a132d6b..26453a7f 100644 --- a/wasm/src/graph.rs +++ b/wasm/src/graph.rs @@ -1,5 +1,6 @@ -use std::{collections::HashMap, rc::Rc}; +use std::rc::Rc; +use hashbrown::HashMap; use petgraph::{ stable_graph::{Edges, StableGraph}, visit::{EdgeRef, IntoEdgeReferences, IntoNodeReferences, NodeRef}, @@ -14,7 +15,7 @@ use crate::{ EdgeData, EdgeStruct, GroupedEdgeList, NGEdgeIndex, NGEdgeRef, NGNodeIndex, NodeData, }, graph_rules::TransitiveGraphRule, - graph_update::BatchGraphUpdate, + graph_update::{AddEdgeGraphUpdate, AddNoteGraphUpdate, BatchGraphUpdate}, utils::{NoteGraphError, PerfLogger, Result, LOGGER}, }; @@ -59,12 +60,6 @@ impl NoteGraph { } } - /// Sets the transitive rules for the graph. - /// Does not rebuild the graph. - pub fn set_transitive_rules(&mut self, rules: Vec) { - self.transitive_rules = rules; - } - /// Set the update callback. /// This will be called after every update to the graph. pub fn set_update_callback(&mut self, callback: js_sys::Function) { @@ -92,50 +87,28 @@ impl NoteGraph { /// Builds the graph from a list of nodes and edges. /// Will delete all existing data. - pub fn build_graph(&mut self, nodes: Vec, edges: Vec) { - let mut perf_logger = PerfLogger::new("Building Graph".to_owned()); - perf_logger.start_split("Adding initial nodes and edges".to_owned()); + pub fn build_graph( + &mut self, + nodes: Vec, + edges: Vec, + transitive_rules: Vec, + ) -> Result<()> { + LOGGER.info("Building Graph"); self.graph = StableGraph::::default(); self.edge_types = VecSet::empty(); - - // self.graph.reserve_exact_nodes(nodes.len()); - self.node_hash = HashMap::new(); + self.transitive_rules = transitive_rules; - for info_node in nodes.as_slice() { - if self.node_hash.contains_key(&info_node.path) { - LOGGER.debug(&format!( - "Duplicate note path in graph construction data: {}", - info_node.path - )); - // LOGGER.with(|l| { - // l.debug(&format!( - // "Duplicate note path in graph construction data: {}", - // info_node.path - // )) - // }); - continue; - } - - self.node_hash.insert( - info_node.path.clone(), - self.graph - .add_node(NodeData::from_construction_data(info_node)), - ); + let mut update = BatchGraphUpdate::new(); + for data in nodes { + AddNoteGraphUpdate::new(data).add_to_batch(&mut update); } - - for edge in edges { - self.int_safe_add_edge(edge); + for data in edges { + AddEdgeGraphUpdate::new(data).add_to_batch(&mut update); } - self.int_build_implied_edges(&mut perf_logger); - - perf_logger.start_split("Update notification callback".to_owned()); - - self.notify_update(); - - perf_logger.log(); + self.apply_update(update) } /// Applies a batch update to the graph. @@ -199,7 +172,7 @@ impl NoteGraph { match node_index { Some(node_index) => self .int_iter_outgoing_edges(node_index) - .map(|edge| self.int_edge_ref_to_struct(edge)) + .map(|edge| EdgeStruct::from_edge_ref(edge)) .collect(), None => Vec::new(), } @@ -219,7 +192,7 @@ impl NoteGraph { .filter(|edge_ref| { edge_matches_edge_filter_string(edge_ref.weight(), edge_types.as_ref()) }) - .map(|edge| self.int_edge_ref_to_struct(edge)) + .map(|edge| EdgeStruct::from_edge_ref(edge)) .collect(), None => Vec::new(), }) @@ -232,7 +205,7 @@ impl NoteGraph { match node_index { Some(node_index) => self .int_iter_incoming_edges(node_index) - .map(|edge| self.int_edge_ref_to_struct(edge)) + .map(|edge| EdgeStruct::from_edge_ref(edge)) .collect(), None => Vec::new(), } @@ -298,6 +271,7 @@ impl NoteGraph { // a rule like [A, B] -> (C with back edge D) would do nothing if applied multiple times, since the edges on the left side were not modified let mut edge_type_tracker = self.edge_types.clone(); + let mut edges_to_add: Vec<(NGNodeIndex, NGNodeIndex, EdgeData)> = Vec::new(); for i in 1..(max_rounds + 1) { let round_perf_split = perf_split.start_split(format!("Round {}", i)); @@ -306,8 +280,6 @@ impl NoteGraph { break; } - let mut edges_to_add: Vec<(NGNodeIndex, NGNodeIndex, EdgeData)> = Vec::new(); - for rule in self.transitive_rules.iter() { // if there is any edge type that the graph doesn't have, we can skip the rule if rule @@ -351,6 +323,10 @@ impl NoteGraph { continue; } + // if self.int_has_edge(start_node, end_node, &rule.edge_type()) { + // continue; + // } + let edge_data = EdgeData::new(rule.edge_type(), rule.name(), false, i); if rule.close_reversed() { @@ -362,23 +338,22 @@ impl NoteGraph { } } - // if edges_to_add.is_empty() { - // break; - // } + edge_type_tracker.retain(|_| false); - // utils::log(format!("New edge count: {}", edges_to_add.len())); + round_perf_split.start_split(format!("Adding {} Edges", edges_to_add.len())); - let mut current_edge_type_tracker: VecSet<[Rc; 16]> = VecSet::empty(); + for (from, to, edge_data) in edges_to_add.drain(..) { + if self.int_has_edge(from, to, &edge_data.edge_type) { + continue; + } - round_perf_split.start_split(format!("Adding {} Edges", edges_to_add.len())); + self.edge_types.insert(Rc::clone(&edge_data.edge_type)); + edge_type_tracker.insert(Rc::clone(&edge_data.edge_type)); - for (from, to, edge_data) in edges_to_add { - self.int_add_edge(from, to, edge_data, Some(&mut current_edge_type_tracker)); + self.graph.add_edge(from, to, edge_data); } round_perf_split.stop(); - - edge_type_tracker = current_edge_type_tracker; } perf_split.stop(); @@ -388,88 +363,10 @@ impl NoteGraph { self.edge_types = VecSet::empty(); for edge in self.graph.edge_references() { - self.edge_types.insert(edge.weight().edge_type.clone()); - } - } - - /// Adds an edge type to the global edge type tracker and an optional local edge type tracker. - pub fn int_add_to_edge_type_tracker( - &mut self, - edge_type: &Rc, - edge_type_tracker: Option<&mut VecSet<[Rc; 16]>>, - ) { - self.edge_types.insert(Rc::clone(edge_type)); - - if let Some(inner) = edge_type_tracker { - inner.insert(Rc::clone(edge_type)); + self.edge_types.insert(Rc::clone(&edge.weight().edge_type)); } } - pub fn int_edge_types(&self) -> Vec> { - self.edge_types.iter().cloned().collect() - } - - pub fn int_edge_ref_to_struct(&self, edge: NGEdgeRef) -> EdgeStruct { - EdgeStruct::from_edge_ref(edge) - } - - pub fn int_edge_to_struct( - &self, - edge_index: NGEdgeIndex, - edge_data: &EdgeData, - ) -> Option { - EdgeStruct::from_edge_data(edge_index, edge_data, self) - } - - pub fn int_has_incoming_edges(&self, node: NGNodeIndex) -> bool { - self.graph - .edges_directed(node, petgraph::Direction::Incoming) - .count() - > 0 - } - - pub fn int_has_outgoing_edges(&self, node: NGNodeIndex) -> bool { - self.graph - .edges_directed(node, petgraph::Direction::Outgoing) - .count() - > 0 - } - - pub fn int_iter_incoming_edges(&self, node: NGNodeIndex) -> Edges<'_, EdgeData, Directed, u32> { - self.graph - .edges_directed(node, petgraph::Direction::Incoming) - } - - pub fn int_iter_outgoing_edges(&self, node: NGNodeIndex) -> Edges<'_, EdgeData, Directed, u32> { - self.graph - .edges_directed(node, petgraph::Direction::Outgoing) - } - - pub fn int_node_count(&self) -> usize { - self.graph.node_count() - } - - pub fn int_edge_count(&self) -> usize { - self.graph.edge_count() - } - - /// Returns the edge weight for a specific edge index. - /// - /// Will return an error if the edge is not found. - pub fn int_get_edge_weight(&self, edge: NGEdgeIndex) -> Result<&EdgeData> { - self.graph - .edge_weight(edge) - .ok_or(NoteGraphError::new("Edge not found")) - } - - pub fn int_edge_matches_edge_filter( - &self, - edge: &EdgeData, - edge_types: Option<&Vec>>, - ) -> bool { - edge_matches_edge_filter(edge, edge_types) - } - /// Removes all implied edges from the graph. /// UNCHECKED: This does not update the edge type tracker. pub fn int_remove_implied_edges_unchecked(&mut self) { @@ -499,37 +396,8 @@ impl NoteGraph { // Node Methods // --------------------- - /// Adds an edge to the graph. will also add the back edge if necessary. - /// This will not add already existing edges. - /// - /// Will add the added edge types to the global edge type tracker and an optional local edge type tracker. - fn int_add_edge( - &mut self, - from: NGNodeIndex, - to: NGNodeIndex, - edge_data: EdgeData, - edge_type_tracker: Option<&mut VecSet<[Rc; 16]>>, - ) { - if self.int_has_edge(from, to, &edge_data.edge_type) { - return; - } - - self.int_add_to_edge_type_tracker(&edge_data.edge_type, edge_type_tracker); - - self.graph.add_edge(from, to, edge_data); - } - - fn int_remove_node(&mut self, node_index: NGNodeIndex) -> Result<()> { - let node_path = &self - .graph - .node_weight(node_index) - .ok_or(NoteGraphError::new("failed to remove node, node not found"))? - .path; - - self.node_hash.remove(node_path); - self.graph.remove_node(node_index); - - Ok(()) + pub fn int_node_count(&self) -> usize { + self.graph.node_count() } /// Returns the node index for a specific node weight. @@ -537,21 +405,6 @@ impl NoteGraph { self.node_hash.get(node).copied() } - pub fn int_get_or_create_unresolved_node(&mut self, node: &str) -> NGNodeIndex { - match self.int_get_node_index(node) { - Some(node_index) => node_index, - None => { - let node_index = self - .graph - .add_node(NodeData::new_unresolved(node.to_owned())); - - self.node_hash.insert(node.to_owned(), node_index); - - node_index - } - } - } - /// Returns the node weight for a specific node index. /// /// Will return an error if the node is not found. @@ -561,57 +414,46 @@ impl NoteGraph { )) } - pub fn int_set_node_resolved(&mut self, node: NGNodeIndex, resolved: bool) -> Result<()> { - let node = self.graph.node_weight_mut(node); - match node { - Some(node) => { - node.resolved = resolved; - - Ok(()) - } - None => Err(NoteGraphError::new( - "Failed to set node resolved, node not found", - )), - } + pub fn int_get_node_weight_mut(&mut self, node: NGNodeIndex) -> Result<&mut NodeData> { + self.graph.node_weight_mut(node).ok_or(NoteGraphError::new( + "failed to get node weight, node not found", + )) } - fn int_add_node(&mut self, node: &GCNodeData) -> Result<()> { - if self.node_hash.contains_key(&node.path) { - return Err(NoteGraphError::new("Node already exists")); - } + pub fn int_has_incoming_edges(&self, node: NGNodeIndex) -> bool { + self.graph + .edges_directed(node, petgraph::Direction::Incoming) + .count() + > 0 + } - let node_index = self.graph.add_node(NodeData::from_construction_data(node)); + pub fn int_has_outgoing_edges(&self, node: NGNodeIndex) -> bool { + self.graph + .edges_directed(node, petgraph::Direction::Outgoing) + .count() + > 0 + } - self.node_hash.insert(node.path.clone(), node_index); + pub fn int_iter_incoming_edges(&self, node: NGNodeIndex) -> Edges<'_, EdgeData, Directed, u32> { + self.graph + .edges_directed(node, petgraph::Direction::Incoming) + } - Ok(()) + pub fn int_iter_outgoing_edges(&self, node: NGNodeIndex) -> Edges<'_, EdgeData, Directed, u32> { + self.graph + .edges_directed(node, petgraph::Direction::Outgoing) } // --------------------- // Edge Methods // --------------------- - /// UNCHECKED: This does not rebuild the edge type tracker. - fn int_remove_edge_unchecked( - &mut self, - from: NGNodeIndex, - to: NGNodeIndex, - edge_type: &str, - ) -> Result<()> { - let edge = self - .graph - .edges(from) - .find(|e| e.target() == to && *e.weight().edge_type == *edge_type); - - match edge { - Some(edge) => { - self.graph.remove_edge(edge.id()); - Ok(()) - } - None => Err(NoteGraphError::new("Edge not found")), - } + pub fn int_edge_count(&self) -> usize { + self.graph.edge_count() } + /// Gets an edge between two nodes based on a specific edge type. + /// Returns None if the edge does not exist. pub fn int_get_edge( &self, from: NGNodeIndex, @@ -623,6 +465,8 @@ impl NoteGraph { .find(|e| e.target() == to && *e.weight().edge_type == *edge_type) } + /// Gets an edge between two nodes based on a specific edge type. + /// Returns None if the edge does not exist. pub fn int_get_edge_by_name(&self, from: &str, to: &str, edge_type: &str) -> Option { let from_index = self.int_get_node_index(from)?; let to_index = self.int_get_node_index(to)?; @@ -637,6 +481,7 @@ impl NoteGraph { .any(|e| e.target() == to && *e.weight().edge_type == *edge_type) } + /// Checks if an edge exists between two nodes with a specific edge type. pub fn int_has_edge_by_name(&self, from: &str, to: &str, edge_type: &str) -> bool { let from_index = self.int_get_node_index(from); let to_index = self.int_get_node_index(to); @@ -649,125 +494,26 @@ impl NoteGraph { } } - // ---------------- - // Safe Methods - // ---------------- - - /// Adds a node to the graph. - /// Throws an error if the node already exists and is resolved. - pub fn int_safe_add_node(&mut self, construction_data: &GCNodeData) -> Result<()> { - // we check if the node already exists in the graph - // if it does, we assert that it is not resolved - match self.int_get_node_index(&construction_data.path) { - Some(node_index) => { - if self.int_get_node_weight(node_index)?.resolved { - return Err(NoteGraphError::new( - "There already exists a resolved node with the same name.", - )); - } - - let node = self.graph.node_weight_mut(node_index); - - match node { - Some(node) => { - node.override_with_construction_data(construction_data); - } - None => { - return Err(NoteGraphError::new( - "failed to override node, node data not found", - )) - } - } - } - None => { - self.int_add_node(construction_data)?; - } - } - - Ok(()) - } - - /// Safely remove a resolved node from the graph. - /// Will also safely remove all outgoing edges. - pub fn int_safe_remove_node(&mut self, node: &str) -> Result<()> { - match self.int_get_node_index(node) { - Some(index) => { - if !self.int_get_node_weight(index)?.resolved { - return Err(NoteGraphError::new("Cannot remove an unresolved node")); - } - - self.int_set_node_resolved(index, false)?; - - let edges_to_remove: Vec = self - .int_iter_outgoing_edges(index) - .map(|edge| edge.id()) - .collect(); - - for edge in edges_to_remove { - self.int_safe_delete_edge_ref(edge)?; - } - } - None => { - return Err(NoteGraphError::new("failed to remove node, node not found")); - } - } - - Ok(()) - } - - pub fn int_safe_rename_node(&mut self, old_name: &str, new_name: &str) -> Result<()> { - let node_index = self - .int_get_node_index(old_name) - .ok_or(NoteGraphError::new( - "failed to rename node, old node not found", - ))?; - - self.graph.node_weight_mut(node_index).unwrap().path = new_name.to_owned(); - self.node_hash.remove(old_name); - self.node_hash.insert(new_name.to_owned(), node_index); - - Ok(()) - } - - /// Safely deletes an edge by reference. - /// Will remove the target node if it is unresolved and has no other incoming edges. - fn int_safe_delete_edge_ref(&mut self, edge: NGEdgeIndex) -> Result<()> { - let (_, target) = self - .graph - .edge_endpoints(edge) - .ok_or(NoteGraphError::new("Edge not found"))?; - let target_data = self.int_get_node_weight(target)?.clone(); - let target_resolved = target_data.resolved; - - self.graph.remove_edge(edge); - - if !target_resolved && self.int_iter_incoming_edges(target).count() == 0 { - // INVARIANT: target node is unresolved and has no incoming edges - self.int_remove_node(target)?; - } - - Ok(()) + /// Get a reference to the edge weight for a specific edge index. + /// + /// Will return an error if the edge is not found. + pub fn int_get_edge_weight(&self, edge: NGEdgeIndex) -> Result<&EdgeData> { + self.graph.edge_weight(edge).ok_or(NoteGraphError::new( + "failed to get edge weight, edge not found", + )) } - pub fn int_safe_delete_edge(&mut self, from: &str, to: &str, edge_type: &str) -> Result<()> { - match (self.int_get_node_index(from), self.int_get_node_index(to)) { - (Some(from_index), Some(to_index)) => { - match self.int_get_edge(from_index, to_index, edge_type) { - Some(edge) => self.int_safe_delete_edge_ref(edge.id()), - None => Err(NoteGraphError::new("failed to delete edge, edge not found")), - } - } - _ => Err(NoteGraphError::new( - "failed to delete edge, connecting node not found", - )), - } + /// Get a mutable reference to the edge weight for a specific edge index. + /// + /// Will return an error if the edge is not found. + pub fn int_get_edge_weight_mut(&mut self, edge: NGEdgeIndex) -> Result<&mut EdgeData> { + self.graph.edge_weight_mut(edge).ok_or(NoteGraphError::new( + "failed to get edge weight, edge not found", + )) } - pub fn int_safe_add_edge(&mut self, construction_data: GCEdgeData) { - let source = self.int_get_or_create_unresolved_node(&construction_data.source); - let target = self.int_get_or_create_unresolved_node(&construction_data.target); - - self.int_add_edge(source, target, construction_data.to_explicit_edge(), None); + pub fn int_edge_types(&self) -> Vec> { + self.edge_types.iter().cloned().collect() } // ---------------- diff --git a/wasm/src/graph_data.rs b/wasm/src/graph_data.rs index a0d66915..f092255c 100644 --- a/wasm/src/graph_data.rs +++ b/wasm/src/graph_data.rs @@ -147,10 +147,10 @@ impl NodeData { } impl NodeData { - pub fn from_construction_data(data: &GCNodeData) -> NodeData { + pub fn from_construction_data(data: GCNodeData) -> NodeData { NodeData { - path: data.path.clone(), - aliases: data.aliases.clone(), + path: data.path, + aliases: data.aliases, resolved: data.resolved, ignore_in_edges: data.ignore_in_edges, ignore_out_edges: data.ignore_out_edges, @@ -167,12 +167,12 @@ impl NodeData { } } - pub fn override_with_construction_data(&mut self, data: &GCNodeData) { + pub fn override_with_construction_data(&mut self, data: GCNodeData) { assert_eq!( self.path, data.path, "Can not override with data for another node." ); - self.aliases = data.aliases.clone(); + self.aliases = data.aliases; self.resolved = data.resolved; self.ignore_in_edges = data.ignore_in_edges; self.ignore_out_edges = data.ignore_out_edges; diff --git a/wasm/src/graph_rules.rs b/wasm/src/graph_rules.rs index 4a34ae46..534a0e71 100644 --- a/wasm/src/graph_rules.rs +++ b/wasm/src/graph_rules.rs @@ -5,6 +5,7 @@ use wasm_bindgen::prelude::*; use crate::{ graph::NoteGraph, graph_construction::{GCEdgeData, GCNodeData}, + utils::Result, }; #[wasm_bindgen] @@ -85,11 +86,9 @@ impl TransitiveGraphRule { } #[wasm_bindgen] -pub fn create_graph_from_rule(rule: TransitiveGraphRule) -> NoteGraph { +pub fn create_graph_from_rule(rule: TransitiveGraphRule) -> Result { let mut graph = NoteGraph::new(); - graph.set_transitive_rules(vec![rule.clone()]); - let mut node_data = vec![]; let mut edge_data = vec![]; @@ -120,7 +119,7 @@ pub fn create_graph_from_rule(rule: TransitiveGraphRule) -> NoteGraph { false, )); - graph.build_graph(node_data, edge_data); + graph.build_graph(node_data, edge_data, vec![rule])?; - graph + Ok(graph) } diff --git a/wasm/src/graph_traversal.rs b/wasm/src/graph_traversal.rs index 183f9c7b..d87b546c 100644 --- a/wasm/src/graph_traversal.rs +++ b/wasm/src/graph_traversal.rs @@ -7,7 +7,7 @@ use web_time::Instant; use crate::{ edge_sorting::EdgeSorter, - graph::NoteGraph, + graph::{edge_matches_edge_filter, NoteGraph}, graph_data::{EdgeStruct, NGEdgeIndex, NGEdgeRef, NGNodeIndex}, utils::{ BreadthFirstTraversalDataStructure, DepthFirstTraversalDataStructure, @@ -476,7 +476,7 @@ impl NoteGraph { .ok_or(NoteGraphError::new("Node not found"))?; for edge in self.graph.edges(start_node) { - if !self.int_edge_matches_edge_filter(edge.weight(), Some(&edge_types)) { + if !edge_matches_edge_filter(edge.weight(), Some(&edge_types)) { continue; } @@ -546,7 +546,7 @@ impl NoteGraph { for outgoing_edge in self.graph.edges(node) { let edge_data = outgoing_edge.weight(); - if self.int_edge_matches_edge_filter(edge_data, edge_types) { + if edge_matches_edge_filter(edge_data, edge_types) { let target = outgoing_edge.target(); // assert!(*self.int_get_node_weight(node).unwrap() == edge.target); @@ -732,7 +732,7 @@ impl NoteGraph { let target = edge.target(); let edge_data = edge.weight(); - if self.int_edge_matches_edge_filter(edge_data, edge_types) { + if edge_matches_edge_filter(edge_data, edge_types) { let already_visited = visited_nodes.contains(&target); // we only add the edge if we are not at the depth limit or if we are at the depth limit and the target node is already in the depth map diff --git a/wasm/src/graph_update.rs b/wasm/src/graph_update.rs index 5c928837..cf7c237b 100644 --- a/wasm/src/graph_update.rs +++ b/wasm/src/graph_update.rs @@ -1,10 +1,12 @@ use crate::{ graph::NoteGraph, graph_construction::{GCEdgeData, GCNodeData}, + graph_data::{NGEdgeIndex, NGNodeIndex, NodeData}, graph_rules::TransitiveGraphRule, - utils::Result, + utils::{NoteGraphError, Result, LOGGER}, }; use enum_dispatch::enum_dispatch; +use petgraph::visit::EdgeRef; use wasm_bindgen::prelude::*; #[enum_dispatch] @@ -77,7 +79,7 @@ impl AddNoteGraphUpdate { impl GraphUpdate for AddNoteGraphUpdate { fn apply(self, graph: &mut NoteGraph) -> Result<()> { - graph.int_safe_add_node(&self.data) + graph.upd_add_node(self.data) } } @@ -101,7 +103,7 @@ impl RemoveNoteGraphUpdate { impl GraphUpdate for RemoveNoteGraphUpdate { fn apply(self, graph: &mut NoteGraph) -> Result<()> { - graph.int_safe_remove_node(&self.data) + graph.upd_remove_node(&self.data) } } @@ -126,7 +128,7 @@ impl RenameNoteGraphUpdate { impl GraphUpdate for RenameNoteGraphUpdate { fn apply(self, graph: &mut NoteGraph) -> Result<()> { - graph.int_safe_rename_node(&self.old_name, &self.new_name) + graph.upd_rename_node(&self.old_name, &self.new_name) } } @@ -150,8 +152,7 @@ impl AddEdgeGraphUpdate { impl GraphUpdate for AddEdgeGraphUpdate { fn apply(self, graph: &mut NoteGraph) -> Result<()> { - graph.int_safe_add_edge(self.data); - Ok(()) + graph.upd_add_edge(self.data) } } @@ -181,7 +182,7 @@ impl RemoveEdgeGraphUpdate { impl GraphUpdate for RemoveEdgeGraphUpdate { fn apply(self, graph: &mut NoteGraph) -> Result<()> { - graph.int_safe_delete_edge(&self.from, &self.to, &self.edge_type) + graph.upd_remove_edge(&self.from, &self.to, &self.edge_type) } } @@ -205,7 +206,166 @@ impl TransitiveRulesGraphUpdate { impl GraphUpdate for TransitiveRulesGraphUpdate { fn apply(self, graph: &mut NoteGraph) -> Result<()> { - graph.set_transitive_rules(self.new_rules); + graph.transitive_rules = self.new_rules; + Ok(()) + } +} + +trait UpdateableGraph { + /// Adds a node to the graph. + /// Throws an error if the node already exists and is resolved. + fn upd_add_node(&mut self, data: GCNodeData) -> Result<()>; + fn upd_remove_node(&mut self, name: &str) -> Result<()>; + fn upd_rename_node(&mut self, old_name: &str, new_name: &str) -> Result<()>; + fn upd_add_edge(&mut self, data: GCEdgeData) -> Result<()>; + fn upd_remove_edge(&mut self, from: &str, to: &str, edge_type: &str) -> Result<()>; +} + +/// INVARIANT: These update methods should keep the node_hash in tact, but the edge type tracker can be made inconsistent. +impl UpdateableGraph for NoteGraph { + fn upd_add_node(&mut self, data: GCNodeData) -> Result<()> { + // we check if the node already exists in the graph + // if it does, we assert that it is not resolved + match self.int_get_node_index(&data.path) { + Some(node_index) => { + if self.int_get_node_weight(node_index)?.resolved { + return Err(NoteGraphError::new( + "There already exists a resolved node with the same name.", + )); + } + + let node = self.int_get_node_weight_mut(node_index)?; + node.override_with_construction_data(data); + } + None => { + let node_path = data.path.clone(); + let node_index = self.graph.add_node(NodeData::from_construction_data(data)); + self.node_hash.insert(node_path, node_index); + } + } + + Ok(()) + } + + fn upd_remove_node(&mut self, name: &str) -> Result<()> { + match self.int_get_node_index(name) { + Some(index) => { + let node_weight = self.int_get_node_weight_mut(index)?; + + if !node_weight.resolved { + LOGGER.warn(&format!( + "Attempted to remove unresolved node {} from the graph", + node_weight.path + )); + } + + node_weight.resolved = false; + + let edges_to_remove: Vec = self + .int_iter_outgoing_edges(index) + .map(|edge| edge.id()) + .collect(); + + for edge in edges_to_remove { + self.remove_edge_by_index(edge)?; + } + } + None => { + return Err(NoteGraphError::new("failed to remove node, node not found")); + } + } + + Ok(()) + } + + fn upd_rename_node(&mut self, old_name: &str, new_name: &str) -> Result<()> { + let node_index = self + .int_get_node_index(old_name) + .ok_or(NoteGraphError::new( + "failed to rename node, old node not found", + ))?; + + self.graph.node_weight_mut(node_index).unwrap().path = new_name.to_owned(); + self.node_hash.remove(old_name); + self.node_hash.insert(new_name.to_owned(), node_index); + + Ok(()) + } + + fn upd_add_edge(&mut self, data: GCEdgeData) -> Result<()> { + let source = self + .int_get_node_index(&data.source) + .ok_or(NoteGraphError::new( + "failed to add edge, source node not found", + ))?; + let target = self.get_node_index_or_create_unresolved(&data.target); + + if self.int_has_edge(source, target, &data.edge_type) { + return Ok(()); + } + + self.graph.add_edge(source, target, data.to_explicit_edge()); + + Ok(()) + } + + fn upd_remove_edge(&mut self, from: &str, to: &str, edge_type: &str) -> Result<()> { + let from = self.int_get_node_index(from).ok_or(NoteGraphError::new( + "failed to delete edge, source node not found", + ))?; + let to = self.int_get_node_index(to).ok_or(NoteGraphError::new( + "failed to delete edge, target node not found", + ))?; + + match self.int_get_edge(from, to, edge_type) { + Some(edge) => self.remove_edge_by_index(edge.id()), + None => Err(NoteGraphError::new("failed to delete edge, edge not found")), + } + } +} + +impl NoteGraph { + /// INVARIANT: This function does not update the edge type tracker. + fn remove_edge_by_index(&mut self, edge_index: NGEdgeIndex) -> Result<()> { + let (_, target) = self + .graph + .edge_endpoints(edge_index) + .ok_or(NoteGraphError::new("Edge not found"))?; + let target_data = self.int_get_node_weight(target)?; + let target_name = target_data.path.clone(); + let target_unresolved = !target_data.resolved; + + self.graph.remove_edge(edge_index); + + if target_unresolved && !self.int_has_incoming_edges(target) { + // INVARIANT: target node is unresolved and has no incoming edges + self.remove_node_by_index_and_name(target, &target_name); + } + Ok(()) } + + /// INVARIANT: This function does not update the edge type tracker. + fn remove_node_by_index_and_name(&mut self, node_index: NGNodeIndex, name: &str) { + self.node_hash.remove(name); + self.graph.remove_node(node_index); + } + + /// Gets the node index for a specific node. + /// If the node does not exist, a new unresolved node will be created and + /// the index of the new node returned. + pub fn get_node_index_or_create_unresolved(&mut self, node: &str) -> NGNodeIndex { + match self.int_get_node_index(node) { + Some(node_index) => node_index, + None => { + let node_index = self + .graph + .add_node(NodeData::new_unresolved(node.to_owned())); + + self.node_hash.insert(node.to_owned(), node_index); + + node_index + } + } + } } diff --git a/wasm/tests/common/mod.rs b/wasm/tests/common/mod.rs index 78b11760..0a1c179c 100644 --- a/wasm/tests/common/mod.rs +++ b/wasm/tests/common/mod.rs @@ -47,7 +47,7 @@ pub fn tdata_generate_tree(depth: u32, branches: u32) -> (Vec, Vec, Vec)) -> NoteGraph { let mut graph = NoteGraph::new(); - graph.build_graph(data.0, data.1); + graph.build_graph(data.0, data.1, vec![]).unwrap(); graph } diff --git a/wasm/tests/graph.rs b/wasm/tests/graph.rs index 2fd95e87..c17d78c5 100644 --- a/wasm/tests/graph.rs +++ b/wasm/tests/graph.rs @@ -30,26 +30,30 @@ fn test_implied_edge_rules_reverse_direction() { let data = common::tdata_generate_tree(3, 2); let mut graph = NoteGraph::new(); - graph.set_transitive_rules(vec![ - TransitiveGraphRule::new( - "".to_string(), - vec!["down".to_string()], - "up".to_string(), - 5, - false, - true, - ), - TransitiveGraphRule::new( - "".to_string(), - vec!["up".to_string()], - "down".to_string(), - 5, - false, - true, - ), - ]); - - graph.build_graph(data.0, data.1); + graph + .build_graph( + data.0, + data.1, + vec![ + TransitiveGraphRule::new( + "".to_string(), + vec!["down".to_string()], + "up".to_string(), + 5, + false, + true, + ), + TransitiveGraphRule::new( + "".to_string(), + vec!["up".to_string()], + "down".to_string(), + 5, + false, + true, + ), + ], + ) + .unwrap(); assert_eq!(graph.int_edge_count(), 28); @@ -70,34 +74,39 @@ fn test_implied_edge_rules_sibling() { let data = common::tdata_generate_tree(3, 2); let mut graph = NoteGraph::new(); - graph.set_transitive_rules(vec![ - TransitiveGraphRule::new( - "".to_string(), - vec!["down".to_string()], - "up".to_string(), - 5, - false, - true, - ), - TransitiveGraphRule::new( - "".to_string(), - vec!["up".to_string()], - "down".to_string(), - 5, - false, - true, - ), - TransitiveGraphRule::new( - "".to_string(), - vec!["up".to_string(), "down".to_string()], - "same".to_string(), - 5, - false, - false, - ), - ]); - - graph.build_graph(data.0, data.1); + graph + .build_graph( + data.0, + data.1, + vec![ + TransitiveGraphRule::new( + "".to_string(), + vec!["down".to_string()], + "up".to_string(), + 5, + false, + true, + ), + TransitiveGraphRule::new( + "".to_string(), + vec!["up".to_string()], + "down".to_string(), + 5, + false, + true, + ), + TransitiveGraphRule::new( + "".to_string(), + vec!["up".to_string(), "down".to_string()], + "same".to_string(), + 5, + false, + false, + ), + ], + ) + .unwrap(); + // same edges between siblings exist let same_edge_1 = graph .int_get_edge_by_name(&"0".to_string(), &"1".to_string(), &"same".to_string()) @@ -123,34 +132,38 @@ fn test_implied_edge_rules_sibling_can_loop() { let data = common::tdata_generate_tree(3, 2); let mut graph = NoteGraph::new(); - graph.set_transitive_rules(vec![ - TransitiveGraphRule::new( - "".to_string(), - vec!["down".to_string()], - "up".to_string(), - 5, - false, - true, - ), - TransitiveGraphRule::new( - "".to_string(), - vec!["up".to_string()], - "down".to_string(), - 5, - false, - true, - ), - TransitiveGraphRule::new( - "".to_string(), - vec!["up".to_string(), "down".to_string()], - "same".to_string(), - 5, - true, - false, - ), - ]); - - graph.build_graph(data.0, data.1); + graph + .build_graph( + data.0, + data.1, + vec![ + TransitiveGraphRule::new( + "".to_string(), + vec!["down".to_string()], + "up".to_string(), + 5, + false, + true, + ), + TransitiveGraphRule::new( + "".to_string(), + vec!["up".to_string()], + "down".to_string(), + 5, + false, + true, + ), + TransitiveGraphRule::new( + "".to_string(), + vec!["up".to_string(), "down".to_string()], + "same".to_string(), + 5, + true, + false, + ), + ], + ) + .unwrap(); // same edges between siblings exist let same_edge_1 = graph .int_get_edge_by_name(&"0".to_string(), &"1".to_string(), &"same".to_string()) From cb39be0ed4076fcd4ff9054fab14ac52cbece5a5 Mon Sep 17 00:00:00 2001 From: Moritz Jung Date: Mon, 6 Jan 2025 23:11:20 +0100 Subject: [PATCH 50/65] checkpoint before running svelte 5 migration --- esbuild.config.mjs | 4 +- package-lock.json | 9589 +++++++++-------- package.json | 18 +- src/commands/list_index/index.ts | 43 +- src/components/NestedEdgeList.svelte | 4 +- .../codeblocks/CodeblockTree.svelte | 25 +- src/components/page_views/TrailView.svelte | 41 - src/components/side_views/TreeView.svelte | 63 +- wasm/src/edge_sorting.rs | 76 +- wasm/src/graph.rs | 15 +- wasm/src/graph_data.rs | 150 +- wasm/src/graph_mermaid.rs | 18 +- wasm/src/graph_traversal.rs | 200 +- 13 files changed, 5263 insertions(+), 4983 deletions(-) diff --git a/esbuild.config.mjs b/esbuild.config.mjs index 6de62f80..7bfe2c19 100644 --- a/esbuild.config.mjs +++ b/esbuild.config.mjs @@ -2,7 +2,7 @@ import builtins from "builtin-modules"; import esbuild from "esbuild"; import esbuildSvelte from "esbuild-svelte"; import process from "process"; -import sveltePreprocess from "svelte-preprocess"; +import { sveltePreprocess } from "svelte-preprocess"; import path from 'node:path'; import fs from 'node:fs'; @@ -70,7 +70,7 @@ const context = await esbuild.context({ outfile: "main.js", plugins: [ esbuildSvelte({ - compilerOptions: { css: true, dev: !prod }, + compilerOptions: { css: 'injected', dev: !prod }, preprocess: sveltePreprocess(), }), wasmPlugin, diff --git a/package-lock.json b/package-lock.json index de946918..854b2b20 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,22 +1,22 @@ { "name": "breadcrumbs", - "version": "4.2.11-beta", + "version": "4.2.35-beta", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "breadcrumbs", - "version": "4.2.11-beta", + "version": "4.2.35-beta", "license": "MIT", "dependencies": { "graphology": "^0.25.4", "lucide-svelte": "^0.360.0", "luxon": "^3.4.4", - "zod": "^3.23.3" + "zod": "^3.24.1" }, "devDependencies": { "@aidenlx/folder-note-core": "^1.3.6", - "@tsconfig/svelte": "^5.0.2", + "@tsconfig/svelte": "^5.0.4", "@types/luxon": "^3.4.2", "@types/node": "^20.11.0", "@types/obsidian-typings": "npm:obsidian-typings@^1.0.6", @@ -25,32 +25,22 @@ "@vitest/coverage-v8": "^1.3.1", "@vitest/ui": "^1.3.1", "builtin-modules": "3.3.0", - "esbuild": "0.19.11", - "esbuild-svelte": "^0.8.0", + "esbuild": "0.24.2", + "esbuild-svelte": "^0.9.0", "npm-run-all": "^4.1.5", - "obsidian": "^1.5.7-1", + "obsidian": "^1.7.2", "obsidian-dataview": "^0.5.64", - "prettier": "^3.2.2", - "prettier-plugin-svelte": "^3.1.2", + "prettier": "^3.4.2", + "prettier-plugin-svelte": "^3.3.2", "prettier-plugin-tailwindcss": "^0.5.11", - "svelte": "^4.2.8", - "svelte-preprocess": "^5.1.3", + "svelte": "^5.16.2", + "svelte-preprocess": "^6.0.3", "tailwindcss": "^3.4.1", "tslib": "2.6.2", "typescript": "^5.3.3", "vitest": "^1.3.1" } }, - "node_modules/@aashutoshrathi/word-wrap": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", - "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==", - "dev": true, - "peer": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/@aidenlx/folder-note-core": { "version": "1.3.6", "resolved": "https://registry.npmjs.org/@aidenlx/folder-note-core/-/folder-note-core-1.3.6.tgz", @@ -74,692 +64,869 @@ } }, "node_modules/@ampproject/remapping": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz", - "integrity": "sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", + "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", "dependencies": { - "@jridgewell/gen-mapping": "^0.3.0", - "@jridgewell/trace-mapping": "^0.3.9" + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" }, "engines": { "node": ">=6.0.0" } }, - "node_modules/@babel/code-frame": { - "version": "7.23.5", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.23.5.tgz", - "integrity": "sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA==", + "node_modules/@babel/helper-string-parser": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz", + "integrity": "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==", "dev": true, - "optional": true, - "peer": true, - "dependencies": { - "@babel/highlight": "^7.23.4", - "chalk": "^2.4.2" - }, "engines": { "node": ">=6.9.0" } }, - "node_modules/@babel/code-frame/node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "node_modules/@babel/helper-validator-identifier": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz", + "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.26.3", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.3.tgz", + "integrity": "sha512-WJ/CvmY8Mea8iDXo6a7RK2wbmJITT5fN3BEkRuFlxVyNx8jOKIIhmC4fSkTcPcf8JyavbBwIe6OpiCOBXt/IcA==", "dev": true, - "optional": true, - "peer": true, "dependencies": { - "color-convert": "^1.9.0" + "@babel/types": "^7.26.3" + }, + "bin": { + "parser": "bin/babel-parser.js" }, "engines": { - "node": ">=4" + "node": ">=6.0.0" } }, - "node_modules/@babel/code-frame/node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "node_modules/@babel/types": { + "version": "7.26.3", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.3.tgz", + "integrity": "sha512-vN5p+1kl59GVKMvTHt55NzzmYVxprfJD+ql7U9NFIfKCBkYE55LYtS+WtPlaYOyzydrKI8Nezd+aZextrd+FMA==", "dev": true, - "optional": true, - "peer": true, "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" + "@babel/helper-string-parser": "^7.25.9", + "@babel/helper-validator-identifier": "^7.25.9" }, "engines": { - "node": ">=4" + "node": ">=6.9.0" } }, - "node_modules/@babel/code-frame/node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "node_modules/@bcoe/v8-coverage": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", + "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", + "dev": true + }, + "node_modules/@codemirror/language": { + "version": "6.10.3", + "resolved": "git+ssh://git@github.com/lishid/cm-language.git#be84c19d93fa87ac4e43c1a5874b1d228b5b2a89", "dev": true, - "optional": true, - "peer": true, + "license": "MIT", "dependencies": { - "color-name": "1.1.3" + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.23.0", + "@lezer/common": "^1.1.0", + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.0.0", + "style-mod": "^4.0.0" } }, - "node_modules/@babel/code-frame/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "node_modules/@codemirror/state": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/@codemirror/state/-/state-6.5.0.tgz", + "integrity": "sha512-MwBHVK60IiIHDcoMet78lxt6iw5gJOGSbNbOIVBHWVXIH4/Nq1+GQgLLGgI1KlnN86WDXsPudVaqYHKBIx7Eyw==", "dev": true, - "optional": true, - "peer": true + "dependencies": { + "@marijn/find-cluster-break": "^1.0.0" + } }, - "node_modules/@babel/code-frame/node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "node_modules/@codemirror/view": { + "version": "6.36.1", + "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.36.1.tgz", + "integrity": "sha512-miD1nyT4m4uopZaDdO2uXU/LLHliKNYL9kB1C1wJHrunHLm/rpkb5QVSokqgw9hFqEZakrdlb/VGWX8aYZTslQ==", "dev": true, - "optional": true, - "peer": true, - "engines": { - "node": ">=0.8.0" + "dependencies": { + "@codemirror/state": "^6.5.0", + "style-mod": "^4.1.0", + "w3c-keyname": "^2.2.4" } }, - "node_modules/@babel/code-frame/node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "node_modules/@esbuild/aix-ppc64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.24.2.tgz", + "integrity": "sha512-thpVCb/rhxE/BnMLQ7GReQLLN8q9qbHmI55F4489/ByVg2aQaQ6kbcLb6FHkocZzQhxc4gx0sCk0tJkKBFzDhA==", + "cpu": [ + "ppc64" + ], "dev": true, "optional": true, - "peer": true, + "os": [ + "aix" + ], "engines": { - "node": ">=4" + "node": ">=18" } }, - "node_modules/@babel/code-frame/node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "node_modules/@esbuild/android-arm": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.24.2.tgz", + "integrity": "sha512-tmwl4hJkCfNHwFB3nBa8z1Uy3ypZpxqxfTQOcHX+xRByyYgunVbZ9MzUUfb0RxaHIMnbHagwAxuTL+tnNM+1/Q==", + "cpu": [ + "arm" + ], "dev": true, "optional": true, - "peer": true, - "dependencies": { - "has-flag": "^3.0.0" - }, + "os": [ + "android" + ], "engines": { - "node": ">=4" + "node": ">=18" } }, - "node_modules/@babel/compat-data": { - "version": "7.23.5", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.23.5.tgz", - "integrity": "sha512-uU27kfDRlhfKl+w1U6vp16IuvSLtjAxdArVXPa9BvLkrr7CYIsxH5adpHObeAGY/41+syctUWOZ140a2Rvkgjw==", + "node_modules/@esbuild/android-arm64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.24.2.tgz", + "integrity": "sha512-cNLgeqCqV8WxfcTIOeL4OAtSmL8JjcN6m09XIgro1Wi7cF4t/THaWEa7eL5CMoMBdjoHOTh/vwTO/o2TRXIyzg==", + "cpu": [ + "arm64" + ], "dev": true, "optional": true, - "peer": true, + "os": [ + "android" + ], "engines": { - "node": ">=6.9.0" + "node": ">=18" } }, - "node_modules/@babel/core": { - "version": "7.24.0", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.24.0.tgz", - "integrity": "sha512-fQfkg0Gjkza3nf0c7/w6Xf34BW4YvzNfACRLmmb7XRLa6XHdR+K9AlJlxneFfWYf6uhOzuzZVTjF/8KfndZANw==", + "node_modules/@esbuild/android-x64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.24.2.tgz", + "integrity": "sha512-B6Q0YQDqMx9D7rvIcsXfmJfvUYLoP722bgfBlO5cGvNVb5V/+Y7nhBE3mHV9OpxBf4eAS2S68KZztiPaWq4XYw==", + "cpu": [ + "x64" + ], "dev": true, "optional": true, - "peer": true, - "dependencies": { - "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.23.5", - "@babel/generator": "^7.23.6", - "@babel/helper-compilation-targets": "^7.23.6", - "@babel/helper-module-transforms": "^7.23.3", - "@babel/helpers": "^7.24.0", - "@babel/parser": "^7.24.0", - "@babel/template": "^7.24.0", - "@babel/traverse": "^7.24.0", - "@babel/types": "^7.24.0", - "convert-source-map": "^2.0.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.2.3", - "semver": "^6.3.1" - }, + "os": [ + "android" + ], "engines": { - "node": ">=6.9.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/babel" + "node": ">=18" } }, - "node_modules/@babel/core/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "node_modules/@esbuild/darwin-arm64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.24.2.tgz", + "integrity": "sha512-kj3AnYWc+CekmZnS5IPu9D+HWtUI49hbnyqk0FLEJDbzCIQt7hg7ucF1SQAilhtYpIujfaHr6O0UHlzzSPdOeA==", + "cpu": [ + "arm64" + ], "dev": true, "optional": true, - "peer": true, - "bin": { - "semver": "bin/semver.js" + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" } }, - "node_modules/@babel/generator": { - "version": "7.23.6", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.6.tgz", - "integrity": "sha512-qrSfCYxYQB5owCmGLbl8XRpX1ytXlpueOb0N0UmQwA073KZxejgQTzAmJezxvpwQD9uGtK2shHdi55QT+MbjIw==", + "node_modules/@esbuild/darwin-x64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.24.2.tgz", + "integrity": "sha512-WeSrmwwHaPkNR5H3yYfowhZcbriGqooyu3zI/3GGpF8AyUdsrrP0X6KumITGA9WOyiJavnGZUwPGvxvwfWPHIA==", + "cpu": [ + "x64" + ], "dev": true, "optional": true, - "peer": true, - "dependencies": { - "@babel/types": "^7.23.6", - "@jridgewell/gen-mapping": "^0.3.2", - "@jridgewell/trace-mapping": "^0.3.17", - "jsesc": "^2.5.1" - }, + "os": [ + "darwin" + ], "engines": { - "node": ">=6.9.0" + "node": ">=18" } }, - "node_modules/@babel/helper-compilation-targets": { - "version": "7.23.6", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.23.6.tgz", - "integrity": "sha512-9JB548GZoQVmzrFgp8o7KxdgkTGm6xs9DW0o/Pim72UDjzr5ObUQ6ZzYPqA+g9OTS2bBQoctLJrky0RDCAWRgQ==", + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.24.2.tgz", + "integrity": "sha512-UN8HXjtJ0k/Mj6a9+5u6+2eZ2ERD7Edt1Q9IZiB5UZAIdPnVKDoG7mdTVGhHJIeEml60JteamR3qhsr1r8gXvg==", + "cpu": [ + "arm64" + ], "dev": true, "optional": true, - "peer": true, - "dependencies": { - "@babel/compat-data": "^7.23.5", - "@babel/helper-validator-option": "^7.23.5", - "browserslist": "^4.22.2", - "lru-cache": "^5.1.1", - "semver": "^6.3.1" - }, + "os": [ + "freebsd" + ], "engines": { - "node": ">=6.9.0" + "node": ">=18" } }, - "node_modules/@babel/helper-compilation-targets/node_modules/lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "node_modules/@esbuild/freebsd-x64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.24.2.tgz", + "integrity": "sha512-TvW7wE/89PYW+IevEJXZ5sF6gJRDY/14hyIGFXdIucxCsbRmLUcjseQu1SyTko+2idmCw94TgyaEZi9HUSOe3Q==", + "cpu": [ + "x64" + ], "dev": true, "optional": true, - "peer": true, - "dependencies": { - "yallist": "^3.0.2" + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" } }, - "node_modules/@babel/helper-compilation-targets/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "node_modules/@esbuild/linux-arm": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.24.2.tgz", + "integrity": "sha512-n0WRM/gWIdU29J57hJyUdIsk0WarGd6To0s+Y+LwvlC55wt+GT/OgkwoXCXvIue1i1sSNWblHEig00GBWiJgfA==", + "cpu": [ + "arm" + ], "dev": true, "optional": true, - "peer": true, - "bin": { - "semver": "bin/semver.js" + "os": [ + "linux" + ], + "engines": { + "node": ">=18" } }, - "node_modules/@babel/helper-compilation-targets/node_modules/yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "node_modules/@esbuild/linux-arm64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.24.2.tgz", + "integrity": "sha512-7HnAD6074BW43YvvUmE/35Id9/NB7BeX5EoNkK9obndmZBUk8xmJJeU7DwmUeN7tkysslb2eSl6CTrYz6oEMQg==", + "cpu": [ + "arm64" + ], "dev": true, "optional": true, - "peer": true + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } }, - "node_modules/@babel/helper-environment-visitor": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", - "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==", + "node_modules/@esbuild/linux-ia32": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.24.2.tgz", + "integrity": "sha512-sfv0tGPQhcZOgTKO3oBE9xpHuUqguHvSo4jl+wjnKwFpapx+vUDcawbwPNuBIAYdRAvIDBfZVvXprIj3HA+Ugw==", + "cpu": [ + "ia32" + ], "dev": true, "optional": true, - "peer": true, + "os": [ + "linux" + ], "engines": { - "node": ">=6.9.0" + "node": ">=18" } }, - "node_modules/@babel/helper-function-name": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", - "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", + "node_modules/@esbuild/linux-loong64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.24.2.tgz", + "integrity": "sha512-CN9AZr8kEndGooS35ntToZLTQLHEjtVB5n7dl8ZcTZMonJ7CCfStrYhrzF97eAecqVbVJ7APOEe18RPI4KLhwQ==", + "cpu": [ + "loong64" + ], "dev": true, "optional": true, - "peer": true, - "dependencies": { - "@babel/template": "^7.22.15", - "@babel/types": "^7.23.0" - }, + "os": [ + "linux" + ], "engines": { - "node": ">=6.9.0" + "node": ">=18" } }, - "node_modules/@babel/helper-hoist-variables": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", - "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", + "node_modules/@esbuild/linux-mips64el": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.24.2.tgz", + "integrity": "sha512-iMkk7qr/wl3exJATwkISxI7kTcmHKE+BlymIAbHO8xanq/TjHaaVThFF6ipWzPHryoFsesNQJPE/3wFJw4+huw==", + "cpu": [ + "mips64el" + ], "dev": true, "optional": true, - "peer": true, - "dependencies": { - "@babel/types": "^7.22.5" - }, + "os": [ + "linux" + ], "engines": { - "node": ">=6.9.0" + "node": ">=18" } }, - "node_modules/@babel/helper-module-imports": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.22.15.tgz", - "integrity": "sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==", + "node_modules/@esbuild/linux-ppc64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.24.2.tgz", + "integrity": "sha512-shsVrgCZ57Vr2L8mm39kO5PPIb+843FStGt7sGGoqiiWYconSxwTiuswC1VJZLCjNiMLAMh34jg4VSEQb+iEbw==", + "cpu": [ + "ppc64" + ], "dev": true, "optional": true, - "peer": true, - "dependencies": { - "@babel/types": "^7.22.15" - }, + "os": [ + "linux" + ], "engines": { - "node": ">=6.9.0" + "node": ">=18" } }, - "node_modules/@babel/helper-module-transforms": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.23.3.tgz", - "integrity": "sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ==", + "node_modules/@esbuild/linux-riscv64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.24.2.tgz", + "integrity": "sha512-4eSFWnU9Hhd68fW16GD0TINewo1L6dRrB+oLNNbYyMUAeOD2yCK5KXGK1GH4qD/kT+bTEXjsyTCiJGHPZ3eM9Q==", + "cpu": [ + "riscv64" + ], "dev": true, "optional": true, - "peer": true, - "dependencies": { - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-module-imports": "^7.22.15", - "@babel/helper-simple-access": "^7.22.5", - "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/helper-validator-identifier": "^7.22.20" - }, + "os": [ + "linux" + ], "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" + "node": ">=18" } }, - "node_modules/@babel/helper-simple-access": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz", - "integrity": "sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==", + "node_modules/@esbuild/linux-s390x": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.24.2.tgz", + "integrity": "sha512-S0Bh0A53b0YHL2XEXC20bHLuGMOhFDO6GN4b3YjRLK//Ep3ql3erpNcPlEFed93hsQAjAQDNsvcK+hV90FubSw==", + "cpu": [ + "s390x" + ], "dev": true, "optional": true, - "peer": true, - "dependencies": { - "@babel/types": "^7.22.5" - }, + "os": [ + "linux" + ], "engines": { - "node": ">=6.9.0" + "node": ">=18" } }, - "node_modules/@babel/helper-split-export-declaration": { - "version": "7.22.6", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", - "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", + "node_modules/@esbuild/linux-x64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.24.2.tgz", + "integrity": "sha512-8Qi4nQcCTbLnK9WoMjdC9NiTG6/E38RNICU6sUNqK0QFxCYgoARqVqxdFmWkdonVsvGqWhmm7MO0jyTqLqwj0Q==", + "cpu": [ + "x64" + ], "dev": true, "optional": true, - "peer": true, - "dependencies": { - "@babel/types": "^7.22.5" - }, + "os": [ + "linux" + ], "engines": { - "node": ">=6.9.0" + "node": ">=18" } }, - "node_modules/@babel/helper-string-parser": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.23.4.tgz", - "integrity": "sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ==", + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.24.2.tgz", + "integrity": "sha512-wuLK/VztRRpMt9zyHSazyCVdCXlpHkKm34WUyinD2lzK07FAHTq0KQvZZlXikNWkDGoT6x3TD51jKQ7gMVpopw==", + "cpu": [ + "arm64" + ], "dev": true, + "optional": true, + "os": [ + "netbsd" + ], "engines": { - "node": ">=6.9.0" + "node": ">=18" } }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", - "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", + "node_modules/@esbuild/netbsd-x64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.24.2.tgz", + "integrity": "sha512-VefFaQUc4FMmJuAxmIHgUmfNiLXY438XrL4GDNV1Y1H/RW3qow68xTwjZKfj/+Plp9NANmzbH5R40Meudu8mmw==", + "cpu": [ + "x64" + ], "dev": true, + "optional": true, + "os": [ + "netbsd" + ], "engines": { - "node": ">=6.9.0" + "node": ">=18" } }, - "node_modules/@babel/helper-validator-option": { - "version": "7.23.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.23.5.tgz", - "integrity": "sha512-85ttAOMLsr53VgXkTbkx8oA6YTfT4q7/HzXSLEYmjcSTJPMPQtvq1BD79Byep5xMUYbGRzEpDsjUf3dyp54IKw==", + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.24.2.tgz", + "integrity": "sha512-YQbi46SBct6iKnszhSvdluqDmxCJA+Pu280Av9WICNwQmMxV7nLRHZfjQzwbPs3jeWnuAhE9Jy0NrnJ12Oz+0A==", + "cpu": [ + "arm64" + ], "dev": true, "optional": true, - "peer": true, + "os": [ + "openbsd" + ], "engines": { - "node": ">=6.9.0" + "node": ">=18" } }, - "node_modules/@babel/helpers": { - "version": "7.24.0", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.24.0.tgz", - "integrity": "sha512-ulDZdc0Aj5uLc5nETsa7EPx2L7rM0YJM8r7ck7U73AXi7qOV44IHHRAYZHY6iU1rr3C5N4NtTmMRUJP6kwCWeA==", + "node_modules/@esbuild/openbsd-x64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.24.2.tgz", + "integrity": "sha512-+iDS6zpNM6EnJyWv0bMGLWSWeXGN/HTaF/LXHXHwejGsVi+ooqDfMCCTerNFxEkM3wYVcExkeGXNqshc9iMaOA==", + "cpu": [ + "x64" + ], "dev": true, "optional": true, - "peer": true, - "dependencies": { - "@babel/template": "^7.24.0", - "@babel/traverse": "^7.24.0", - "@babel/types": "^7.24.0" - }, + "os": [ + "openbsd" + ], "engines": { - "node": ">=6.9.0" + "node": ">=18" } }, - "node_modules/@babel/highlight": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.23.4.tgz", - "integrity": "sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A==", + "node_modules/@esbuild/sunos-x64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.24.2.tgz", + "integrity": "sha512-hTdsW27jcktEvpwNHJU4ZwWFGkz2zRJUz8pvddmXPtXDzVKTTINmlmga3ZzwcuMpUvLw7JkLy9QLKyGpD2Yxig==", + "cpu": [ + "x64" + ], "dev": true, "optional": true, - "peer": true, - "dependencies": { - "@babel/helper-validator-identifier": "^7.22.20", - "chalk": "^2.4.2", - "js-tokens": "^4.0.0" - }, + "os": [ + "sunos" + ], "engines": { - "node": ">=6.9.0" + "node": ">=18" } }, - "node_modules/@babel/highlight/node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "node_modules/@esbuild/win32-arm64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.24.2.tgz", + "integrity": "sha512-LihEQ2BBKVFLOC9ZItT9iFprsE9tqjDjnbulhHoFxYQtQfai7qfluVODIYxt1PgdoyQkz23+01rzwNwYfutxUQ==", + "cpu": [ + "arm64" + ], "dev": true, "optional": true, - "peer": true, - "dependencies": { - "color-convert": "^1.9.0" - }, + "os": [ + "win32" + ], "engines": { - "node": ">=4" + "node": ">=18" } }, - "node_modules/@babel/highlight/node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "node_modules/@esbuild/win32-ia32": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.24.2.tgz", + "integrity": "sha512-q+iGUwfs8tncmFC9pcnD5IvRHAzmbwQ3GPS5/ceCyHdjXubwQWI12MKWSNSMYLJMq23/IUCvJMS76PDqXe1fxA==", + "cpu": [ + "ia32" + ], "dev": true, "optional": true, - "peer": true, - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, + "os": [ + "win32" + ], "engines": { - "node": ">=4" + "node": ">=18" } }, - "node_modules/@babel/highlight/node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "node_modules/@esbuild/win32-x64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.24.2.tgz", + "integrity": "sha512-7VTgWzgMGvup6aSqDPLiW5zHaxYJGTO4OokMjIlrCtf+VpEL+cXKtCvg723iguPYI5oaUNdS+/V7OU2gvXVWEg==", + "cpu": [ + "x64" + ], "dev": true, "optional": true, - "peer": true, - "dependencies": { - "color-name": "1.1.3" + "os": [ + "win32" + ], + "engines": { + "node": ">=18" } }, - "node_modules/@babel/highlight/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "node_modules/@eslint-community/eslint-utils": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.1.tgz", + "integrity": "sha512-s3O3waFUrMV8P/XaF/+ZTp1X9XBZW1a4B97ZnjQF2KYWaFD2A8KyFBsrsfSjEmjn3RGWAIuvlneuZm3CUK3jbA==", "dev": true, - "optional": true, - "peer": true + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } }, - "node_modules/@babel/highlight/node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "node_modules/@eslint-community/regexpp": { + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", + "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", "dev": true, - "optional": true, - "peer": true, "engines": { - "node": ">=0.8.0" + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" } }, - "node_modules/@babel/highlight/node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "node_modules/@eslint/eslintrc": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", + "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", "dev": true, - "optional": true, "peer": true, + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.6.0", + "globals": "^13.19.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, "engines": { - "node": ">=4" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, - "node_modules/@babel/highlight/node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "node_modules/@eslint/eslintrc/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, - "optional": true, "peer": true, "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" } }, - "node_modules/@babel/parser": { - "version": "7.24.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.0.tgz", - "integrity": "sha512-QuP/FxEAzMSjXygs8v4N9dvdXzEHN4W1oF3PxuWAtPo08UdM17u89RDMgjLn/mlc56iM0HlLmVkO/wgR+rDgHg==", + "node_modules/@eslint/eslintrc/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, - "bin": { - "parser": "bin/babel-parser.js" + "peer": true, + "dependencies": { + "brace-expansion": "^1.1.7" }, "engines": { - "node": ">=6.0.0" + "node": "*" } }, - "node_modules/@babel/template": { - "version": "7.24.0", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.24.0.tgz", - "integrity": "sha512-Bkf2q8lMB0AFpX0NFEqSbx1OkTHf0f+0j82mkw+ZpzBnkk7e9Ql0891vlfgi+kHwOk8tQjiQHpqh4LaSa0fKEA==", + "node_modules/@eslint/js": { + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.1.tgz", + "integrity": "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==", "dev": true, - "optional": true, "peer": true, - "dependencies": { - "@babel/code-frame": "^7.23.5", - "@babel/parser": "^7.24.0", - "@babel/types": "^7.24.0" - }, "engines": { - "node": ">=6.9.0" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, - "node_modules/@babel/traverse": { - "version": "7.24.0", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.24.0.tgz", - "integrity": "sha512-HfuJlI8qq3dEDmNU5ChzzpZRWq+oxCZQyMzIMEqLho+AQnhMnKQUzH6ydo3RBl/YjPCuk68Y6s0Gx0AeyULiWw==", + "node_modules/@humanwhocodes/config-array": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz", + "integrity": "sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==", + "deprecated": "Use @eslint/config-array instead", "dev": true, - "optional": true, "peer": true, "dependencies": { - "@babel/code-frame": "^7.23.5", - "@babel/generator": "^7.23.6", - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-function-name": "^7.23.0", - "@babel/helper-hoist-variables": "^7.22.5", - "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/parser": "^7.24.0", - "@babel/types": "^7.24.0", + "@humanwhocodes/object-schema": "^2.0.3", "debug": "^4.3.1", - "globals": "^11.1.0" + "minimatch": "^3.0.5" }, "engines": { - "node": ">=6.9.0" + "node": ">=10.10.0" } }, - "node_modules/@babel/traverse/node_modules/globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "node_modules/@humanwhocodes/config-array/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, - "optional": true, "peer": true, - "engines": { - "node": ">=4" + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" } }, - "node_modules/@babel/types": { - "version": "7.24.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.24.0.tgz", - "integrity": "sha512-+j7a5c253RfKh8iABBhywc8NSfP5LURe7Uh4qpsh6jc+aLJguvmIUBdjSdEMQv2bENrCR5MfRdjGo7vzS/ob7w==", + "node_modules/@humanwhocodes/config-array/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, + "peer": true, "dependencies": { - "@babel/helper-string-parser": "^7.23.4", - "@babel/helper-validator-identifier": "^7.22.20", - "to-fast-properties": "^2.0.0" + "brace-expansion": "^1.1.7" }, "engines": { - "node": ">=6.9.0" + "node": "*" } }, - "node_modules/@bcoe/v8-coverage": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", - "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", - "dev": true - }, - "node_modules/@codemirror/language": { - "version": "6.9.2", - "resolved": "git+ssh://git@github.com/lishid/cm-language.git#cc6a2cc30288db6be3f879ddf0e3ef64f14ed6ab", + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", "dev": true, - "license": "MIT", - "dependencies": { - "@codemirror/state": "^6.0.0", - "@codemirror/view": "^6.0.0", - "@lezer/common": "^1.1.0", - "@lezer/highlight": "^1.0.0", - "@lezer/lr": "^1.0.0", - "style-mod": "^4.0.0" + "peer": true, + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" } }, - "node_modules/@codemirror/state": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/@codemirror/state/-/state-6.4.0.tgz", - "integrity": "sha512-hm8XshYj5Fo30Bb922QX9hXB/bxOAVH+qaqHBzw5TKa72vOeslyGwd4X8M0c1dJ9JqxlaMceOQ8RsL9tC7gU0A==", - "dev": true + "node_modules/@humanwhocodes/object-schema": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", + "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", + "deprecated": "Use @eslint/object-schema instead", + "dev": true, + "peer": true }, - "node_modules/@codemirror/view": { - "version": "6.23.0", - "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.23.0.tgz", - "integrity": "sha512-/51px9N4uW8NpuWkyUX+iam5+PM6io2fm+QmRnzwqBy5v/pwGg9T0kILFtYeum8hjuvENtgsGNKluOfqIICmeQ==", + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", "dev": true, "dependencies": { - "@codemirror/state": "^6.4.0", - "style-mod": "^4.1.0", - "w3c-keyname": "^2.2.4" + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" } }, - "node_modules/@cspotcode/source-map-support": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", - "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "node_modules/@isaacs/cliui/node_modules/ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", "dev": true, - "optional": true, - "peer": true, "dependencies": { - "@jridgewell/trace-mapping": "0.3.9" + "ansi-regex": "^6.0.1" }, "engines": { "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, - "node_modules/@cspotcode/source-map-support/node_modules/@jridgewell/trace-mapping": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", - "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "node_modules/@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/schemas": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", + "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", "dev": true, - "optional": true, - "peer": true, "dependencies": { - "@jridgewell/resolve-uri": "^3.0.3", - "@jridgewell/sourcemap-codec": "^1.4.10" + "@sinclair/typebox": "^0.27.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/@esbuild/aix-ppc64": { - "version": "0.19.11", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.19.11.tgz", - "integrity": "sha512-FnzU0LyE3ySQk7UntJO4+qIiQgI7KoODnZg5xzXIrFJlKd2P2gwHsHY4927xj9y5PJmJSzULiUCWmv7iWnNa7g==", - "cpu": [ - "ppc64" - ], + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz", + "integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==", + "dependencies": { + "@jridgewell/set-array": "^1.2.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", + "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@lezer/common": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@lezer/common/-/common-1.2.3.tgz", + "integrity": "sha512-w7ojc8ejBqr2REPsWxJjrMFsA/ysDCFICn8zEOR9mrqzOu2amhITYuLD8ag6XZf0CFXDrhKqw7+tW8cX66NaDA==", + "dev": true + }, + "node_modules/@lezer/highlight": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@lezer/highlight/-/highlight-1.2.1.tgz", + "integrity": "sha512-Z5duk4RN/3zuVO7Jq0pGLJ3qynpxUVsh7IbUbGj88+uV2ApSAn6kWg2au3iJb+0Zi7kKtqffIESgNcRXWZWmSA==", "dev": true, - "optional": true, - "os": [ - "aix" - ], + "dependencies": { + "@lezer/common": "^1.0.0" + } + }, + "node_modules/@lezer/lr": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@lezer/lr/-/lr-1.4.2.tgz", + "integrity": "sha512-pu0K1jCIdnQ12aWNaAVU5bzi7Bd1w54J3ECgANPmYLtQKP0HBj2cE/5coBD66MT10xbtIuUr7tg0Shbsvk0mDA==", + "dev": true, + "dependencies": { + "@lezer/common": "^1.0.0" + } + }, + "node_modules/@marijn/find-cluster-break": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@marijn/find-cluster-break/-/find-cluster-break-1.0.2.tgz", + "integrity": "sha512-l0h88YhZFyKdXIFNfSWpyjStDjGHwZ/U7iobcK1cQQD8sejsONdQtTVU+1wVN1PBw40PiiHB1vA5S7VTfQiP9g==", + "dev": true + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, "engines": { - "node": ">=12" + "node": ">= 8" } }, - "node_modules/@esbuild/android-arm": { - "version": "0.19.11", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.19.11.tgz", - "integrity": "sha512-5OVapq0ClabvKvQ58Bws8+wkLCV+Rxg7tUVbo9xu034Nm536QTII4YzhaFriQ7rMrorfnFKUsArD2lqKbFY4vw==", - "cpu": [ - "arm" - ], + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", "dev": true, "optional": true, - "os": [ - "android" - ], "engines": { - "node": ">=12" + "node": ">=14" } }, - "node_modules/@esbuild/android-arm64": { - "version": "0.19.11", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.19.11.tgz", - "integrity": "sha512-aiu7K/5JnLj//KOnOfEZ0D90obUkRzDMyqd/wNAUQ34m4YUPVhRZpnqKV9uqDGxT7cToSDnIHsGooyIczu9T+Q==", + "node_modules/@polka/url": { + "version": "1.0.0-next.28", + "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.28.tgz", + "integrity": "sha512-8LduaNlMZGwdZ6qWrKlfa+2M4gahzFkprZiAt2TF8uS0qQgBizKXpXURqvTJ4WtmupWxaLqjRb2UCTe72mu+Aw==", + "dev": true + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.30.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.30.0.tgz", + "integrity": "sha512-qFcFto9figFLz2g25DxJ1WWL9+c91fTxnGuwhToCl8BaqDsDYMl/kOnBXAyAqkkzAWimYMSWNPWEjt+ADAHuoQ==", "cpu": [ - "arm64" + "arm" ], "dev": true, "optional": true, "os": [ "android" - ], - "engines": { - "node": ">=12" - } + ] }, - "node_modules/@esbuild/android-x64": { - "version": "0.19.11", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.19.11.tgz", - "integrity": "sha512-eccxjlfGw43WYoY9QgB82SgGgDbibcqyDTlk3l3C0jOVHKxrjdc9CTwDUQd0vkvYg5um0OH+GpxYvp39r+IPOg==", + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.30.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.30.0.tgz", + "integrity": "sha512-vqrQdusvVl7dthqNjWCL043qelBK+gv9v3ZiqdxgaJvmZyIAAXMjeGVSqZynKq69T7062T5VrVTuikKSAAVP6A==", "cpu": [ - "x64" + "arm64" ], "dev": true, "optional": true, "os": [ "android" - ], - "engines": { - "node": ">=12" - } + ] }, - "node_modules/@esbuild/darwin-arm64": { - "version": "0.19.11", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.19.11.tgz", - "integrity": "sha512-ETp87DRWuSt9KdDVkqSoKoLFHYTrkyz2+65fj9nfXsaV3bMhTCjtQfw3y+um88vGRKRiF7erPrh/ZuIdLUIVxQ==", + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.30.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.30.0.tgz", + "integrity": "sha512-617pd92LhdA9+wpixnzsyhVft3szYiN16aNUMzVkf2N+yAk8UXY226Bfp36LvxYTUt7MO/ycqGFjQgJ0wlMaWQ==", "cpu": [ "arm64" ], @@ -767,15 +934,12 @@ "optional": true, "os": [ "darwin" - ], - "engines": { - "node": ">=12" - } + ] }, - "node_modules/@esbuild/darwin-x64": { - "version": "0.19.11", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.19.11.tgz", - "integrity": "sha512-fkFUiS6IUK9WYUO/+22omwetaSNl5/A8giXvQlcinLIjVkxwTLSktbF5f/kJMftM2MJp9+fXqZ5ezS7+SALp4g==", + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.30.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.30.0.tgz", + "integrity": "sha512-Y3b4oDoaEhCypg8ajPqigKDcpi5ZZovemQl9Edpem0uNv6UUjXv7iySBpGIUTSs2ovWOzYpfw9EbFJXF/fJHWw==", "cpu": [ "x64" ], @@ -783,15 +947,12 @@ "optional": true, "os": [ "darwin" - ], - "engines": { - "node": ">=12" - } + ] }, - "node_modules/@esbuild/freebsd-arm64": { - "version": "0.19.11", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.11.tgz", - "integrity": "sha512-lhoSp5K6bxKRNdXUtHoNc5HhbXVCS8V0iZmDvyWvYq9S5WSfTIHU2UGjcGt7UeS6iEYp9eeymIl5mJBn0yiuxA==", + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.30.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.30.0.tgz", + "integrity": "sha512-3REQJ4f90sFIBfa0BUokiCdrV/E4uIjhkWe1bMgCkhFXbf4D8YN6C4zwJL881GM818qVYE9BO3dGwjKhpo2ABA==", "cpu": [ "arm64" ], @@ -799,15 +960,12 @@ "optional": true, "os": [ "freebsd" - ], - "engines": { - "node": ">=12" - } + ] }, - "node_modules/@esbuild/freebsd-x64": { - "version": "0.19.11", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.19.11.tgz", - "integrity": "sha512-JkUqn44AffGXitVI6/AbQdoYAq0TEullFdqcMY/PCUZ36xJ9ZJRtQabzMA+Vi7r78+25ZIBosLTOKnUXBSi1Kw==", + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.30.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.30.0.tgz", + "integrity": "sha512-ZtY3Y8icbe3Cc+uQicsXG5L+CRGUfLZjW6j2gn5ikpltt3Whqjfo5mkyZ86UiuHF9Q3ZsaQeW7YswlHnN+lAcg==", "cpu": [ "x64" ], @@ -815,15 +973,12 @@ "optional": true, "os": [ "freebsd" - ], - "engines": { - "node": ">=12" - } + ] }, - "node_modules/@esbuild/linux-arm": { - "version": "0.19.11", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.19.11.tgz", - "integrity": "sha512-3CRkr9+vCV2XJbjwgzjPtO8T0SZUmRZla+UL1jw+XqHZPkPgZiyWvbDvl9rqAN8Zl7qJF0O/9ycMtjU67HN9/Q==", + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.30.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.30.0.tgz", + "integrity": "sha512-bsPGGzfiHXMhQGuFGpmo2PyTwcrh2otL6ycSZAFTESviUoBOuxF7iBbAL5IJXc/69peXl5rAtbewBFeASZ9O0g==", "cpu": [ "arm" ], @@ -831,15 +986,25 @@ "optional": true, "os": [ "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.30.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.30.0.tgz", + "integrity": "sha512-kvyIECEhs2DrrdfQf++maCWJIQ974EI4txlz1nNSBaCdtf7i5Xf1AQCEJWOC5rEBisdaMFFnOWNLYt7KpFqy5A==", + "cpu": [ + "arm" ], - "engines": { - "node": ">=12" - } + "dev": true, + "optional": true, + "os": [ + "linux" + ] }, - "node_modules/@esbuild/linux-arm64": { - "version": "0.19.11", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.19.11.tgz", - "integrity": "sha512-LneLg3ypEeveBSMuoa0kwMpCGmpu8XQUh+mL8XXwoYZ6Be2qBnVtcDI5azSvh7vioMDhoJFZzp9GWp9IWpYoUg==", + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.30.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.30.0.tgz", + "integrity": "sha512-CFE7zDNrokaotXu+shwIrmWrFxllg79vciH4E/zeK7NitVuWEaXRzS0mFfFvyhZfn8WfVOG/1E9u8/DFEgK7WQ==", "cpu": [ "arm64" ], @@ -847,31 +1012,25 @@ "optional": true, "os": [ "linux" - ], - "engines": { - "node": ">=12" - } + ] }, - "node_modules/@esbuild/linux-ia32": { - "version": "0.19.11", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.19.11.tgz", - "integrity": "sha512-caHy++CsD8Bgq2V5CodbJjFPEiDPq8JJmBdeyZ8GWVQMjRD0sU548nNdwPNvKjVpamYYVL40AORekgfIubwHoA==", + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.30.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.30.0.tgz", + "integrity": "sha512-MctNTBlvMcIBP0t8lV/NXiUwFg9oK5F79CxLU+a3xgrdJjfBLVIEHSAjQ9+ipofN2GKaMLnFFXLltg1HEEPaGQ==", "cpu": [ - "ia32" + "arm64" ], "dev": true, "optional": true, "os": [ "linux" - ], - "engines": { - "node": ">=12" - } + ] }, - "node_modules/@esbuild/linux-loong64": { - "version": "0.19.11", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.19.11.tgz", - "integrity": "sha512-ppZSSLVpPrwHccvC6nQVZaSHlFsvCQyjnvirnVjbKSHuE5N24Yl8F3UwYUUR1UEPaFObGD2tSvVKbvR+uT1Nrg==", + "node_modules/@rollup/rollup-linux-loongarch64-gnu": { + "version": "4.30.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.30.0.tgz", + "integrity": "sha512-fBpoYwLEPivL3q368+gwn4qnYnr7GVwM6NnMo8rJ4wb0p/Y5lg88vQRRP077gf+tc25akuqd+1Sxbn9meODhwA==", "cpu": [ "loong64" ], @@ -879,31 +1038,12 @@ "optional": true, "os": [ "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-mips64el": { - "version": "0.19.11", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.19.11.tgz", - "integrity": "sha512-B5x9j0OgjG+v1dF2DkH34lr+7Gmv0kzX6/V0afF41FkPMMqaQ77pH7CrhWeR22aEeHKaeZVtZ6yFwlxOKPVFyg==", - "cpu": [ - "mips64el" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } + ] }, - "node_modules/@esbuild/linux-ppc64": { - "version": "0.19.11", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.19.11.tgz", - "integrity": "sha512-MHrZYLeCG8vXblMetWyttkdVRjQlQUb/oMgBNurVEnhj4YWOr4G5lmBfZjHYQHHN0g6yDmCAQRR8MUHldvvRDA==", + "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { + "version": "4.30.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.30.0.tgz", + "integrity": "sha512-1hiHPV6dUaqIMXrIjN+vgJqtfkLpqHS1Xsg0oUfUVD98xGp1wX89PIXgDF2DWra1nxAd8dfE0Dk59MyeKaBVAw==", "cpu": [ "ppc64" ], @@ -911,15 +1051,12 @@ "optional": true, "os": [ "linux" - ], - "engines": { - "node": ">=12" - } + ] }, - "node_modules/@esbuild/linux-riscv64": { - "version": "0.19.11", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.19.11.tgz", - "integrity": "sha512-f3DY++t94uVg141dozDu4CCUkYW+09rWtaWfnb3bqe4w5NqmZd6nPVBm+qbz7WaHZCoqXqHz5p6CM6qv3qnSSQ==", + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.30.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.30.0.tgz", + "integrity": "sha512-U0xcC80SMpEbvvLw92emHrNjlS3OXjAM0aVzlWfar6PR0ODWCTQtKeeB+tlAPGfZQXicv1SpWwRz9Hyzq3Jx3g==", "cpu": [ "riscv64" ], @@ -927,15 +1064,12 @@ "optional": true, "os": [ "linux" - ], - "engines": { - "node": ">=12" - } + ] }, - "node_modules/@esbuild/linux-s390x": { - "version": "0.19.11", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.19.11.tgz", - "integrity": "sha512-A5xdUoyWJHMMlcSMcPGVLzYzpcY8QP1RtYzX5/bS4dvjBGVxdhuiYyFwp7z74ocV7WDc0n1harxmpq2ePOjI0Q==", + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.30.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.30.0.tgz", + "integrity": "sha512-VU/P/IODrNPasgZDLIFJmMiLGez+BN11DQWfTVlViJVabyF3JaeaJkP6teI8760f18BMGCQOW9gOmuzFaI1pUw==", "cpu": [ "s390x" ], @@ -943,15 +1077,12 @@ "optional": true, "os": [ "linux" - ], - "engines": { - "node": ">=12" - } + ] }, - "node_modules/@esbuild/linux-x64": { - "version": "0.19.11", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.19.11.tgz", - "integrity": "sha512-grbyMlVCvJSfxFQUndw5mCtWs5LO1gUlwP4CDi4iJBbVpZcqLVT29FxgGuBJGSzyOxotFG4LoO5X+M1350zmPA==", + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.30.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.30.0.tgz", + "integrity": "sha512-laQVRvdbKmjXuFA3ZiZj7+U24FcmoPlXEi2OyLfbpY2MW1oxLt9Au8q9eHd0x6Pw/Kw4oe9gwVXWwIf2PVqblg==", "cpu": [ "x64" ], @@ -959,63 +1090,25 @@ "optional": true, "os": [ "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/netbsd-x64": { - "version": "0.19.11", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.19.11.tgz", - "integrity": "sha512-13jvrQZJc3P230OhU8xgwUnDeuC/9egsjTkXN49b3GcS5BKvJqZn86aGM8W9pd14Kd+u7HuFBMVtrNGhh6fHEQ==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/openbsd-x64": { - "version": "0.19.11", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.19.11.tgz", - "integrity": "sha512-ysyOGZuTp6SNKPE11INDUeFVVQFrhcNDVUgSQVDzqsqX38DjhPEPATpid04LCoUr2WXhQTEZ8ct/EgJCUDpyNw==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=12" - } + ] }, - "node_modules/@esbuild/sunos-x64": { - "version": "0.19.11", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.19.11.tgz", - "integrity": "sha512-Hf+Sad9nVwvtxy4DXCZQqLpgmRTQqyFyhT3bZ4F2XlJCjxGmRFF0Shwn9rzhOYRB61w9VMXUkxlBy56dk9JJiQ==", + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.30.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.30.0.tgz", + "integrity": "sha512-3wzKzduS7jzxqcOvy/ocU/gMR3/QrHEFLge5CD7Si9fyHuoXcidyYZ6jyx8OPYmCcGm3uKTUl+9jUSAY74Ln5A==", "cpu": [ "x64" ], "dev": true, "optional": true, "os": [ - "sunos" - ], - "engines": { - "node": ">=12" - } + "linux" + ] }, - "node_modules/@esbuild/win32-arm64": { - "version": "0.19.11", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.19.11.tgz", - "integrity": "sha512-0P58Sbi0LctOMOQbpEOvOL44Ne0sqbS0XWHMvvrg6NE5jQ1xguCSSw9jQeUk2lfrXYsKDdOe6K+oZiwKPilYPQ==", + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.30.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.30.0.tgz", + "integrity": "sha512-jROwnI1+wPyuv696rAFHp5+6RFhXGGwgmgSfzE8e4xfit6oLRg7GyMArVUoM3ChS045OwWr9aTnU+2c1UdBMyw==", "cpu": [ "arm64" ], @@ -1023,15 +1116,12 @@ "optional": true, "os": [ "win32" - ], - "engines": { - "node": ">=12" - } + ] }, - "node_modules/@esbuild/win32-ia32": { - "version": "0.19.11", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.19.11.tgz", - "integrity": "sha512-6YOrWS+sDJDmshdBIQU+Uoyh7pQKrdykdefC1avn76ss5c+RN6gut3LZA4E2cH5xUEp5/cA0+YxRaVtRAb0xBg==", + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.30.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.30.0.tgz", + "integrity": "sha512-duzweyup5WELhcXx5H1jokpr13i3BV9b48FMiikYAwk/MT1LrMYYk2TzenBd0jj4ivQIt58JWSxc19y4SvLP4g==", "cpu": [ "ia32" ], @@ -1039,15 +1129,12 @@ "optional": true, "os": [ "win32" - ], - "engines": { - "node": ">=12" - } + ] }, - "node_modules/@esbuild/win32-x64": { - "version": "0.19.11", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.19.11.tgz", - "integrity": "sha512-vfkhltrjCAb603XaFhqhAF4LGDi2M4OrCRrFusyQ+iTLQ/o60QQXxc9cZC/FFpihBI9N1Grn6SMKVJ4KP7Fuiw==", + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.30.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.30.0.tgz", + "integrity": "sha512-DYvxS0M07PvgvavMIybCOBYheyrqlui6ZQBHJs6GqduVzHSZ06TPPvlfvnYstjODHQ8UUXFwt5YE+h0jFI8kwg==", "cpu": [ "x64" ], @@ -1055,1108 +1142,956 @@ "optional": true, "os": [ "win32" - ], - "engines": { - "node": ">=12" - } + ] }, - "node_modules/@eslint-community/eslint-utils": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", - "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "node_modules/@sinclair/typebox": { + "version": "0.27.8", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", + "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", + "dev": true + }, + "node_modules/@tsconfig/svelte": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/@tsconfig/svelte/-/svelte-5.0.4.tgz", + "integrity": "sha512-BV9NplVgLmSi4mwKzD8BD/NQ8erOY/nUE/GpgWe2ckx+wIQF5RyRirn/QsSSCPeulVpc3RA/iJt6DpfTIZps0Q==", + "dev": true + }, + "node_modules/@types/codemirror": { + "version": "5.60.8", + "resolved": "https://registry.npmjs.org/@types/codemirror/-/codemirror-5.60.8.tgz", + "integrity": "sha512-VjFgDF/eB+Aklcy15TtOTLQeMjTo07k7KAjql8OK5Dirr7a6sJY4T1uVBDuTVG9VEmn1uUsohOpYnVfgC6/jyw==", "dev": true, "dependencies": { - "eslint-visitor-keys": "^3.3.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + "@types/tern": "*" } }, - "node_modules/@eslint-community/regexpp": { - "version": "4.10.0", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.0.tgz", - "integrity": "sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==", + "node_modules/@types/electron": { + "name": "@ophidian/electron-types", + "version": "24.3.1", + "resolved": "https://registry.npmjs.org/@ophidian/electron-types/-/electron-types-24.3.1.tgz", + "integrity": "sha512-fzvB7sQMxTbQHbp3rcYIxaqHiWjFnpSBVvZBSDH0Rq0xMvihBH1D4/O2JqybSlVpYtbvXR4nVKeH3w4JmHLTag==", "dev": true, - "engines": { - "node": "^12.0.0 || ^14.0.0 || >=16.0.0" - } + "peer": true }, - "node_modules/@eslint/eslintrc": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", - "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", + "node_modules/@types/estree": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", + "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==" + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true + }, + "node_modules/@types/luxon": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/@types/luxon/-/luxon-3.4.2.tgz", + "integrity": "sha512-TifLZlFudklWlMBfhubvgqTXRzLDI5pCbGa4P8a3wPyUQSW+1xQ5eDsreP9DWHX3tjq1ke96uYG/nwundroWcA==", + "dev": true + }, + "node_modules/@types/node": { + "version": "20.17.12", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.12.tgz", + "integrity": "sha512-vo/wmBgMIiEA23A/knMfn/cf37VnuF52nZh5ZoW0GWt4e4sxNquibrMRJ7UQsA06+MBx9r/H1jsI9grYjQCQlw==", "dev": true, - "peer": true, "dependencies": { - "ajv": "^6.12.4", - "debug": "^4.3.2", - "espree": "^9.6.0", - "globals": "^13.19.0", - "ignore": "^5.2.0", - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", - "minimatch": "^3.1.2", - "strip-json-comments": "^3.1.1" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" + "undici-types": "~6.19.2" } }, - "node_modules/@eslint/eslintrc/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "node_modules/@types/obsidian-typings": { + "name": "obsidian-typings", + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/obsidian-typings/-/obsidian-typings-1.1.6.tgz", + "integrity": "sha512-p3iWfvHDJfrwdYKAhb8adlBVOGtGteEy5nE7Jzhb8K5nyrTb5DvOtG+BQGneVM7Ou6UMkZnRoOLPkhJMXLquGw==", + "dev": true, + "peerDependencies": { + "@types/electron": "npm:@ophidian/electron-types", + "@types/node": "^20.10.7", + "obsidian": "^1.5.7-1" + } + }, + "node_modules/@types/semver": { + "version": "7.5.8", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.8.tgz", + "integrity": "sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==", + "dev": true + }, + "node_modules/@types/tern": { + "version": "0.23.9", + "resolved": "https://registry.npmjs.org/@types/tern/-/tern-0.23.9.tgz", + "integrity": "sha512-ypzHFE/wBzh+BlH6rrBgS5I/Z7RD21pGhZ2rltb/+ZrVM1awdZwjx7hE5XfuYgHWk9uvV5HLZN3SloevCAp3Bw==", "dev": true, - "peer": true, "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "@types/estree": "*" } }, - "node_modules/@eslint/eslintrc/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "6.18.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.18.1.tgz", + "integrity": "sha512-nISDRYnnIpk7VCFrGcu1rnZfM1Dh9LRHnfgdkjcbi/l7g16VYRri3TjXi9Ir4lOZSw5N/gnV/3H7jIPQ8Q4daA==", "dev": true, - "peer": true, "dependencies": { - "brace-expansion": "^1.1.7" + "@eslint-community/regexpp": "^4.5.1", + "@typescript-eslint/scope-manager": "6.18.1", + "@typescript-eslint/type-utils": "6.18.1", + "@typescript-eslint/utils": "6.18.1", + "@typescript-eslint/visitor-keys": "6.18.1", + "debug": "^4.3.4", + "graphemer": "^1.4.0", + "ignore": "^5.2.4", + "natural-compare": "^1.4.0", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" }, "engines": { - "node": "*" + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^6.0.0 || ^6.0.0-alpha", + "eslint": "^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/@eslint/js": { - "version": "8.56.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.56.0.tgz", - "integrity": "sha512-gMsVel9D7f2HLkBma9VbtzZRehRogVRfbr++f06nL2vnCGCNlzOD+/MUov/F4p8myyAHspEhVobgjpX64q5m6A==", + "node_modules/@typescript-eslint/parser": { + "version": "6.18.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.18.1.tgz", + "integrity": "sha512-zct/MdJnVaRRNy9e84XnVtRv9Vf91/qqe+hZJtKanjojud4wAVy/7lXxJmMyX6X6J+xc6c//YEWvpeif8cAhWA==", "dev": true, - "peer": true, + "dependencies": { + "@typescript-eslint/scope-manager": "6.18.1", + "@typescript-eslint/types": "6.18.1", + "@typescript-eslint/typescript-estree": "6.18.1", + "@typescript-eslint/visitor-keys": "6.18.1", + "debug": "^4.3.4" + }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/@humanwhocodes/config-array": { - "version": "0.11.14", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz", - "integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==", + "node_modules/@typescript-eslint/scope-manager": { + "version": "6.18.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.18.1.tgz", + "integrity": "sha512-BgdBwXPFmZzaZUuw6wKiHKIovms97a7eTImjkXCZE04TGHysG+0hDQPmygyvgtkoB/aOQwSM/nWv3LzrOIQOBw==", "dev": true, - "peer": true, "dependencies": { - "@humanwhocodes/object-schema": "^2.0.2", - "debug": "^4.3.1", - "minimatch": "^3.0.5" + "@typescript-eslint/types": "6.18.1", + "@typescript-eslint/visitor-keys": "6.18.1" }, "engines": { - "node": ">=10.10.0" - } - }, - "node_modules/@humanwhocodes/config-array/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "peer": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/@humanwhocodes/config-array/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "node_modules/@typescript-eslint/type-utils": { + "version": "6.18.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.18.1.tgz", + "integrity": "sha512-wyOSKhuzHeU/5pcRDP2G2Ndci+4g653V43gXTpt4nbyoIOAASkGDA9JIAgbQCdCkcr1MvpSYWzxTz0olCn8+/Q==", "dev": true, - "peer": true, "dependencies": { - "brace-expansion": "^1.1.7" + "@typescript-eslint/typescript-estree": "6.18.1", + "@typescript-eslint/utils": "6.18.1", + "debug": "^4.3.4", + "ts-api-utils": "^1.0.1" }, "engines": { - "node": "*" + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/@humanwhocodes/module-importer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", - "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "node_modules/@typescript-eslint/types": { + "version": "6.18.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.18.1.tgz", + "integrity": "sha512-4TuMAe+tc5oA7wwfqMtB0Y5OrREPF1GeJBAjqwgZh1lEMH5PJQgWgHGfYufVB51LtjD+peZylmeyxUXPfENLCw==", "dev": true, - "peer": true, "engines": { - "node": ">=12.22" + "node": "^16.0.0 || >=18.0.0" }, "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/@humanwhocodes/object-schema": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.2.tgz", - "integrity": "sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw==", - "dev": true, - "peer": true - }, - "node_modules/@isaacs/cliui": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", - "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "node_modules/@typescript-eslint/typescript-estree": { + "version": "6.18.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.18.1.tgz", + "integrity": "sha512-fv9B94UAhywPRhUeeV/v+3SBDvcPiLxRZJw/xZeeGgRLQZ6rLMG+8krrJUyIf6s1ecWTzlsbp0rlw7n9sjufHA==", "dev": true, "dependencies": { - "string-width": "^5.1.2", - "string-width-cjs": "npm:string-width@^4.2.0", - "strip-ansi": "^7.0.1", - "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", - "wrap-ansi": "^8.1.0", - "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + "@typescript-eslint/types": "6.18.1", + "@typescript-eslint/visitor-keys": "6.18.1", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "minimatch": "9.0.3", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" }, "engines": { - "node": ">=12" + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/@isaacs/cliui/node_modules/ansi-regex": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "node_modules/@typescript-eslint/utils": { + "version": "6.18.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.18.1.tgz", + "integrity": "sha512-zZmTuVZvD1wpoceHvoQpOiewmWu3uP9FuTWo8vqpy2ffsmfCE8mklRPi+vmnIYAIk9t/4kOThri2QCDgor+OpQ==", "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "@types/json-schema": "^7.0.12", + "@types/semver": "^7.5.0", + "@typescript-eslint/scope-manager": "6.18.1", + "@typescript-eslint/types": "6.18.1", + "@typescript-eslint/typescript-estree": "6.18.1", + "semver": "^7.5.4" + }, "engines": { - "node": ">=12" + "node": "^16.0.0 || >=18.0.0" }, "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" } }, - "node_modules/@isaacs/cliui/node_modules/strip-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "node_modules/@typescript-eslint/visitor-keys": { + "version": "6.18.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.18.1.tgz", + "integrity": "sha512-/kvt0C5lRqGoCfsbmm7/CwMqoSkY3zzHLIjdhHZQW3VFrnz7ATecOHR7nb7V+xn4286MBxfnQfQhAmCI0u+bJA==", "dev": true, "dependencies": { - "ansi-regex": "^6.0.1" + "@typescript-eslint/types": "6.18.1", + "eslint-visitor-keys": "^3.4.1" }, "engines": { - "node": ">=12" + "node": "^16.0.0 || >=18.0.0" }, "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/@istanbuljs/schema": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", - "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "node_modules/@ungap/structured-clone": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.1.tgz", + "integrity": "sha512-fEzPV3hSkSMltkw152tJKNARhOupqbH96MZWyRjNaYZOMIzbrTeQDG+MTc6Mr2pgzFQzFxAfmhGDNP5QK++2ZA==", "dev": true, - "engines": { - "node": ">=8" - } + "peer": true }, - "node_modules/@jest/schemas": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", - "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", + "node_modules/@vitest/coverage-v8": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-1.6.0.tgz", + "integrity": "sha512-KvapcbMY/8GYIG0rlwwOKCVNRc0OL20rrhFkg/CHNzncV03TE2XWvO5w9uZYoxNiMEBacAJt3unSOiZ7svePew==", "dev": true, "dependencies": { - "@sinclair/typebox": "^0.27.8" + "@ampproject/remapping": "^2.2.1", + "@bcoe/v8-coverage": "^0.2.3", + "debug": "^4.3.4", + "istanbul-lib-coverage": "^3.2.2", + "istanbul-lib-report": "^3.0.1", + "istanbul-lib-source-maps": "^5.0.4", + "istanbul-reports": "^3.1.6", + "magic-string": "^0.30.5", + "magicast": "^0.3.3", + "picocolors": "^1.0.0", + "std-env": "^3.5.0", + "strip-literal": "^2.0.0", + "test-exclude": "^6.0.0" }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", - "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", - "dependencies": { - "@jridgewell/set-array": "^1.0.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.9" + "funding": { + "url": "https://opencollective.com/vitest" }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz", - "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/set-array": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", - "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.4.15", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", - "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==" - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.21", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.21.tgz", - "integrity": "sha512-SRfKmRe1KvYnxjEMtxEr+J4HIeMX5YBg/qhRHpxEIGjhX1rshcHlnFUE9K0GazhVKWM7B+nARSkV8LuvJdJ5/g==", - "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" + "peerDependencies": { + "vitest": "1.6.0" } }, - "node_modules/@lezer/common": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@lezer/common/-/common-1.2.0.tgz", - "integrity": "sha512-Wmvlm4q6tRpwiy20TnB3yyLTZim38Tkc50dPY8biQRwqE+ati/wD84rm3N15hikvdT4uSg9phs9ubjvcLmkpKg==", - "dev": true - }, - "node_modules/@lezer/highlight": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@lezer/highlight/-/highlight-1.2.0.tgz", - "integrity": "sha512-WrS5Mw51sGrpqjlh3d4/fOwpEV2Hd3YOkp9DBt4k8XZQcoTHZFB7sx030A6OcahF4J1nDQAa3jXlTVVYH50IFA==", + "node_modules/@vitest/expect": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-1.6.0.tgz", + "integrity": "sha512-ixEvFVQjycy/oNgHjqsL6AZCDduC+tflRluaHIzKIsdbzkLn2U/iBnVeJwB6HsIjQBdfMR8Z0tRxKUsvFJEeWQ==", "dev": true, "dependencies": { - "@lezer/common": "^1.0.0" + "@vitest/spy": "1.6.0", + "@vitest/utils": "1.6.0", + "chai": "^4.3.10" + }, + "funding": { + "url": "https://opencollective.com/vitest" } }, - "node_modules/@lezer/lr": { - "version": "1.3.14", - "resolved": "https://registry.npmjs.org/@lezer/lr/-/lr-1.3.14.tgz", - "integrity": "sha512-z5mY4LStlA3yL7aHT/rqgG614cfcvklS+8oFRFBYrs4YaWLJyKKM4+nN6KopToX0o9Hj6zmH6M5kinOYuy06ug==", + "node_modules/@vitest/runner": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-1.6.0.tgz", + "integrity": "sha512-P4xgwPjwesuBiHisAVz/LSSZtDjOTPYZVmNAnpHHSR6ONrf8eCJOFRvUwdHn30F5M1fxhqtl7QZQUk2dprIXAg==", "dev": true, "dependencies": { - "@lezer/common": "^1.0.0" + "@vitest/utils": "1.6.0", + "p-limit": "^5.0.0", + "pathe": "^1.1.1" + }, + "funding": { + "url": "https://opencollective.com/vitest" } }, - "node_modules/@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "node_modules/@vitest/runner/node_modules/p-limit": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-5.0.0.tgz", + "integrity": "sha512-/Eaoq+QyLSiXQ4lyYV23f14mZRQcXnxfHrN0vCai+ak9G0pp9iEQukIIZq5NccEvwRB8PUnZT0KsOoDCINS1qQ==", "dev": true, "dependencies": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" + "yocto-queue": "^1.0.0" }, "engines": { - "node": ">= 8" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "node_modules/@vitest/runner/node_modules/yocto-queue": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.1.1.tgz", + "integrity": "sha512-b4JR1PFR10y1mKjhHY9LaGo6tmrgjit7hxVIeAmyMw3jegXR4dhYqLaQF5zMXZxY7tLpMyJeLjr1C4rLmkVe8g==", "dev": true, "engines": { - "node": ">= 8" + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "node_modules/@vitest/snapshot": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-1.6.0.tgz", + "integrity": "sha512-+Hx43f8Chus+DCmygqqfetcAZrDJwvTj0ymqjQq4CvmpKFSTVteEOBzCusu1x2tt4OJcvBflyHUE0DZSLgEMtQ==", "dev": true, "dependencies": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - }, - "engines": { - "node": ">= 8" + "magic-string": "^0.30.5", + "pathe": "^1.1.1", + "pretty-format": "^29.7.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" } }, - "node_modules/@pkgjs/parseargs": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", - "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "node_modules/@vitest/spy": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-1.6.0.tgz", + "integrity": "sha512-leUTap6B/cqi/bQkXUu6bQV5TZPx7pmMBKBQiI0rJA8c3pB56ZsaTbREnF7CJfmvAS4V2cXIBAh/3rVwrrCYgw==", "dev": true, - "optional": true, - "engines": { - "node": ">=14" + "dependencies": { + "tinyspy": "^2.2.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" } }, - "node_modules/@polka/url": { - "version": "1.0.0-next.25", - "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.25.tgz", - "integrity": "sha512-j7P6Rgr3mmtdkeDGTe0E/aYyWEWVtc5yFXtHCRHs28/jptDEWfaVOc5T7cblqy1XKPPfCxJc/8DwQ5YgLOZOVQ==", - "dev": true - }, - "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.13.0.tgz", - "integrity": "sha512-5ZYPOuaAqEH/W3gYsRkxQATBW3Ii1MfaT4EQstTnLKViLi2gLSQmlmtTpGucNP3sXEpOiI5tdGhjdE111ekyEg==", - "cpu": [ - "arm" - ], - "dev": true, - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@rollup/rollup-android-arm64": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.13.0.tgz", - "integrity": "sha512-BSbaCmn8ZadK3UAQdlauSvtaJjhlDEjS5hEVVIN3A4bbl3X+otyf/kOJV08bYiRxfejP3DXFzO2jz3G20107+Q==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.13.0.tgz", - "integrity": "sha512-Ovf2evVaP6sW5Ut0GHyUSOqA6tVKfrTHddtmxGQc1CTQa1Cw3/KMCDEEICZBbyppcwnhMwcDce9ZRxdWRpVd6g==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.13.0.tgz", - "integrity": "sha512-U+Jcxm89UTK592vZ2J9st9ajRv/hrwHdnvyuJpa5A2ngGSVHypigidkQJP+YiGL6JODiUeMzkqQzbCG3At81Gg==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.13.0.tgz", - "integrity": "sha512-8wZidaUJUTIR5T4vRS22VkSMOVooG0F4N+JSwQXWSRiC6yfEsFMLTYRFHvby5mFFuExHa/yAp9juSphQQJAijQ==", - "cpu": [ - "arm" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.13.0.tgz", - "integrity": "sha512-Iu0Kno1vrD7zHQDxOmvweqLkAzjxEVqNhUIXBsZ8hu8Oak7/5VTPrxOEZXYC1nmrBVJp0ZcL2E7lSuuOVaE3+w==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.13.0.tgz", - "integrity": "sha512-C31QrW47llgVyrRjIwiOwsHFcaIwmkKi3PCroQY5aVq4H0A5v/vVVAtFsI1nfBngtoRpeREvZOkIhmRwUKkAdw==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.13.0.tgz", - "integrity": "sha512-Oq90dtMHvthFOPMl7pt7KmxzX7E71AfyIhh+cPhLY9oko97Zf2C9tt/XJD4RgxhaGeAraAXDtqxvKE1y/j35lA==", - "cpu": [ - "riscv64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.13.0.tgz", - "integrity": "sha512-yUD/8wMffnTKuiIsl6xU+4IA8UNhQ/f1sAnQebmE/lyQ8abjsVyDkyRkWop0kdMhKMprpNIhPmYlCxgHrPoXoA==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.13.0.tgz", - "integrity": "sha512-9RyNqoFNdF0vu/qqX63fKotBh43fJQeYC98hCaf89DYQpv+xu0D8QFSOS0biA7cGuqJFOc1bJ+m2rhhsKcw1hw==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.13.0.tgz", - "integrity": "sha512-46ue8ymtm/5PUU6pCvjlic0z82qWkxv54GTJZgHrQUuZnVH+tvvSP0LsozIDsCBFO4VjJ13N68wqrKSeScUKdA==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.13.0.tgz", - "integrity": "sha512-P5/MqLdLSlqxbeuJ3YDeX37srC8mCflSyTrUsgbU1c/U9j6l2g2GiIdYaGD9QjdMQPMSgYm7hgg0551wHyIluw==", - "cpu": [ - "ia32" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.13.0.tgz", - "integrity": "sha512-UKXUQNbO3DOhzLRwHSpa0HnhhCgNODvfoPWv2FCXme8N/ANFfhIPMGuOT+QuKd16+B5yxZ0HdpNlqPvTMS1qfw==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@sinclair/typebox": { - "version": "0.27.8", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", - "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", - "dev": true - }, - "node_modules/@tsconfig/node10": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", - "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==", - "dev": true, - "optional": true, - "peer": true - }, - "node_modules/@tsconfig/node12": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", - "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", - "dev": true, - "optional": true, - "peer": true - }, - "node_modules/@tsconfig/node14": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", - "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", - "dev": true, - "optional": true, - "peer": true - }, - "node_modules/@tsconfig/node16": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", - "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", - "dev": true, - "optional": true, - "peer": true - }, - "node_modules/@tsconfig/svelte": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/@tsconfig/svelte/-/svelte-5.0.2.tgz", - "integrity": "sha512-BRbo1fOtyVbhfLyuCWw6wAWp+U8UQle+ZXu84MYYWzYSEB28dyfnRBIE99eoG+qdAC0po6L2ScIEivcT07UaMA==", - "dev": true - }, - "node_modules/@types/codemirror": { - "version": "5.60.8", - "resolved": "https://registry.npmjs.org/@types/codemirror/-/codemirror-5.60.8.tgz", - "integrity": "sha512-VjFgDF/eB+Aklcy15TtOTLQeMjTo07k7KAjql8OK5Dirr7a6sJY4T1uVBDuTVG9VEmn1uUsohOpYnVfgC6/jyw==", + "node_modules/@vitest/ui": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@vitest/ui/-/ui-1.6.0.tgz", + "integrity": "sha512-k3Lyo+ONLOgylctiGovRKy7V4+dIN2yxstX3eY5cWFXH6WP+ooVX79YSyi0GagdTQzLmT43BF27T0s6dOIPBXA==", "dev": true, "dependencies": { - "@types/tern": "*" + "@vitest/utils": "1.6.0", + "fast-glob": "^3.3.2", + "fflate": "^0.8.1", + "flatted": "^3.2.9", + "pathe": "^1.1.1", + "picocolors": "^1.0.0", + "sirv": "^2.0.4" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "vitest": "1.6.0" } }, - "node_modules/@types/estree": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", - "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==" - }, - "node_modules/@types/istanbul-lib-coverage": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", - "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", - "dev": true - }, - "node_modules/@types/json-schema": { - "version": "7.0.15", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", - "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", - "dev": true - }, - "node_modules/@types/luxon": { - "version": "3.4.2", - "resolved": "https://registry.npmjs.org/@types/luxon/-/luxon-3.4.2.tgz", - "integrity": "sha512-TifLZlFudklWlMBfhubvgqTXRzLDI5pCbGa4P8a3wPyUQSW+1xQ5eDsreP9DWHX3tjq1ke96uYG/nwundroWcA==", - "dev": true - }, - "node_modules/@types/node": { - "version": "20.11.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.0.tgz", - "integrity": "sha512-o9bjXmDNcF7GbM4CNQpmi+TutCgap/K3w1JyKgxAjqx41zp9qlIAVFi0IhCNsJcXolEqLWhbFbEeL0PvYm4pcQ==", + "node_modules/@vitest/utils": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-1.6.0.tgz", + "integrity": "sha512-21cPiuGMoMZwiOHa2i4LXkMkMkCGzA+MVFV70jRwHo95dL4x/ts5GZhML1QWuy7yfp3WzK3lRvZi3JnXTYqrBw==", "dev": true, "dependencies": { - "undici-types": "~5.26.4" + "diff-sequences": "^29.6.3", + "estree-walker": "^3.0.3", + "loupe": "^2.3.7", + "pretty-format": "^29.7.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" } }, - "node_modules/@types/obsidian-typings": { - "name": "obsidian-typings", - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/obsidian-typings/-/obsidian-typings-1.0.6.tgz", - "integrity": "sha512-pgQeeIa7Lj6qYjNFAewv8ZcguJsTRTXl9e3cboCGTSmvicmyBATH58UNbC+ioUjvm8uBA4sMDUUbTeDpGxRH4Q==", - "dev": true, - "dependencies": { - "obsidian": "^1.4.11" + "node_modules/acorn": { + "version": "8.14.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", + "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" } }, - "node_modules/@types/pug": { - "version": "2.0.10", - "resolved": "https://registry.npmjs.org/@types/pug/-/pug-2.0.10.tgz", - "integrity": "sha512-Sk/uYFOBAB7mb74XcpizmH0KOR2Pv3D2Hmrh1Dmy5BmK3MpdSa5kqZcg6EKBdklU0bFXX9gCfzvpnyUehrPIuA==", - "dev": true + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "peer": true, + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } }, - "node_modules/@types/semver": { - "version": "7.5.6", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.6.tgz", - "integrity": "sha512-dn1l8LaMea/IjDoHNd9J52uBbInB796CDffS6VdIxvqYCPSG0V0DzHp76GpaWnlhg88uYyPbXCDIowa86ybd5A==", - "dev": true + "node_modules/acorn-typescript": { + "version": "1.4.13", + "resolved": "https://registry.npmjs.org/acorn-typescript/-/acorn-typescript-1.4.13.tgz", + "integrity": "sha512-xsc9Xv0xlVfwp2o7sQ+GCQ1PgbkdcpWdTzrwXxO3xDMTAywVS3oXVOcOHuRjAPkS4P9b+yc/qNF15460v+jp4Q==", + "peerDependencies": { + "acorn": ">=8.9.0" + } }, - "node_modules/@types/tern": { - "version": "0.23.9", - "resolved": "https://registry.npmjs.org/@types/tern/-/tern-0.23.9.tgz", - "integrity": "sha512-ypzHFE/wBzh+BlH6rrBgS5I/Z7RD21pGhZ2rltb/+ZrVM1awdZwjx7hE5XfuYgHWk9uvV5HLZN3SloevCAp3Bw==", + "node_modules/acorn-walk": { + "version": "8.3.4", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz", + "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==", "dev": true, "dependencies": { - "@types/estree": "*" + "acorn": "^8.11.0" + }, + "engines": { + "node": ">=0.4.0" } }, - "node_modules/@typescript-eslint/eslint-plugin": { - "version": "6.18.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.18.1.tgz", - "integrity": "sha512-nISDRYnnIpk7VCFrGcu1rnZfM1Dh9LRHnfgdkjcbi/l7g16VYRri3TjXi9Ir4lOZSw5N/gnV/3H7jIPQ8Q4daA==", + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dev": true, + "peer": true, "dependencies": { - "@eslint-community/regexpp": "^4.5.1", - "@typescript-eslint/scope-manager": "6.18.1", - "@typescript-eslint/type-utils": "6.18.1", - "@typescript-eslint/utils": "6.18.1", - "@typescript-eslint/visitor-keys": "6.18.1", - "debug": "^4.3.4", - "graphemer": "^1.4.0", - "ignore": "^5.2.4", - "natural-compare": "^1.4.0", - "semver": "^7.5.4", - "ts-api-utils": "^1.0.1" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "@typescript-eslint/parser": "^6.0.0 || ^6.0.0-alpha", - "eslint": "^7.0.0 || ^8.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/@typescript-eslint/parser": { - "version": "6.18.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.18.1.tgz", - "integrity": "sha512-zct/MdJnVaRRNy9e84XnVtRv9Vf91/qqe+hZJtKanjojud4wAVy/7lXxJmMyX6X6J+xc6c//YEWvpeif8cAhWA==", + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true, - "dependencies": { - "@typescript-eslint/scope-manager": "6.18.1", - "@typescript-eslint/types": "6.18.1", - "@typescript-eslint/typescript-estree": "6.18.1", - "@typescript-eslint/visitor-keys": "6.18.1", - "debug": "^4.3.4" - }, "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^7.0.0 || ^8.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "node": ">=8" } }, - "node_modules/@typescript-eslint/scope-manager": { - "version": "6.18.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.18.1.tgz", - "integrity": "sha512-BgdBwXPFmZzaZUuw6wKiHKIovms97a7eTImjkXCZE04TGHysG+0hDQPmygyvgtkoB/aOQwSM/nWv3LzrOIQOBw==", + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.18.1", - "@typescript-eslint/visitor-keys": "6.18.1" + "color-convert": "^2.0.1" }, "engines": { - "node": "^16.0.0 || >=18.0.0" + "node": ">=8" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/@typescript-eslint/type-utils": { - "version": "6.18.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.18.1.tgz", - "integrity": "sha512-wyOSKhuzHeU/5pcRDP2G2Ndci+4g653V43gXTpt4nbyoIOAASkGDA9JIAgbQCdCkcr1MvpSYWzxTz0olCn8+/Q==", + "node_modules/any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", + "dev": true + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", "dev": true, "dependencies": { - "@typescript-eslint/typescript-estree": "6.18.1", - "@typescript-eslint/utils": "6.18.1", - "debug": "^4.3.4", - "ts-api-utils": "^1.0.1" + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" }, "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^7.0.0 || ^8.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "node": ">= 8" } }, - "node_modules/@typescript-eslint/types": { - "version": "6.18.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.18.1.tgz", - "integrity": "sha512-4TuMAe+tc5oA7wwfqMtB0Y5OrREPF1GeJBAjqwgZh1lEMH5PJQgWgHGfYufVB51LtjD+peZylmeyxUXPfENLCw==", + "node_modules/arg": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", + "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", + "dev": true + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "dev": true, + "peer": true + }, + "node_modules/aria-query": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.2.tgz", + "integrity": "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==", "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "node": ">= 0.4" } }, - "node_modules/@typescript-eslint/typescript-estree": { - "version": "6.18.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.18.1.tgz", - "integrity": "sha512-fv9B94UAhywPRhUeeV/v+3SBDvcPiLxRZJw/xZeeGgRLQZ6rLMG+8krrJUyIf6s1ecWTzlsbp0rlw7n9sjufHA==", + "node_modules/array-buffer-byte-length": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz", + "integrity": "sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.18.1", - "@typescript-eslint/visitor-keys": "6.18.1", - "debug": "^4.3.4", - "globby": "^11.1.0", - "is-glob": "^4.0.3", - "minimatch": "9.0.3", - "semver": "^7.5.4", - "ts-api-utils": "^1.0.1" + "call-bound": "^1.0.3", + "is-array-buffer": "^3.0.5" }, "engines": { - "node": "^16.0.0 || >=18.0.0" + "node": ">= 0.4" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/@typescript-eslint/utils": { - "version": "6.18.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.18.1.tgz", - "integrity": "sha512-zZmTuVZvD1wpoceHvoQpOiewmWu3uP9FuTWo8vqpy2ffsmfCE8mklRPi+vmnIYAIk9t/4kOThri2QCDgor+OpQ==", - "dev": true, - "dependencies": { - "@eslint-community/eslint-utils": "^4.4.0", - "@types/json-schema": "^7.0.12", - "@types/semver": "^7.5.0", - "@typescript-eslint/scope-manager": "6.18.1", - "@typescript-eslint/types": "6.18.1", - "@typescript-eslint/typescript-estree": "6.18.1", - "semver": "^7.5.4" - }, + "node_modules/array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true, "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^7.0.0 || ^8.0.0" + "node": ">=8" } }, - "node_modules/@typescript-eslint/visitor-keys": { - "version": "6.18.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.18.1.tgz", - "integrity": "sha512-/kvt0C5lRqGoCfsbmm7/CwMqoSkY3zzHLIjdhHZQW3VFrnz7ATecOHR7nb7V+xn4286MBxfnQfQhAmCI0u+bJA==", + "node_modules/arraybuffer.prototype.slice": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.4.tgz", + "integrity": "sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.18.1", - "eslint-visitor-keys": "^3.4.1" + "array-buffer-byte-length": "^1.0.1", + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "is-array-buffer": "^3.0.4" }, "engines": { - "node": "^16.0.0 || >=18.0.0" + "node": ">= 0.4" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/@ungap/structured-clone": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", - "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", + "node_modules/assertion-error": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", + "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", "dev": true, - "peer": true + "engines": { + "node": "*" + } }, - "node_modules/@vitest/coverage-v8": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-1.3.1.tgz", - "integrity": "sha512-UuBnkSJUNE9rdHjDCPyJ4fYuMkoMtnghes1XohYa4At0MS3OQSAo97FrbwSLRshYsXThMZy1+ybD/byK5llyIg==", + "node_modules/available-typed-arrays": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", + "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", "dev": true, "dependencies": { - "@ampproject/remapping": "^2.2.1", - "@bcoe/v8-coverage": "^0.2.3", - "debug": "^4.3.4", - "istanbul-lib-coverage": "^3.2.2", - "istanbul-lib-report": "^3.0.1", - "istanbul-lib-source-maps": "^4.0.1", - "istanbul-reports": "^3.1.6", - "magic-string": "^0.30.5", - "magicast": "^0.3.3", - "picocolors": "^1.0.0", - "std-env": "^3.5.0", - "test-exclude": "^6.0.0", - "v8-to-istanbul": "^9.2.0" + "possible-typed-array-names": "^1.0.0" }, - "funding": { - "url": "https://opencollective.com/vitest" + "engines": { + "node": ">= 0.4" }, - "peerDependencies": { - "vitest": "1.3.1" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/@vitest/expect": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-1.3.1.tgz", - "integrity": "sha512-xofQFwIzfdmLLlHa6ag0dPV8YsnKOCP1KdAeVVh34vSjN2dcUiXYCD9htu/9eM7t8Xln4v03U9HLxLpPlsXdZw==", + "node_modules/axobject-query": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz", + "integrity": "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", "dev": true, - "dependencies": { - "@vitest/spy": "1.3.1", - "@vitest/utils": "1.3.1", - "chai": "^4.3.10" + "engines": { + "node": ">=8" }, "funding": { - "url": "https://opencollective.com/vitest" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@vitest/runner": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-1.3.1.tgz", - "integrity": "sha512-5FzF9c3jG/z5bgCnjr8j9LNq/9OxV2uEBAITOXfoe3rdZJTdO7jzThth7FXv/6b+kdY65tpRQB7WaKhNZwX+Kg==", + "node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", "dev": true, "dependencies": { - "@vitest/utils": "1.3.1", - "p-limit": "^5.0.0", - "pathe": "^1.1.1" - }, - "funding": { - "url": "https://opencollective.com/vitest" + "balanced-match": "^1.0.0" } }, - "node_modules/@vitest/runner/node_modules/p-limit": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-5.0.0.tgz", - "integrity": "sha512-/Eaoq+QyLSiXQ4lyYV23f14mZRQcXnxfHrN0vCai+ak9G0pp9iEQukIIZq5NccEvwRB8PUnZT0KsOoDCINS1qQ==", + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "dev": true, "dependencies": { - "yocto-queue": "^1.0.0" + "fill-range": "^7.1.1" }, "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=8" } }, - "node_modules/@vitest/runner/node_modules/yocto-queue": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.0.0.tgz", - "integrity": "sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==", + "node_modules/builtin-modules": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.3.0.tgz", + "integrity": "sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==", "dev": true, "engines": { - "node": ">=12.20" + "node": ">=6" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@vitest/snapshot": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-1.3.1.tgz", - "integrity": "sha512-EF++BZbt6RZmOlE3SuTPu/NfwBF6q4ABS37HHXzs2LUVPBLx2QoY/K0fKpRChSo8eLiuxcbCVfqKgx/dplCDuQ==", + "node_modules/cac": { + "version": "6.7.14", + "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", + "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/call-bind": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", + "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==", "dev": true, "dependencies": { - "magic-string": "^0.30.5", - "pathe": "^1.1.1", - "pretty-format": "^29.7.0" + "call-bind-apply-helpers": "^1.0.0", + "es-define-property": "^1.0.0", + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.2" + }, + "engines": { + "node": ">= 0.4" }, "funding": { - "url": "https://opencollective.com/vitest" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/@vitest/spy": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-1.3.1.tgz", - "integrity": "sha512-xAcW+S099ylC9VLU7eZfdT9myV67Nor9w9zhf0mGCYJSO+zM2839tOeROTdikOi/8Qeusffvxb/MyBSOja1Uig==", + "node_modules/call-bind-apply-helpers": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.1.tgz", + "integrity": "sha512-BhYE+WDaywFg2TBWYNXAE+8B1ATnThNBqXHP5nQu0jWJdVvY2hvkpyB3qOmtmDePiS5/BDQ8wASEWGMWRG148g==", "dev": true, "dependencies": { - "tinyspy": "^2.2.0" + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" }, - "funding": { - "url": "https://opencollective.com/vitest" + "engines": { + "node": ">= 0.4" } }, - "node_modules/@vitest/ui": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/@vitest/ui/-/ui-1.3.1.tgz", - "integrity": "sha512-2UrFLJ62c/eJGPHcclstMKlAR7E1WB1ITe1isuowEPJJHi3HfqofvsUqQ1cGrEF7kitG1DJuwURUA3HLDtQkXA==", + "node_modules/call-bound": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.3.tgz", + "integrity": "sha512-YTd+6wGlNlPxSuri7Y6X8tY2dmm12UMH66RpKMhiX6rsk5wXXnYgbUcOt8kiS31/AjfoTOvCsE+w8nZQLQnzHA==", "dev": true, "dependencies": { - "@vitest/utils": "1.3.1", - "fast-glob": "^3.3.2", - "fflate": "^0.8.1", - "flatted": "^3.2.9", - "pathe": "^1.1.1", - "picocolors": "^1.0.0", - "sirv": "^2.0.4" + "call-bind-apply-helpers": "^1.0.1", + "get-intrinsic": "^1.2.6" }, - "funding": { - "url": "https://opencollective.com/vitest" + "engines": { + "node": ">= 0.4" }, - "peerDependencies": { - "vitest": "1.3.1" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/@vitest/utils": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-1.3.1.tgz", - "integrity": "sha512-d3Waie/299qqRyHTm2DjADeTaNdNSVsnwHPWrs20JMpjh6eiVq7ggggweO8rc4arhf6rRkWuHKwvxGvejUXZZQ==", + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "peer": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase-css": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", + "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/chai": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.5.0.tgz", + "integrity": "sha512-RITGBfijLkBddZvnn8jdqoTypxvqbOLYQkGGxXzeFjVHvudaPw0HNFD9x928/eUwYWd2dPCugVqspGALTZZQKw==", "dev": true, "dependencies": { - "diff-sequences": "^29.6.3", - "estree-walker": "^3.0.3", - "loupe": "^2.3.7", - "pretty-format": "^29.7.0" + "assertion-error": "^1.1.0", + "check-error": "^1.0.3", + "deep-eql": "^4.1.3", + "get-func-name": "^2.0.2", + "loupe": "^2.3.6", + "pathval": "^1.1.1", + "type-detect": "^4.1.0" }, - "funding": { - "url": "https://opencollective.com/vitest" + "engines": { + "node": ">=4" } }, - "node_modules/acorn": { - "version": "8.11.3", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", - "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", - "bin": { - "acorn": "bin/acorn" + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "peer": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" }, "engines": { - "node": ">=0.4.0" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/acorn-jsx": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "node_modules/check-error": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.3.tgz", + "integrity": "sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==", "dev": true, - "peer": true, - "peerDependencies": { - "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + "dependencies": { + "get-func-name": "^2.0.2" + }, + "engines": { + "node": "*" } }, - "node_modules/acorn-walk": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.2.tgz", - "integrity": "sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A==", + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", "dev": true, + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, "engines": { - "node": ">=0.4.0" + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" } }, - "node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "node_modules/chokidar/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", "dev": true, - "peer": true, "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" + "is-glob": "^4.0.1" }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" + "engines": { + "node": ">= 6" } }, - "node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, + "node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", "engines": { - "node": ">=8" + "node": ">=6" } }, - "node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, "dependencies": { - "color-convert": "^2.0.1" + "color-name": "~1.1.4" }, "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "node": ">=7.0.0" } }, - "node_modules/any-promise": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", - "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, - "node_modules/anymatch": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "node_modules/commander": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", "dev": true, - "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - }, "engines": { - "node": ">= 8" + "node": ">= 6" } }, - "node_modules/arg": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", - "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", "dev": true }, - "node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true, - "peer": true + "node_modules/confbox": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.1.8.tgz", + "integrity": "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==", + "dev": true }, - "node_modules/aria-query": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz", - "integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==", + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, "dependencies": { - "dequal": "^2.0.3" + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" } }, - "node_modules/array-buffer-byte-length": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.0.tgz", - "integrity": "sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A==", + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "dev": true, + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/data-view-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.2.tgz", + "integrity": "sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "is-array-buffer": "^3.0.1" + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "node_modules/data-view-byte-length": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.2.tgz", + "integrity": "sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==", "dev": true, + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.2" + }, "engines": { - "node": ">=8" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/inspect-js" } }, - "node_modules/arraybuffer.prototype.slice": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.2.tgz", - "integrity": "sha512-yMBKppFur/fbHu9/6USUe03bZ4knMYiwFBcyiaXB8Go0qNehwX6inYPzK9U0NeQvGxKthcmHcaR8P5MStSRBAw==", + "node_modules/data-view-byte-offset": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.1.tgz", + "integrity": "sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==", "dev": true, "dependencies": { - "array-buffer-byte-length": "^1.0.0", - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "get-intrinsic": "^1.2.1", - "is-array-buffer": "^3.0.2", - "is-shared-array-buffer": "^1.0.2" + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" }, "engines": { "node": ">= 0.4" @@ -2165,20 +2100,52 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/assertion-error": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", - "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", + "node_modules/debug": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", "dev": true, + "dependencies": { + "ms": "^2.1.3" + }, "engines": { - "node": "*" + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } } }, - "node_modules/available-typed-arrays": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", - "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==", + "node_modules/deep-eql": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.4.tgz", + "integrity": "sha512-SUwdGfqdKOwxCPeVYjwSyRpJ7Z+fhpwIAtmCUdZIWZ/YP5R9WAsyuSgpLVDi9bjWoN2LXHNss/dk3urXtdQxGg==", + "dev": true, + "dependencies": { + "type-detect": "^4.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true, + "peer": true + }, + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", "dev": true, + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, "engines": { "node": ">= 0.4" }, @@ -2186,792 +2153,860 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/axobject-query": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-3.2.1.tgz", - "integrity": "sha512-jsyHu61e6N4Vbz/v18DHwWYKK0bSWLqn47eeDSKPB7m8tqMHF9YJ+mhIk2lVteyZrY8tnSj/jHOv4YiTCuCJgg==", + "node_modules/define-properties": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", + "dev": true, "dependencies": { - "dequal": "^2.0.3" + "define-data-property": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "node_modules/didyoumean": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", + "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==", "dev": true }, - "node_modules/binary-extensions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", - "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "node_modules/diff-sequences": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", + "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", "dev": true, "engines": { - "node": ">=8" - } - }, - "node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", "dev": true, "dependencies": { - "fill-range": "^7.0.1" + "path-type": "^4.0.0" }, "engines": { "node": ">=8" } }, - "node_modules/browserslist": { - "version": "4.23.0", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.0.tgz", - "integrity": "sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ==", + "node_modules/dlv": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", + "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", + "dev": true + }, + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "optional": true, "peer": true, "dependencies": { - "caniuse-lite": "^1.0.30001587", - "electron-to-chromium": "^1.4.668", - "node-releases": "^2.0.14", - "update-browserslist-db": "^1.0.13" - }, - "bin": { - "browserslist": "cli.js" + "esutils": "^2.0.2" }, "engines": { - "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + "node": ">=6.0.0" } }, - "node_modules/buffer-crc32": { - "version": "0.2.13", - "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", - "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", "dev": true, + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, "engines": { - "node": "*" + "node": ">= 0.4" } }, - "node_modules/builtin-modules": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.3.0.tgz", - "integrity": "sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==", + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true + }, + "node_modules/emoji-regex": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.4.0.tgz", + "integrity": "sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==", + "dev": true + }, + "node_modules/error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", "dev": true, + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/es-abstract": { + "version": "1.23.9", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.9.tgz", + "integrity": "sha512-py07lI0wjxAC/DcfK1S6G7iANonniZwTISvdPzk9hzeH0IZIshbuuFxLIU96OyF89Yb9hiqWn8M/bY83KY5vzA==", + "dev": true, + "dependencies": { + "array-buffer-byte-length": "^1.0.2", + "arraybuffer.prototype.slice": "^1.0.4", + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "data-view-buffer": "^1.0.2", + "data-view-byte-length": "^1.0.2", + "data-view-byte-offset": "^1.0.1", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "es-set-tostringtag": "^2.1.0", + "es-to-primitive": "^1.3.0", + "function.prototype.name": "^1.1.8", + "get-intrinsic": "^1.2.7", + "get-proto": "^1.0.0", + "get-symbol-description": "^1.1.0", + "globalthis": "^1.0.4", + "gopd": "^1.2.0", + "has-property-descriptors": "^1.0.2", + "has-proto": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "internal-slot": "^1.1.0", + "is-array-buffer": "^3.0.5", + "is-callable": "^1.2.7", + "is-data-view": "^1.0.2", + "is-regex": "^1.2.1", + "is-shared-array-buffer": "^1.0.4", + "is-string": "^1.1.1", + "is-typed-array": "^1.1.15", + "is-weakref": "^1.1.0", + "math-intrinsics": "^1.1.0", + "object-inspect": "^1.13.3", + "object-keys": "^1.1.1", + "object.assign": "^4.1.7", + "own-keys": "^1.0.1", + "regexp.prototype.flags": "^1.5.3", + "safe-array-concat": "^1.1.3", + "safe-push-apply": "^1.0.0", + "safe-regex-test": "^1.1.0", + "set-proto": "^1.0.0", + "string.prototype.trim": "^1.2.10", + "string.prototype.trimend": "^1.0.9", + "string.prototype.trimstart": "^1.0.8", + "typed-array-buffer": "^1.0.3", + "typed-array-byte-length": "^1.0.3", + "typed-array-byte-offset": "^1.0.4", + "typed-array-length": "^1.0.7", + "unbox-primitive": "^1.1.0", + "which-typed-array": "^1.1.18" + }, "engines": { - "node": ">=6" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "dev": true, + "engines": { + "node": ">= 0.4" } }, - "node_modules/cac": { - "version": "6.7.14", - "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", - "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==", + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", "dev": true, "engines": { - "node": ">=8" + "node": ">= 0.4" } }, - "node_modules/call-bind": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.5.tgz", - "integrity": "sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ==", + "node_modules/es-object-atoms": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.0.0.tgz", + "integrity": "sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==", "dev": true, "dependencies": { - "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.1", - "set-function-length": "^1.1.1" + "es-errors": "^1.3.0" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": ">= 0.4" } }, - "node_modules/callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", "dev": true, - "peer": true, + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, "engines": { - "node": ">=6" + "node": ">= 0.4" } }, - "node_modules/camelcase-css": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", - "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==", + "node_modules/es-to-primitive": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.3.0.tgz", + "integrity": "sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==", "dev": true, + "dependencies": { + "is-callable": "^1.2.7", + "is-date-object": "^1.0.5", + "is-symbol": "^1.0.4" + }, "engines": { - "node": ">= 6" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/caniuse-lite": { - "version": "1.0.30001612", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001612.tgz", - "integrity": "sha512-lFgnZ07UhaCcsSZgWW0K5j4e69dK1u/ltrL9lTUiFOwNHs12S3UMIEYgBV0Z6C6hRDev7iRnMzzYmKabYdXF9g==", + "node_modules/esbuild": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.24.2.tgz", + "integrity": "sha512-+9egpBW8I3CD5XPe0n6BfT5fxLzxrlDzqydF3aviG+9ni1lDC/OvMHcxqEFV0+LANZG5R1bFMWfUrjVsdwxJvA==", "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/caniuse-lite" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "optional": true, - "peer": true + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.24.2", + "@esbuild/android-arm": "0.24.2", + "@esbuild/android-arm64": "0.24.2", + "@esbuild/android-x64": "0.24.2", + "@esbuild/darwin-arm64": "0.24.2", + "@esbuild/darwin-x64": "0.24.2", + "@esbuild/freebsd-arm64": "0.24.2", + "@esbuild/freebsd-x64": "0.24.2", + "@esbuild/linux-arm": "0.24.2", + "@esbuild/linux-arm64": "0.24.2", + "@esbuild/linux-ia32": "0.24.2", + "@esbuild/linux-loong64": "0.24.2", + "@esbuild/linux-mips64el": "0.24.2", + "@esbuild/linux-ppc64": "0.24.2", + "@esbuild/linux-riscv64": "0.24.2", + "@esbuild/linux-s390x": "0.24.2", + "@esbuild/linux-x64": "0.24.2", + "@esbuild/netbsd-arm64": "0.24.2", + "@esbuild/netbsd-x64": "0.24.2", + "@esbuild/openbsd-arm64": "0.24.2", + "@esbuild/openbsd-x64": "0.24.2", + "@esbuild/sunos-x64": "0.24.2", + "@esbuild/win32-arm64": "0.24.2", + "@esbuild/win32-ia32": "0.24.2", + "@esbuild/win32-x64": "0.24.2" + } }, - "node_modules/chai": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/chai/-/chai-4.4.1.tgz", - "integrity": "sha512-13sOfMv2+DWduEU+/xbun3LScLoqN17nBeTLUsmDfKdoiC1fr0n9PU4guu4AhRcOVFk/sW8LyZWHuhWtQZiF+g==", + "node_modules/esbuild-svelte": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/esbuild-svelte/-/esbuild-svelte-0.9.0.tgz", + "integrity": "sha512-ebGQYTuM4U1Tfx9HdkNtfBjaxY7t7LirlD1yylpSIkhRW+zLzff1wOK1jhuM7ZCnBVCGpt6sGZqiPb5c99KzJg==", "dev": true, "dependencies": { - "assertion-error": "^1.1.0", - "check-error": "^1.0.3", - "deep-eql": "^4.1.3", - "get-func-name": "^2.0.2", - "loupe": "^2.3.6", - "pathval": "^1.1.1", - "type-detect": "^4.0.8" + "@jridgewell/trace-mapping": "^0.3.19" }, "engines": { - "node": ">=4" + "node": ">=18" + }, + "peerDependencies": { + "esbuild": ">=0.17.0", + "svelte": ">=4.2.1 <6" } }, - "node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", "dev": true, "peer": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, "engines": { "node": ">=10" }, "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/check-error": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.3.tgz", - "integrity": "sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==", + "node_modules/eslint": { + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.1.tgz", + "integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==", + "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", "dev": true, + "peer": true, "dependencies": { - "get-func-name": "^2.0.2" + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.4", + "@eslint/js": "8.57.1", + "@humanwhocodes/config-array": "^0.13.0", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "@ungap/structured-clone": "^1.2.0", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.2.2", + "eslint-visitor-keys": "^3.4.3", + "espree": "^9.6.1", + "esquery": "^1.4.2", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "globals": "^13.19.0", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3", + "strip-ansi": "^6.0.1", + "text-table": "^0.2.0" + }, + "bin": { + "eslint": "bin/eslint.js" }, "engines": { - "node": "*" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, - "node_modules/chokidar": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", - "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "node_modules/eslint-scope": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - ], + "peer": true, "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" }, "engines": { - "node": ">= 8.10.0" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, - "optionalDependencies": { - "fsevents": "~2.3.2" + "funding": { + "url": "https://opencollective.com/eslint" } }, - "node_modules/chokidar/node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", "dev": true, - "dependencies": { - "is-glob": "^4.0.1" - }, "engines": { - "node": ">= 6" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, - "node_modules/code-red": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/code-red/-/code-red-1.0.4.tgz", - "integrity": "sha512-7qJWqItLA8/VPVlKJlFXU+NBlo/qyfs39aJcuMT/2ere32ZqvF5OSxgdM5xOfJJ7O429gg2HM47y8v9P+9wrNw==", + "node_modules/eslint/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "peer": true, "dependencies": { - "@jridgewell/sourcemap-codec": "^1.4.15", - "@types/estree": "^1.0.1", - "acorn": "^8.10.0", - "estree-walker": "^3.0.3", - "periscopic": "^3.1.0" + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" } }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "node_modules/eslint/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, + "peer": true, "dependencies": { - "color-name": "~1.1.4" + "brace-expansion": "^1.1.7" }, "engines": { - "node": ">=7.0.0" + "node": "*" } }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true + "node_modules/esm-env": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/esm-env/-/esm-env-1.2.1.tgz", + "integrity": "sha512-U9JedYYjCnadUlXk7e1Kr+aENQhtUaoaV9+gZm1T8LC/YBAPJx3NSPIAurFOC0U5vrdSevnUJS2/wUVxGwPhng==" }, - "node_modules/commander": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", - "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "node_modules/espree": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", "dev": true, + "peer": true, + "dependencies": { + "acorn": "^8.9.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" + }, "engines": { - "node": ">= 6" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true - }, - "node_modules/convert-source-map": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", - "dev": true - }, - "node_modules/create-require": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", - "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", - "dev": true, - "optional": true, - "peer": true - }, - "node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "node_modules/esquery": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", + "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", "dev": true, + "peer": true, "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" + "estraverse": "^5.1.0" }, "engines": { - "node": ">= 8" + "node": ">=0.10" } }, - "node_modules/css-tree": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.3.1.tgz", - "integrity": "sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==", + "node_modules/esrap": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/esrap/-/esrap-1.3.2.tgz", + "integrity": "sha512-C4PXusxYhFT98GjLSmb20k9PREuUdporer50dhzGuJu9IJXktbMddVCMLAERl5dAHyAi73GWWCE4FVHGP1794g==", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.4.15" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "peer": true, "dependencies": { - "mdn-data": "2.0.30", - "source-map-js": "^1.0.1" + "estraverse": "^5.2.0" }, "engines": { - "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" + "node": ">=4.0" } }, - "node_modules/cssesc": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", - "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", "dev": true, - "bin": { - "cssesc": "bin/cssesc" - }, + "peer": true, "engines": { - "node": ">=4" + "node": ">=4.0" } }, - "node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", "dev": true, "dependencies": { - "ms": "2.1.2" - }, + "@types/estree": "^1.0.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "peer": true, "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } + "node": ">=0.10.0" } }, - "node_modules/deep-eql": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.3.tgz", - "integrity": "sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw==", + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "engines": { + "node": ">=0.8.x" + } + }, + "node_modules/execa": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz", + "integrity": "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==", "dev": true, "dependencies": { - "type-detect": "^4.0.0" + "cross-spawn": "^7.0.3", + "get-stream": "^8.0.1", + "human-signals": "^5.0.0", + "is-stream": "^3.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^5.1.0", + "onetime": "^6.0.0", + "signal-exit": "^4.1.0", + "strip-final-newline": "^3.0.0" }, "engines": { - "node": ">=6" + "node": ">=16.17" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" } }, - "node_modules/deep-is": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "dev": true, "peer": true }, - "node_modules/define-data-property": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.1.tgz", - "integrity": "sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ==", + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", "dev": true, "dependencies": { - "get-intrinsic": "^1.2.1", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.0" + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" }, "engines": { - "node": ">= 0.4" + "node": ">=8.6.0" } }, - "node_modules/define-properties": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", - "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", "dev": true, "dependencies": { - "define-data-property": "^1.0.1", - "has-property-descriptors": "^1.0.0", - "object-keys": "^1.1.1" + "is-glob": "^4.0.1" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">= 6" } }, - "node_modules/dequal": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", - "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", - "engines": { - "node": ">=6" - } + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "peer": true }, - "node_modules/detect-indent": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-6.1.0.tgz", - "integrity": "sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==", + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", "dev": true, - "engines": { - "node": ">=8" + "peer": true + }, + "node_modules/fastq": { + "version": "1.18.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.18.0.tgz", + "integrity": "sha512-QKHXPW0hD8g4UET03SdOdunzSouc9N4AuHdsX8XNcTsuz+yYFILVNIX4l9yHABMhiEI9Db0JTTIpu0wB+Y1QQw==", + "dev": true, + "dependencies": { + "reusify": "^1.0.4" } }, - "node_modules/didyoumean": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", - "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==", + "node_modules/fflate": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz", + "integrity": "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==", "dev": true }, - "node_modules/diff": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "node_modules/file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", "dev": true, - "optional": true, "peer": true, + "dependencies": { + "flat-cache": "^3.0.4" + }, "engines": { - "node": ">=0.3.1" + "node": "^10.12.0 || >=12.0.0" } }, - "node_modules/diff-sequences": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", - "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=8" } }, - "node_modules/dir-glob": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", - "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", "dev": true, + "peer": true, "dependencies": { - "path-type": "^4.0.0" + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" }, "engines": { - "node": ">=8" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/dlv": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", - "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", - "dev": true - }, - "node_modules/doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "node_modules/flat-cache": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", + "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", "dev": true, "peer": true, "dependencies": { - "esutils": "^2.0.2" + "flatted": "^3.2.9", + "keyv": "^4.5.3", + "rimraf": "^3.0.2" }, "engines": { - "node": ">=6.0.0" + "node": "^10.12.0 || >=12.0.0" } }, - "node_modules/eastasianwidth": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", - "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "node_modules/flatted": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.2.tgz", + "integrity": "sha512-AiwGJM8YcNOaobumgtng+6NHuOqC3A7MixFeDafM3X9cIUM+xUXoS5Vfgf+OihAYe20fxqNM9yPBXJzRtZ/4eA==", "dev": true }, - "node_modules/electron-to-chromium": { - "version": "1.4.699", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.699.tgz", - "integrity": "sha512-I7q3BbQi6e4tJJN5CRcyvxhK0iJb34TV8eJQcgh+fR2fQ8miMgZcEInckCo1U9exDHbfz7DLDnFn8oqH/VcRKw==", + "node_modules/for-each": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", + "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", "dev": true, - "optional": true, - "peer": true + "dependencies": { + "is-callable": "^1.1.3" + } }, - "node_modules/emoji-regex": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "node_modules/foreground-child": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz", + "integrity": "sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.0", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", "dev": true }, - "node_modules/error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", "dev": true, - "dependencies": { - "is-arrayish": "^0.2.1" + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, - "node_modules/es-abstract": { - "version": "1.22.3", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.22.3.tgz", - "integrity": "sha512-eiiY8HQeYfYH2Con2berK+To6GrK2RxbPawDkGq4UiCQQfZHb6wX9qQqkbpPqaxQFcl8d9QzZqo0tGE0VcrdwA==", + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", "dev": true, - "dependencies": { - "array-buffer-byte-length": "^1.0.0", - "arraybuffer.prototype.slice": "^1.0.2", - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.5", - "es-set-tostringtag": "^2.0.1", - "es-to-primitive": "^1.2.1", - "function.prototype.name": "^1.1.6", - "get-intrinsic": "^1.2.2", - "get-symbol-description": "^1.0.0", - "globalthis": "^1.0.3", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.0", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3", - "hasown": "^2.0.0", - "internal-slot": "^1.0.5", - "is-array-buffer": "^3.0.2", - "is-callable": "^1.2.7", - "is-negative-zero": "^2.0.2", - "is-regex": "^1.1.4", - "is-shared-array-buffer": "^1.0.2", - "is-string": "^1.0.7", - "is-typed-array": "^1.1.12", - "is-weakref": "^1.0.2", - "object-inspect": "^1.13.1", - "object-keys": "^1.1.1", - "object.assign": "^4.1.4", - "regexp.prototype.flags": "^1.5.1", - "safe-array-concat": "^1.0.1", - "safe-regex-test": "^1.0.0", - "string.prototype.trim": "^1.2.8", - "string.prototype.trimend": "^1.0.7", - "string.prototype.trimstart": "^1.0.7", - "typed-array-buffer": "^1.0.0", - "typed-array-byte-length": "^1.0.0", - "typed-array-byte-offset": "^1.0.0", - "typed-array-length": "^1.0.4", - "unbox-primitive": "^1.0.2", - "which-typed-array": "^1.1.13" - }, - "engines": { - "node": ">= 0.4" - }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/es-set-tostringtag": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.2.tgz", - "integrity": "sha512-BuDyupZt65P9D2D2vA/zqcI3G5xRsklm5N3xCwuiy+/vKy8i0ifdsQP1sLgO4tZDSCaQUSnmC48khknGMV3D2Q==", + "node_modules/function.prototype.name": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.8.tgz", + "integrity": "sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==", "dev": true, "dependencies": { - "get-intrinsic": "^1.2.2", - "has-tostringtag": "^1.0.0", - "hasown": "^2.0.0" + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "functions-have-names": "^1.2.3", + "hasown": "^2.0.2", + "is-callable": "^1.2.7" }, "engines": { "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/es-to-primitive": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", - "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "node_modules/functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", "dev": true, - "dependencies": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/es6-promise": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.3.1.tgz", - "integrity": "sha512-SOp9Phqvqn7jtEUxPWdWfWoLmyt2VaJ6MpvP9Comy1MceMXqE6bxvaTu4iaxpYYPzhny28Lc+M87/c2cPK6lDg==", - "dev": true - }, - "node_modules/esbuild": { - "version": "0.19.11", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.19.11.tgz", - "integrity": "sha512-HJ96Hev2hX/6i5cDVwcqiJBBtuo9+FeIJOtZ9W1kA5M6AMJRHUZlpYZ1/SbEwtO0ioNAW8rUooVpC/WehY2SfA==", + "node_modules/get-func-name": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.2.tgz", + "integrity": "sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==", "dev": true, - "hasInstallScript": true, - "bin": { - "esbuild": "bin/esbuild" - }, "engines": { - "node": ">=12" - }, - "optionalDependencies": { - "@esbuild/aix-ppc64": "0.19.11", - "@esbuild/android-arm": "0.19.11", - "@esbuild/android-arm64": "0.19.11", - "@esbuild/android-x64": "0.19.11", - "@esbuild/darwin-arm64": "0.19.11", - "@esbuild/darwin-x64": "0.19.11", - "@esbuild/freebsd-arm64": "0.19.11", - "@esbuild/freebsd-x64": "0.19.11", - "@esbuild/linux-arm": "0.19.11", - "@esbuild/linux-arm64": "0.19.11", - "@esbuild/linux-ia32": "0.19.11", - "@esbuild/linux-loong64": "0.19.11", - "@esbuild/linux-mips64el": "0.19.11", - "@esbuild/linux-ppc64": "0.19.11", - "@esbuild/linux-riscv64": "0.19.11", - "@esbuild/linux-s390x": "0.19.11", - "@esbuild/linux-x64": "0.19.11", - "@esbuild/netbsd-x64": "0.19.11", - "@esbuild/openbsd-x64": "0.19.11", - "@esbuild/sunos-x64": "0.19.11", - "@esbuild/win32-arm64": "0.19.11", - "@esbuild/win32-ia32": "0.19.11", - "@esbuild/win32-x64": "0.19.11" + "node": "*" } }, - "node_modules/esbuild-svelte": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/esbuild-svelte/-/esbuild-svelte-0.8.0.tgz", - "integrity": "sha512-uKcPf1kl2UGMjrfHChv4dLxGAvCNhf9s72mHo19ZhKP+LrVOuQkOM/g8GE7MiGpoqjpk8UHqL08uLRbSKXhmhw==", + "node_modules/get-intrinsic": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.7.tgz", + "integrity": "sha512-VW6Pxhsrk0KAOqs3WEd0klDiF/+V7gQOpAvY1jVU/LHmaD/kQO4523aiJuikX/QAKYiW6x8Jh+RJej1almdtCA==", "dev": true, "dependencies": { - "@jridgewell/trace-mapping": "^0.3.19" + "call-bind-apply-helpers": "^1.0.1", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "function-bind": "^1.1.2", + "get-proto": "^1.0.0", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" }, "engines": { - "node": ">=14" + "node": ">= 0.4" }, - "peerDependencies": { - "esbuild": ">=0.9.6", - "svelte": ">=3.43.0 <5" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/escalade": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz", - "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==", + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", "dev": true, - "optional": true, - "peer": true, + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, "engines": { - "node": ">=6" + "node": ">= 0.4" } }, - "node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "node_modules/get-stream": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz", + "integrity": "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==", "dev": true, - "peer": true, "engines": { - "node": ">=10" + "node": ">=16" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/eslint": { - "version": "8.56.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.56.0.tgz", - "integrity": "sha512-Go19xM6T9puCOWntie1/P997aXxFsOi37JIHRWI514Hc6ZnaHGKY9xFhrU65RT6CcBEzZoGG1e6Nq+DT04ZtZQ==", + "node_modules/get-symbol-description": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.1.0.tgz", + "integrity": "sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==", "dev": true, - "peer": true, "dependencies": { - "@eslint-community/eslint-utils": "^4.2.0", - "@eslint-community/regexpp": "^4.6.1", - "@eslint/eslintrc": "^2.1.4", - "@eslint/js": "8.56.0", - "@humanwhocodes/config-array": "^0.11.13", - "@humanwhocodes/module-importer": "^1.0.1", - "@nodelib/fs.walk": "^1.2.8", - "@ungap/structured-clone": "^1.2.0", - "ajv": "^6.12.4", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.2", - "debug": "^4.3.2", - "doctrine": "^3.0.0", - "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.2.2", - "eslint-visitor-keys": "^3.4.3", - "espree": "^9.6.1", - "esquery": "^1.4.2", - "esutils": "^2.0.2", - "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^6.0.1", - "find-up": "^5.0.0", - "glob-parent": "^6.0.2", - "globals": "^13.19.0", - "graphemer": "^1.4.0", - "ignore": "^5.2.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "is-path-inside": "^3.0.3", - "js-yaml": "^4.1.0", - "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.4.1", - "lodash.merge": "^4.6.2", - "minimatch": "^3.1.2", - "natural-compare": "^1.4.0", - "optionator": "^0.9.3", - "strip-ansi": "^6.0.1", - "text-table": "^0.2.0" - }, - "bin": { - "eslint": "bin/eslint.js" + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": ">= 0.4" }, "funding": { - "url": "https://opencollective.com/eslint" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/eslint-scope": { - "version": "7.2.2", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", - "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", "dev": true, - "peer": true, "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "*" }, "funding": { - "url": "https://opencollective.com/eslint" + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/eslint-visitor-keys": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", - "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", "dev": true, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "dependencies": { + "is-glob": "^4.0.3" }, - "funding": { - "url": "https://opencollective.com/eslint" + "engines": { + "node": ">=10.13.0" } }, - "node_modules/eslint/node_modules/brace-expansion": { + "node_modules/glob/node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, - "peer": true, "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, - "node_modules/eslint/node_modules/minimatch": { + "node_modules/glob/node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, - "peer": true, "dependencies": { "brace-expansion": "^1.1.7" }, @@ -2979,300 +3014,384 @@ "node": "*" } }, - "node_modules/espree": { - "version": "9.6.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", - "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "node_modules/globals": { + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", "dev": true, "peer": true, "dependencies": { - "acorn": "^8.9.0", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.4.1" + "type-fest": "^0.20.2" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": ">=8" }, "funding": { - "url": "https://opencollective.com/eslint" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/esquery": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", - "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", + "node_modules/globalthis": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", + "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", "dev": true, - "peer": true, "dependencies": { - "estraverse": "^5.1.0" + "define-properties": "^1.2.1", + "gopd": "^1.0.1" }, "engines": { - "node": ">=0.10" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", "dev": true, - "peer": true, "dependencies": { - "estraverse": "^5.2.0" + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" }, "engines": { - "node": ">=4.0" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true + }, + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true + }, + "node_modules/graphology": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/graphology/-/graphology-0.25.4.tgz", + "integrity": "sha512-33g0Ol9nkWdD6ulw687viS8YJQBxqG5LWII6FI6nul0pq6iM2t5EKquOTFDbyTblRB3O9I+7KX4xI8u5ffekAQ==", + "dependencies": { + "events": "^3.3.0", + "obliterator": "^2.0.2" + }, + "peerDependencies": { + "graphology-types": ">=0.24.0" } }, - "node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "node_modules/graphology-types": { + "version": "0.24.8", + "resolved": "https://registry.npmjs.org/graphology-types/-/graphology-types-0.24.8.tgz", + "integrity": "sha512-hDRKYXa8TsoZHjgEaysSRyPdT6uB78Ci8WnjgbStlQysz7xR52PInxNsmnB7IBOM1BhikxkNyCVEFgmPKnpx3Q==", + "peer": true + }, + "node_modules/has-bigints": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz", + "integrity": "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==", "dev": true, - "peer": true, "engines": { - "node": ">=4.0" - } - }, - "node_modules/estree-walker": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", - "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", - "dependencies": { - "@types/estree": "^1.0.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, - "peer": true, "engines": { - "node": ">=0.10.0" + "node": ">=8" } }, - "node_modules/events": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", - "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", - "engines": { - "node": ">=0.8.x" + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "dev": true, + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/execa": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz", - "integrity": "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==", + "node_modules/has-proto": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.2.0.tgz", + "integrity": "sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==", "dev": true, "dependencies": { - "cross-spawn": "^7.0.3", - "get-stream": "^8.0.1", - "human-signals": "^5.0.0", - "is-stream": "^3.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^5.1.0", - "onetime": "^6.0.0", - "signal-exit": "^4.1.0", - "strip-final-newline": "^3.0.0" + "dunder-proto": "^1.0.0" }, "engines": { - "node": ">=16.17" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", "dev": true, - "peer": true + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, - "node_modules/fast-glob": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", - "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", "dev": true, "dependencies": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.4" + "has-symbols": "^1.0.3" }, "engines": { - "node": ">=8.6.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/fast-glob/node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", "dev": true, "dependencies": { - "is-glob": "^4.0.1" + "function-bind": "^1.1.2" }, "engines": { - "node": ">= 6" + "node": ">= 0.4" } }, - "node_modules/fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true, - "peer": true + "node_modules/hosted-git-info": { + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", + "dev": true }, - "node_modules/fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true + }, + "node_modules/human-signals": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz", + "integrity": "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==", "dev": true, - "peer": true + "engines": { + "node": ">=16.17.0" + } }, - "node_modules/fastq": { - "version": "1.16.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.16.0.tgz", - "integrity": "sha512-ifCoaXsDrsdkWTtiNJX5uzHDsrck5TzfKKDcuFFTIrrc/BS076qgEIfoIy1VeZqViznfKiysPYTh/QeHtnIsYA==", + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", "dev": true, - "dependencies": { - "reusify": "^1.0.4" + "engines": { + "node": ">= 4" } }, - "node_modules/fflate": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz", - "integrity": "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==", + "node_modules/immediate": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", + "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==", "dev": true }, - "node_modules/file-entry-cache": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", - "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "node_modules/import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", "dev": true, "peer": true, "dependencies": { - "flat-cache": "^3.0.4" + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" }, "engines": { - "node": "^10.12.0 || >=12.0.0" + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", "dev": true, - "dependencies": { - "to-regex-range": "^5.0.1" - }, + "peer": true, "engines": { - "node": ">=8" + "node": ">=0.8.19" } }, - "node_modules/find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", "dev": true, - "peer": true, "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "node_modules/internal-slot": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz", + "integrity": "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==", + "dev": true, + "dependencies": { + "es-errors": "^1.3.0", + "hasown": "^2.0.2", + "side-channel": "^1.1.0" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">= 0.4" } }, - "node_modules/flat-cache": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", - "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", + "node_modules/is-array-buffer": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz", + "integrity": "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==", "dev": true, - "peer": true, "dependencies": { - "flatted": "^3.2.9", - "keyv": "^4.5.3", - "rimraf": "^3.0.2" + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" }, "engines": { - "node": "^10.12.0 || >=12.0.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/flatted": { - "version": "3.2.9", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.9.tgz", - "integrity": "sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==", + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", "dev": true }, - "node_modules/for-each": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", - "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", + "node_modules/is-async-function": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.0.tgz", + "integrity": "sha512-GExz9MtyhlZyXYLxzlJRj5WUCE661zhDa1Yna52CN57AJsymh+DvXXjyveSioqSRdxvUrdKdvqB1b5cVKsNpWQ==", "dev": true, "dependencies": { - "is-callable": "^1.1.3" + "call-bound": "^1.0.3", + "get-proto": "^1.0.1", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/foreground-child": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz", - "integrity": "sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==", + "node_modules/is-bigint": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.1.0.tgz", + "integrity": "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==", "dev": true, "dependencies": { - "cross-spawn": "^7.0.0", - "signal-exit": "^4.0.1" + "has-bigints": "^1.0.2" }, "engines": { - "node": ">=14" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/isaacs" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "node_modules/is-boolean-object": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.1.tgz", + "integrity": "sha512-l9qO6eFlUETHtuihLcYOaLKByJ1f+N4kthcU9YjHy3N+B3hWv0y/2Nd0mu/7lTFnRQHTrSdXF50HQ3bl5fEnng==", "dev": true, - "hasInstallScript": true, - "optional": true, - "os": [ - "darwin" - ], + "dependencies": { + "call-bound": "^1.0.2", + "has-tostringtag": "^1.0.2" + }, "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/function-bind": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "node_modules/is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", "dev": true, + "engines": { + "node": ">= 0.4" + }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/function.prototype.name": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.6.tgz", - "integrity": "sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==", + "node_modules/is-core-module": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "functions-have-names": "^1.2.3" + "hasown": "^2.0.2" }, "engines": { "node": ">= 0.4" @@ -3281,70 +3400,82 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/functions-have-names": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", - "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", + "node_modules/is-data-view": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.2.tgz", + "integrity": "sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==", "dev": true, + "dependencies": { + "call-bound": "^1.0.2", + "get-intrinsic": "^1.2.6", + "is-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/gensync": { - "version": "1.0.0-beta.2", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", - "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "node_modules/is-date-object": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.1.0.tgz", + "integrity": "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==", "dev": true, - "optional": true, - "peer": true, + "dependencies": { + "call-bound": "^1.0.2", + "has-tostringtag": "^1.0.2" + }, "engines": { - "node": ">=6.9.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/get-func-name": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.2.tgz", - "integrity": "sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==", + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", "dev": true, "engines": { - "node": "*" + "node": ">=0.10.0" } }, - "node_modules/get-intrinsic": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.2.tgz", - "integrity": "sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA==", + "node_modules/is-finalizationregistry": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.1.1.tgz", + "integrity": "sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==", "dev": true, "dependencies": { - "function-bind": "^1.1.2", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3", - "hasown": "^2.0.0" + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/get-stream": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz", - "integrity": "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==", + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", "dev": true, "engines": { - "node": ">=16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=8" } }, - "node_modules/get-symbol-description": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", - "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", + "node_modules/is-generator-function": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.0.tgz", + "integrity": "sha512-nPUB5km40q9e8UfN/Zc24eLlzdSf9OfKByBw9CIdw4H1giPMeA0OIJvbchsCu4npfI2QcMVBsGEBHKZ7wLTWmQ==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.1" + "call-bound": "^1.0.3", + "get-proto": "^1.0.0", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" }, "engines": { "node": ">= 0.4" @@ -3353,83 +3484,83 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", "dev": true, "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" + "is-extglob": "^2.1.1" }, "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": ">=0.10.0" } }, - "node_modules/glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "node_modules/is-map": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", + "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==", "dev": true, - "dependencies": { - "is-glob": "^4.0.3" - }, "engines": { - "node": ">=10.13.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/glob/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "dev": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "engines": { + "node": ">=0.12.0" } }, - "node_modules/glob/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "node_modules/is-number-object": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.1.1.tgz", + "integrity": "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==", "dev": true, "dependencies": { - "brace-expansion": "^1.1.7" + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" }, "engines": { - "node": "*" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/globals": { - "version": "13.24.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", - "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "node_modules/is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", "dev": true, "peer": true, - "dependencies": { - "type-fest": "^0.20.2" - }, "engines": { "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/globalthis": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.3.tgz", - "integrity": "sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==", + "node_modules/is-reference": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-3.0.3.tgz", + "integrity": "sha512-ixkJoqQvAP88E6wLydLGGqCJsrFUnqoH6HnaczB8XmDH1oaWU+xxdptvikTgaEhtZ53Ky6YXiBuUI2WXLMCwjw==", + "dependencies": { + "@types/estree": "^1.0.6" + } + }, + "node_modules/is-regex": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", + "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==", "dev": true, "dependencies": { - "define-properties": "^1.1.3" + "call-bound": "^1.0.2", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" }, "engines": { "node": ">= 0.4" @@ -3438,103 +3569,86 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/globby": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", - "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "node_modules/is-set": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz", + "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==", "dev": true, - "dependencies": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.9", - "ignore": "^5.2.0", - "merge2": "^1.4.1", - "slash": "^3.0.0" - }, "engines": { - "node": ">=10" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/gopd": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", - "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "node_modules/is-shared-array-buffer": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.4.tgz", + "integrity": "sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==", "dev": true, "dependencies": { - "get-intrinsic": "^1.1.3" + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/graceful-fs": { - "version": "4.2.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "dev": true - }, - "node_modules/graphemer": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", - "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", - "dev": true - }, - "node_modules/graphology": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/graphology/-/graphology-0.25.4.tgz", - "integrity": "sha512-33g0Ol9nkWdD6ulw687viS8YJQBxqG5LWII6FI6nul0pq6iM2t5EKquOTFDbyTblRB3O9I+7KX4xI8u5ffekAQ==", - "dependencies": { - "events": "^3.3.0", - "obliterator": "^2.0.2" - }, - "peerDependencies": { - "graphology-types": ">=0.24.0" - } - }, - "node_modules/graphology-types": { - "version": "0.24.7", - "resolved": "https://registry.npmjs.org/graphology-types/-/graphology-types-0.24.7.tgz", - "integrity": "sha512-tdcqOOpwArNjEr0gNQKCXwaNCWnQJrog14nJNQPeemcLnXQUUGrsCWpWkVKt46zLjcS6/KGoayeJfHHyPDlvwA==", - "peer": true - }, - "node_modules/has-bigints": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", - "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", + "node_modules/is-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", + "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", "dev": true, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "node_modules/is-string": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.1.1.tgz", + "integrity": "sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==", "dev": true, + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, "engines": { - "node": ">=8" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/has-property-descriptors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.1.tgz", - "integrity": "sha512-VsX8eaIewvas0xnvinAe9bw4WfIeODpGYikiWYLH+dma0Jw6KHYqWiWfhQlgOVK8D6PvjubK5Uc4P0iIhIcNVg==", + "node_modules/is-symbol": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.1.1.tgz", + "integrity": "sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==", "dev": true, "dependencies": { - "get-intrinsic": "^1.2.2" + "call-bound": "^1.0.2", + "has-symbols": "^1.1.0", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/has-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", - "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==", + "node_modules/is-typed-array": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz", + "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==", "dev": true, + "dependencies": { + "which-typed-array": "^1.1.16" + }, "engines": { "node": ">= 0.4" }, @@ -3542,10 +3656,10 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "node_modules/is-weakmap": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz", + "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==", "dev": true, "engines": { "node": ">= 0.4" @@ -3554,13 +3668,13 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/has-tostringtag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", - "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", + "node_modules/is-weakref": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.1.0.tgz", + "integrity": "sha512-SXM8Nwyys6nT5WP6pltOwKytLV7FqQ4UiibxVmW+EIosHcmCqkkjViTb5SNssDlkCiEYRP1/pdWUKVvZBmsR2Q==", "dev": true, "dependencies": { - "has-symbols": "^1.0.2" + "call-bound": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -3569,2024 +3683,2114 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/hasown": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz", - "integrity": "sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==", + "node_modules/is-weakset": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.4.tgz", + "integrity": "sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==", "dev": true, "dependencies": { - "function-bind": "^1.1.2" + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" }, "engines": { "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/hosted-git-info": { - "version": "2.8.9", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", - "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", + "node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", "dev": true }, - "node_modules/html-escaper": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", - "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "dev": true }, - "node_modules/human-signals": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz", - "integrity": "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==", - "dev": true, - "engines": { - "node": ">=16.17.0" - } - }, - "node_modules/ignore": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.0.tgz", - "integrity": "sha512-g7dmpshy+gD7mh88OC9NwSGTKoc3kyLAZQRU1mt53Aw/vnvfXnbC+F/7F7QoYVKbV+KNvJx8wArewKy1vXMtlg==", + "node_modules/istanbul-lib-coverage": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", + "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", "dev": true, "engines": { - "node": ">= 4" + "node": ">=8" } }, - "node_modules/immediate": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", - "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==", - "dev": true - }, - "node_modules/import-fresh": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "node_modules/istanbul-lib-report": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", + "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", "dev": true, - "peer": true, "dependencies": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - }, - "engines": { - "node": ">=6" + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^4.0.0", + "supports-color": "^7.1.0" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", - "dev": true, - "peer": true, "engines": { - "node": ">=0.8.19" + "node": ">=10" } }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "node_modules/istanbul-lib-source-maps": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-5.0.6.tgz", + "integrity": "sha512-yg2d+Em4KizZC5niWhQaIomgf5WlL4vOOjZ5xGCmF8SnPE/mDWWXgvRExdcpCgh9lLRRa1/fSYp2ymmbJ1pI+A==", "dev": true, "dependencies": { - "once": "^1.3.0", - "wrappy": "1" + "@jridgewell/trace-mapping": "^0.3.23", + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0" + }, + "engines": { + "node": ">=10" } }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true - }, - "node_modules/internal-slot": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.6.tgz", - "integrity": "sha512-Xj6dv+PsbtwyPpEflsejS+oIZxmMlV44zAhG479uYu89MsjcYOhCFnNyKrkJrihbsiasQyY0afoCl/9BLR65bg==", + "node_modules/istanbul-reports": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.7.tgz", + "integrity": "sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==", "dev": true, "dependencies": { - "get-intrinsic": "^1.2.2", - "hasown": "^2.0.0", - "side-channel": "^1.0.4" + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" }, "engines": { - "node": ">= 0.4" + "node": ">=8" } }, - "node_modules/is-array-buffer": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.2.tgz", - "integrity": "sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w==", + "node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.2.0", - "is-typed-array": "^1.1.10" + "@isaacs/cliui": "^8.0.2" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" } }, - "node_modules/is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "node_modules/jiti": { + "version": "1.21.7", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.7.tgz", + "integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==", + "dev": true, + "bin": { + "jiti": "bin/jiti.js" + } + }, + "node_modules/js-tokens": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-9.0.1.tgz", + "integrity": "sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==", "dev": true }, - "node_modules/is-bigint": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", - "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", "dev": true, + "peer": true, "dependencies": { - "has-bigints": "^1.0.1" + "argparse": "^2.0.1" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "bin": { + "js-yaml": "bin/js-yaml.js" } }, - "node_modules/is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", "dev": true, - "dependencies": { - "binary-extensions": "^2.0.0" - }, - "engines": { - "node": ">=8" - } + "peer": true }, - "node_modules/is-boolean-object": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", - "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", + "node_modules/json-parse-better-errors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", + "dev": true + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } + "peer": true }, - "node_modules/is-callable": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", - "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } + "peer": true }, - "node_modules/is-core-module": { - "version": "2.13.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", - "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", "dev": true, + "peer": true, "dependencies": { - "hasown": "^2.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "json-buffer": "3.0.1" } }, - "node_modules/is-date-object": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", - "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", "dev": true, + "peer": true, "dependencies": { - "has-tostringtag": "^1.0.0" + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">= 0.8.0" } }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "node_modules/lie": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/lie/-/lie-3.1.1.tgz", + "integrity": "sha512-RiNhHysUjhrDQntfYSfY4MU24coXXdEOgw9WGcKHNeEwffDYbF//u87M1EWaMGzuFoSbqW0C9C6lEEhDOAswfw==", "dev": true, - "engines": { - "node": ">=0.10.0" + "dependencies": { + "immediate": "~3.0.5" } }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "node_modules/lilconfig": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", + "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", "dev": true, "engines": { - "node": ">=8" + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antonk52" } }, - "node_modules/is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true + }, + "node_modules/load-json-file": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", + "integrity": "sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw==", "dev": true, "dependencies": { - "is-extglob": "^2.1.1" + "graceful-fs": "^4.1.2", + "parse-json": "^4.0.0", + "pify": "^3.0.0", + "strip-bom": "^3.0.0" }, "engines": { - "node": ">=0.10.0" + "node": ">=4" } }, - "node_modules/is-negative-zero": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", - "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==", + "node_modules/local-pkg": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/local-pkg/-/local-pkg-0.5.1.tgz", + "integrity": "sha512-9rrA30MRRP3gBD3HTGnC6cDFpaE1kVDWxWgqWJUN0RvDNAo+Nz/9GxB+nHOH0ifbVFy0hSA1V6vFDvnx54lTEQ==", "dev": true, + "dependencies": { + "mlly": "^1.7.3", + "pkg-types": "^1.2.1" + }, "engines": { - "node": ">= 0.4" + "node": ">=14" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/antfu" } }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "node_modules/localforage": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/localforage/-/localforage-1.10.0.tgz", + "integrity": "sha512-14/H1aX7hzBBmmh7sGPd+AOMkkIrHM3Z1PAyGgZigA1H1p5O5ANnMyWzvpAETtG68/dC4pC0ncy3+PPGzXZHPg==", "dev": true, - "engines": { - "node": ">=0.12.0" + "dependencies": { + "lie": "3.1.1" } }, - "node_modules/is-number-object": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", - "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", + "node_modules/locate-character": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-character/-/locate-character-3.0.0.tgz", + "integrity": "sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA==" + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", "dev": true, + "peer": true, "dependencies": { - "has-tostringtag": "^1.0.0" + "p-locate": "^5.0.0" }, "engines": { - "node": ">= 0.4" + "node": ">=10" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/is-path-inside": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", - "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "dev": true, - "peer": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-reference": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-3.0.2.tgz", - "integrity": "sha512-v3rht/LgVcsdZa3O2Nqs+NMowLOxeOm7Ay9+/ARQ2F+qEoANRcqrjAZKGN0v8ymUetZGgkp26LTnGT7H0Qo9Pg==", - "dependencies": { - "@types/estree": "*" - } + "peer": true }, - "node_modules/is-regex": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", - "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", + "node_modules/loglevel": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.9.2.tgz", + "integrity": "sha512-HgMmCqIJSAKqo68l0rS2AanEWfkxaZ5wNiEFb5ggm08lDs9Xl2KxBlX3PTcaD2chBM1gXAYf491/M2Rv8Jwayg==", "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - }, "engines": { - "node": ">= 0.4" + "node": ">= 0.6.0" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/loglevel" } }, - "node_modules/is-shared-array-buffer": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", - "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", + "node_modules/loupe": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.7.tgz", + "integrity": "sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==", "dev": true, "dependencies": { - "call-bind": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "get-func-name": "^2.0.1" } }, - "node_modules/is-stream": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", - "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", - "dev": true, + "node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true + }, + "node_modules/lucide-svelte": { + "version": "0.360.0", + "resolved": "https://registry.npmjs.org/lucide-svelte/-/lucide-svelte-0.360.0.tgz", + "integrity": "sha512-Rs1opdeQZIQjyhMgqwQUJ3GdwrBOl6Z7HtbomzXN/rNWHqEKlgqsQvNh3kMSPoDHQiREqW1Sj/drYP5fw9J6BA==", + "peerDependencies": { + "svelte": "^3 || ^4 || ^5.0.0-next.42" + } + }, + "node_modules/luxon": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/luxon/-/luxon-3.5.0.tgz", + "integrity": "sha512-rh+Zjr6DNfUYR3bPwJEnuwDdqMbxZW7LOQfUN4B54+Cl+0o5zaU9RJ6bcidfDtC1cWCZXQ+nvX8bf6bAji37QQ==", "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=12" } }, - "node_modules/is-string": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", - "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", + "node_modules/magic-string": { + "version": "0.30.17", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz", + "integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0" + } + }, + "node_modules/magicast": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/magicast/-/magicast-0.3.5.tgz", + "integrity": "sha512-L0WhttDl+2BOsybvEOLK7fW3UA0OQ0IQ2d6Zl2x/a6vVRs3bAY0ECOSHHeL5jD+SbOpOCUEi0y1DgHEn9Qn1AQ==", "dev": true, "dependencies": { - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "@babel/parser": "^7.25.4", + "@babel/types": "^7.25.4", + "source-map-js": "^1.2.0" } }, - "node_modules/is-symbol": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", - "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", + "node_modules/make-dir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", "dev": true, "dependencies": { - "has-symbols": "^1.0.2" + "semver": "^7.5.3" }, "engines": { - "node": ">= 0.4" + "node": ">=10" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/is-typed-array": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.12.tgz", - "integrity": "sha512-Z14TF2JNG8Lss5/HMqt0//T9JeHXttXy5pH/DBU4vi98ozO2btxzq9MwYDZYnKwU8nRsz/+GVFVRDq3DkVuSPg==", + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", "dev": true, - "dependencies": { - "which-typed-array": "^1.1.11" - }, "engines": { "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-weakref": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", - "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", + "node_modules/memorystream": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz", + "integrity": "sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw==", "dev": true, - "dependencies": { - "call-bind": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": ">= 0.10.0" } }, - "node_modules/isarray": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", - "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", - "dev": true - }, - "node_modules/isexe": { + "node_modules/merge-stream": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", "dev": true }, - "node_modules/istanbul-lib-coverage": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", - "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", "dev": true, "engines": { - "node": ">=8" + "node": ">= 8" } }, - "node_modules/istanbul-lib-report": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", - "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", "dev": true, "dependencies": { - "istanbul-lib-coverage": "^3.0.0", - "make-dir": "^4.0.0", - "supports-color": "^7.1.0" + "braces": "^3.0.3", + "picomatch": "^2.3.1" }, "engines": { - "node": ">=10" + "node": ">=8.6" } }, - "node_modules/istanbul-lib-source-maps": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", - "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", + "node_modules/mimic-fn": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", + "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", "dev": true, - "dependencies": { - "debug": "^4.1.1", - "istanbul-lib-coverage": "^3.0.0", - "source-map": "^0.6.1" - }, "engines": { - "node": ">=10" - } - }, - "node_modules/istanbul-reports": { - "version": "3.1.7", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.7.tgz", - "integrity": "sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==", - "dev": true, - "dependencies": { - "html-escaper": "^2.0.0", - "istanbul-lib-report": "^3.0.0" + "node": ">=12" }, - "engines": { - "node": ">=8" + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/jackspeak": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.6.tgz", - "integrity": "sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==", + "node_modules/minimatch": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", "dev": true, "dependencies": { - "@isaacs/cliui": "^8.0.2" + "brace-expansion": "^2.0.1" }, "engines": { - "node": ">=14" + "node": ">=16 || 14 >=14.17" }, "funding": { "url": "https://github.com/sponsors/isaacs" - }, - "optionalDependencies": { - "@pkgjs/parseargs": "^0.11.0" } }, - "node_modules/jiti": { - "version": "1.21.0", - "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.0.tgz", - "integrity": "sha512-gFqAIbuKyyso/3G2qhiO2OM6shY6EPP/R0+mkDbyspxKazh8BXDC5FiFsUjlczgdNz/vfra0da2y+aHrusLG/Q==", + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", "dev": true, - "bin": { - "jiti": "bin/jiti.js" + "engines": { + "node": ">=16 || 14 >=14.17" } }, - "node_modules/js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "node_modules/mlly": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.7.3.tgz", + "integrity": "sha512-xUsx5n/mN0uQf4V548PKQ+YShA4/IW0KI1dZhrNrPCLG+xizETbHTkOa1f8/xut9JRPp8kQuMnz0oqwkTiLo/A==", "dev": true, - "optional": true, - "peer": true + "dependencies": { + "acorn": "^8.14.0", + "pathe": "^1.1.2", + "pkg-types": "^1.2.1", + "ufo": "^1.5.4" + } }, - "node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "node_modules/moment": { + "version": "2.29.4", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz", + "integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/mrmime": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.0.tgz", + "integrity": "sha512-eu38+hdgojoyq63s+yTpN4XMBdt5l8HhMhc4VKLO9KM5caLIBvUm4thi7fFaxyTmCKeNnXZ5pAlBwCUnhA09uw==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "node_modules/mz": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", + "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", "dev": true, - "peer": true, "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" + "any-promise": "^1.0.0", + "object-assign": "^4.0.1", + "thenify-all": "^1.0.0" } }, - "node_modules/jsesc": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "node_modules/nanoid": { + "version": "3.3.8", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz", + "integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==", "dev": true, - "optional": true, - "peer": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], "bin": { - "jsesc": "bin/jsesc" + "nanoid": "bin/nanoid.cjs" }, "engines": { - "node": ">=4" + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, - "node_modules/json-buffer": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", - "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", - "dev": true, - "peer": true - }, - "node_modules/json-parse-better-errors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", - "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", "dev": true }, - "node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true, - "peer": true + "node_modules/nice-try": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", + "dev": true }, - "node_modules/json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "node_modules/normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", "dev": true, - "peer": true + "dependencies": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } }, - "node_modules/json5": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "node_modules/normalize-package-data/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", "dev": true, - "optional": true, - "peer": true, "bin": { - "json5": "lib/cli.js" - }, - "engines": { - "node": ">=6" + "semver": "bin/semver" } }, - "node_modules/jsonc-parser": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.1.tgz", - "integrity": "sha512-AilxAyFOAcK5wA1+LeaySVBrHsGQvUFCDWXKpZjzaL0PqW+xfBOttn8GNtWKFWqneyMZj41MWF9Kl6iPWLwgOA==", - "dev": true - }, - "node_modules/keyv": { - "version": "4.5.4", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", - "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", "dev": true, - "peer": true, - "dependencies": { - "json-buffer": "3.0.1" + "engines": { + "node": ">=0.10.0" } }, - "node_modules/levn": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "node_modules/npm-run-all": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/npm-run-all/-/npm-run-all-4.1.5.tgz", + "integrity": "sha512-Oo82gJDAVcaMdi3nuoKFavkIHBRVqQ1qvMb+9LHk/cF4P6B2m8aP04hGf7oL6wZ9BuGwX1onlLhpuoofSyoQDQ==", "dev": true, - "peer": true, "dependencies": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" + "ansi-styles": "^3.2.1", + "chalk": "^2.4.1", + "cross-spawn": "^6.0.5", + "memorystream": "^0.3.1", + "minimatch": "^3.0.4", + "pidtree": "^0.3.0", + "read-pkg": "^3.0.0", + "shell-quote": "^1.6.1", + "string.prototype.padend": "^3.0.0" + }, + "bin": { + "npm-run-all": "bin/npm-run-all/index.js", + "run-p": "bin/run-p/index.js", + "run-s": "bin/run-s/index.js" }, "engines": { - "node": ">= 0.8.0" + "node": ">= 4" } }, - "node_modules/lie": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/lie/-/lie-3.1.1.tgz", - "integrity": "sha512-RiNhHysUjhrDQntfYSfY4MU24coXXdEOgw9WGcKHNeEwffDYbF//u87M1EWaMGzuFoSbqW0C9C6lEEhDOAswfw==", + "node_modules/npm-run-all/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", "dev": true, "dependencies": { - "immediate": "~3.0.5" + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" } }, - "node_modules/lilconfig": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz", - "integrity": "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==", + "node_modules/npm-run-all/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, - "engines": { - "node": ">=10" + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" } }, - "node_modules/lines-and-columns": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", - "dev": true - }, - "node_modules/load-json-file": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", - "integrity": "sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw==", + "node_modules/npm-run-all/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", "dev": true, "dependencies": { - "graceful-fs": "^4.1.2", - "parse-json": "^4.0.0", - "pify": "^3.0.0", - "strip-bom": "^3.0.0" + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" }, "engines": { "node": ">=4" } }, - "node_modules/load-json-file/node_modules/pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", + "node_modules/npm-run-all/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", "dev": true, - "engines": { - "node": ">=4" + "dependencies": { + "color-name": "1.1.3" } }, - "node_modules/local-pkg": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/local-pkg/-/local-pkg-0.5.0.tgz", - "integrity": "sha512-ok6z3qlYyCDS4ZEU27HaU6x/xZa9Whf8jD4ptH5UZTQYZVYeb9bnZ3ojVhiJNLiXK1Hfc0GNbLXcmZ5plLDDBg==", + "node_modules/npm-run-all/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "node_modules/npm-run-all/node_modules/cross-spawn": { + "version": "6.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.6.tgz", + "integrity": "sha512-VqCUuhcd1iB+dsv8gxPttb5iZh/D0iubSP21g36KXdEuf6I5JiioesUVjpCdHV9MZRUfVFlvwtIUyPfxo5trtw==", "dev": true, "dependencies": { - "mlly": "^1.4.2", - "pkg-types": "^1.0.3" + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" }, "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/antfu" + "node": ">=4.8" } }, - "node_modules/localforage": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/localforage/-/localforage-1.10.0.tgz", - "integrity": "sha512-14/H1aX7hzBBmmh7sGPd+AOMkkIrHM3Z1PAyGgZigA1H1p5O5ANnMyWzvpAETtG68/dC4pC0ncy3+PPGzXZHPg==", + "node_modules/npm-run-all/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", "dev": true, - "dependencies": { - "lie": "3.1.1" + "engines": { + "node": ">=0.8.0" } }, - "node_modules/locate-character": { + "node_modules/npm-run-all/node_modules/has-flag": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-character/-/locate-character-3.0.0.tgz", - "integrity": "sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA==" + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "engines": { + "node": ">=4" + } }, - "node_modules/locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "node_modules/npm-run-all/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, - "peer": true, "dependencies": { - "p-locate": "^5.0.0" + "brace-expansion": "^1.1.7" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": "*" } }, - "node_modules/lodash.merge": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true, - "peer": true - }, - "node_modules/loglevel": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.9.1.tgz", - "integrity": "sha512-hP3I3kCrDIMuRwAwHltphhDM1r8i55H33GgqjXbrisuJhF4kRhW1dNuxsRklp4bXl8DSdLaNLuiL4A/LWRfxvg==", + "node_modules/npm-run-all/node_modules/path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==", "dev": true, "engines": { - "node": ">= 0.6.0" - }, - "funding": { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/loglevel" + "node": ">=4" } }, - "node_modules/loupe": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.7.tgz", - "integrity": "sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==", + "node_modules/npm-run-all/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", "dev": true, - "dependencies": { - "get-func-name": "^2.0.1" + "bin": { + "semver": "bin/semver" } }, - "node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "node_modules/npm-run-all/node_modules/shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==", "dev": true, "dependencies": { - "yallist": "^4.0.0" + "shebang-regex": "^1.0.0" }, "engines": { - "node": ">=10" - } - }, - "node_modules/lucide-svelte": { - "version": "0.360.0", - "resolved": "https://registry.npmjs.org/lucide-svelte/-/lucide-svelte-0.360.0.tgz", - "integrity": "sha512-Rs1opdeQZIQjyhMgqwQUJ3GdwrBOl6Z7HtbomzXN/rNWHqEKlgqsQvNh3kMSPoDHQiREqW1Sj/drYP5fw9J6BA==", - "peerDependencies": { - "svelte": "^3 || ^4 || ^5.0.0-next.42" + "node": ">=0.10.0" } }, - "node_modules/luxon": { - "version": "3.4.4", - "resolved": "https://registry.npmjs.org/luxon/-/luxon-3.4.4.tgz", - "integrity": "sha512-zobTr7akeGHnv7eBOXcRgMeCP6+uyYsczwmeRCauvpvaAltgNyTbLH/+VaEAPUeWBT+1GuNmz4wC/6jtQzbbVA==", + "node_modules/npm-run-all/node_modules/shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==", + "dev": true, "engines": { - "node": ">=12" + "node": ">=0.10.0" } }, - "node_modules/magic-string": { - "version": "0.30.5", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.5.tgz", - "integrity": "sha512-7xlpfBaQaP/T6Vh8MO/EqXSW5En6INHEvEXQiuff7Gku0PWjU3uf6w/j9o7O+SpB5fOAkrI5HeoNgwjEO0pFsA==", + "node_modules/npm-run-all/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, "dependencies": { - "@jridgewell/sourcemap-codec": "^1.4.15" + "has-flag": "^3.0.0" }, "engines": { - "node": ">=12" + "node": ">=4" } }, - "node_modules/magicast": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/magicast/-/magicast-0.3.3.tgz", - "integrity": "sha512-ZbrP1Qxnpoes8sz47AM0z08U+jW6TyRgZzcWy3Ma3vDhJttwMwAFDMMQFobwdBxByBD46JYmxRzeF7w2+wJEuw==", + "node_modules/npm-run-all/node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", "dev": true, "dependencies": { - "@babel/parser": "^7.23.6", - "@babel/types": "^7.23.6", - "source-map-js": "^1.0.2" + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" } }, - "node_modules/make-dir": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", - "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", + "node_modules/npm-run-path": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.3.0.tgz", + "integrity": "sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==", "dev": true, "dependencies": { - "semver": "^7.5.3" + "path-key": "^4.0.0" }, "engines": { - "node": ">=10" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/make-error": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", - "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", - "dev": true, - "optional": true, - "peer": true - }, - "node_modules/mdn-data": { - "version": "2.0.30", - "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz", - "integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==" - }, - "node_modules/memorystream": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz", - "integrity": "sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw==", + "node_modules/npm-run-path/node_modules/path-key": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", + "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", "dev": true, "engines": { - "node": ">= 0.10.0" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/merge-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", - "dev": true - }, - "node_modules/merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", "dev": true, "engines": { - "node": ">= 8" + "node": ">=0.10.0" } }, - "node_modules/micromatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "node_modules/object-hash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", + "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", "dev": true, - "dependencies": { - "braces": "^3.0.2", - "picomatch": "^2.3.1" - }, "engines": { - "node": ">=8.6" + "node": ">= 6" } }, - "node_modules/mimic-fn": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", - "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", + "node_modules/object-inspect": { + "version": "1.13.3", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.3.tgz", + "integrity": "sha512-kDCGIbxkDSXE3euJZZXzc6to7fCrKHNI/hSRQnRuQ+BWjFNzZwiFF8fj/6o2t2G9/jTj8PSIYTfCLelLZEeRpA==", "dev": true, "engines": { - "node": ">=12" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/min-indent": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", - "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", "dev": true, "engines": { - "node": ">=4" + "node": ">= 0.4" } }, - "node_modules/minimatch": { - "version": "9.0.3", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", - "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "node_modules/object.assign": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.7.tgz", + "integrity": "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==", "dev": true, "dependencies": { - "brace-expansion": "^2.0.1" + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0", + "has-symbols": "^1.1.0", + "object-keys": "^1.1.1" }, "engines": { - "node": ">=16 || 14 >=14.17" + "node": ">= 0.4" }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/minimist": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "dev": true, "funding": { "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/minipass": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.4.tgz", - "integrity": "sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==", - "dev": true, - "engines": { - "node": ">=16 || 14 >=14.17" - } + "node_modules/obliterator": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/obliterator/-/obliterator-2.0.5.tgz", + "integrity": "sha512-42CPE9AhahZRsMNslczq0ctAEtqk8Eka26QofnqC346BZdHDySk3LWka23LI7ULIw11NmltpiLagIq8gBozxTw==" }, - "node_modules/mkdirp": { - "version": "0.5.6", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", - "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "node_modules/obsidian": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/obsidian/-/obsidian-1.7.2.tgz", + "integrity": "sha512-k9hN9brdknJC+afKr5FQzDRuEFGDKbDjfCazJwpgibwCAoZNYHYV8p/s3mM8I6AsnKrPKNXf8xGuMZ4enWelZQ==", "dev": true, "dependencies": { - "minimist": "^1.2.6" + "@types/codemirror": "5.60.8", + "moment": "2.29.4" }, - "bin": { - "mkdirp": "bin/cmd.js" + "peerDependencies": { + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.0.0" } }, - "node_modules/mlly": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.6.1.tgz", - "integrity": "sha512-vLgaHvaeunuOXHSmEbZ9izxPx3USsk8KCQ8iC+aTlp5sKRSoZvwhHh5L9VbKSaVC6sJDqbyohIS76E2VmHIPAA==", + "node_modules/obsidian-calendar-ui": { + "version": "0.3.12", + "resolved": "https://registry.npmjs.org/obsidian-calendar-ui/-/obsidian-calendar-ui-0.3.12.tgz", + "integrity": "sha512-hdoRqCPnukfRgCARgArXaqMQZ+Iai0eY7f0ZsFHHfywpv4gKg3Tx5p47UsLvRO5DD+4knlbrL7Gel57MkfcLTw==", "dev": true, "dependencies": { - "acorn": "^8.11.3", - "pathe": "^1.1.2", - "pkg-types": "^1.0.3", - "ufo": "^1.3.2" - } - }, - "node_modules/moment": { - "version": "2.29.4", - "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz", - "integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==", - "dev": true, - "engines": { - "node": "*" + "obsidian-daily-notes-interface": "0.8.4", + "svelte": "3.35.0", + "tslib": "2.1.0" } }, - "node_modules/mrmime": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.0.tgz", - "integrity": "sha512-eu38+hdgojoyq63s+yTpN4XMBdt5l8HhMhc4VKLO9KM5caLIBvUm4thi7fFaxyTmCKeNnXZ5pAlBwCUnhA09uw==", + "node_modules/obsidian-calendar-ui/node_modules/svelte": { + "version": "3.35.0", + "resolved": "https://registry.npmjs.org/svelte/-/svelte-3.35.0.tgz", + "integrity": "sha512-gknlZkR2sXheu/X+B7dDImwANVvK1R0QGQLd8CNIfxxGPeXBmePnxfzb6fWwTQRsYQG7lYkZXvpXJvxvpsoB7g==", "dev": true, "engines": { - "node": ">=10" + "node": ">= 8" } }, - "node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "node_modules/obsidian-calendar-ui/node_modules/tslib": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.1.0.tgz", + "integrity": "sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A==", "dev": true }, - "node_modules/mz": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", - "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", + "node_modules/obsidian-daily-notes-interface": { + "version": "0.8.4", + "resolved": "https://registry.npmjs.org/obsidian-daily-notes-interface/-/obsidian-daily-notes-interface-0.8.4.tgz", + "integrity": "sha512-REKQtAuIOKDbvNH/th1C1gWmJWCP5tRn9T/mfZGZt4Zncgko7McXK0aSKFtEInipvgbZJ2nScivvyLdiWluSMw==", "dev": true, "dependencies": { - "any-promise": "^1.0.0", - "object-assign": "^4.0.1", - "thenify-all": "^1.0.0" + "obsidian": "github:obsidianmd/obsidian-api#master", + "tslib": "2.1.0" + }, + "bin": { + "obsidian-daily-notes-interface": "dist/main.js" } }, - "node_modules/nanoid": { - "version": "3.3.7", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", - "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", + "node_modules/obsidian-daily-notes-interface/node_modules/obsidian": { + "version": "1.7.2", + "resolved": "git+ssh://git@github.com/obsidianmd/obsidian-api.git#23947b58d372ea02225324308e31d36b4aa95869", "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "bin": { - "nanoid": "bin/nanoid.cjs" + "license": "MIT", + "dependencies": { + "@types/codemirror": "5.60.8", + "moment": "2.29.4" }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + "peerDependencies": { + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.0.0" } }, - "node_modules/natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", - "dev": true - }, - "node_modules/nice-try": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", - "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", + "node_modules/obsidian-daily-notes-interface/node_modules/tslib": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.1.0.tgz", + "integrity": "sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A==", "dev": true }, - "node_modules/node-releases": { - "version": "2.0.14", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz", - "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==", - "dev": true, - "optional": true, - "peer": true - }, - "node_modules/normalize-package-data": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", - "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", - "dev": true, - "dependencies": { - "hosted-git-info": "^2.1.4", - "resolve": "^1.10.0", - "semver": "2 || 3 || 4 || 5", - "validate-npm-package-license": "^3.0.1" - } - }, - "node_modules/normalize-package-data/node_modules/semver": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "node_modules/obsidian-dataview": { + "version": "0.5.67", + "resolved": "https://registry.npmjs.org/obsidian-dataview/-/obsidian-dataview-0.5.67.tgz", + "integrity": "sha512-nLQrjvZ6Ny5s6mCfi+rv0TsdYkKTV4YfDqyLNixxNkyLCqgE9AXKJlJNnkv3Ic1brGOw2m/0SgtdWykKzobwMQ==", "dev": true, - "bin": { - "semver": "bin/semver" + "dependencies": { + "@codemirror/language": "git+https://github.com/lishid/cm-language.git", + "@codemirror/state": "^6.0.1", + "@codemirror/view": "^6.0.1", + "emoji-regex": "^10.0.0", + "localforage": "^1.10.0", + "luxon": "^3.2.0", + "obsidian-calendar-ui": "^0.3.12", + "papaparse": "^5.3.1", + "parsimmon": "^1.18.0", + "preact": "^10.6.5" } }, - "node_modules/normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", "dev": true, - "engines": { - "node": ">=0.10.0" + "dependencies": { + "wrappy": "1" } }, - "node_modules/npm-run-all": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/npm-run-all/-/npm-run-all-4.1.5.tgz", - "integrity": "sha512-Oo82gJDAVcaMdi3nuoKFavkIHBRVqQ1qvMb+9LHk/cF4P6B2m8aP04hGf7oL6wZ9BuGwX1onlLhpuoofSyoQDQ==", + "node_modules/onetime": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", + "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", "dev": true, "dependencies": { - "ansi-styles": "^3.2.1", - "chalk": "^2.4.1", - "cross-spawn": "^6.0.5", - "memorystream": "^0.3.1", - "minimatch": "^3.0.4", - "pidtree": "^0.3.0", - "read-pkg": "^3.0.0", - "shell-quote": "^1.6.1", - "string.prototype.padend": "^3.0.0" - }, - "bin": { - "npm-run-all": "bin/npm-run-all/index.js", - "run-p": "bin/run-p/index.js", - "run-s": "bin/run-s/index.js" + "mimic-fn": "^4.0.0" }, "engines": { - "node": ">= 4" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/npm-run-all/node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", "dev": true, + "peer": true, "dependencies": { - "color-convert": "^1.9.0" + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" }, "engines": { - "node": ">=4" + "node": ">= 0.8.0" } }, - "node_modules/npm-run-all/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "node_modules/own-keys": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/own-keys/-/own-keys-1.0.1.tgz", + "integrity": "sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==", "dev": true, "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "get-intrinsic": "^1.2.6", + "object-keys": "^1.1.1", + "safe-push-apply": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/npm-run-all/node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", "dev": true, + "peer": true, "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" + "yocto-queue": "^0.1.0" }, "engines": { - "node": ">=4" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/npm-run-all/node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", "dev": true, + "peer": true, "dependencies": { - "color-name": "1.1.3" + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/npm-run-all/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", "dev": true }, - "node_modules/npm-run-all/node_modules/cross-spawn": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", - "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "node_modules/papaparse": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/papaparse/-/papaparse-5.4.1.tgz", + "integrity": "sha512-HipMsgJkZu8br23pW15uvo6sib6wne/4woLZPlFf3rpDyMe9ywEXUsuD7+6K9PRkJlVT51j/sCOYDKGGS3ZJrw==", + "dev": true + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", "dev": true, + "peer": true, "dependencies": { - "nice-try": "^1.0.4", - "path-key": "^2.0.1", - "semver": "^5.5.0", - "shebang-command": "^1.2.0", - "which": "^1.2.9" + "callsites": "^3.0.0" }, "engines": { - "node": ">=4.8" + "node": ">=6" } }, - "node_modules/npm-run-all/node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "node_modules/parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==", "dev": true, + "dependencies": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + }, "engines": { - "node": ">=0.8.0" + "node": ">=4" } }, - "node_modules/npm-run-all/node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "node_modules/parsimmon": { + "version": "1.18.1", + "resolved": "https://registry.npmjs.org/parsimmon/-/parsimmon-1.18.1.tgz", + "integrity": "sha512-u7p959wLfGAhJpSDJVYXoyMCXWYwHia78HhRBWqk7AIbxdmlrfdp5wX0l3xv/iTSH5HvhN9K7o26hwwpgS5Nmw==", + "dev": true + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", "dev": true, + "peer": true, "engines": { - "node": ">=4" + "node": ">=8" } }, - "node_modules/npm-run-all/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, "engines": { - "node": "*" + "node": ">=0.10.0" } }, - "node_modules/npm-run-all/node_modules/path-key": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==", + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", "dev": true, "engines": { - "node": ">=4" + "node": ">=8" } }, - "node_modules/npm-run-all/node_modules/semver": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", - "dev": true, - "bin": { - "semver": "bin/semver" - } + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true }, - "node_modules/npm-run-all/node_modules/shebang-command": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", - "integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==", + "node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", "dev": true, "dependencies": { - "shebang-regex": "^1.0.0" + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" }, "engines": { - "node": ">=0.10.0" + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/npm-run-all/node_modules/shebang-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", - "integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==", + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", "dev": true, "engines": { - "node": ">=0.10.0" + "node": ">=8" } }, - "node_modules/npm-run-all/node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } + "node_modules/pathe": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.2.tgz", + "integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==", + "dev": true }, - "node_modules/npm-run-all/node_modules/which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", - "dev": true, - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "which": "bin/which" + "node_modules/pathval": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", + "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", + "dev": true, + "engines": { + "node": "*" } }, - "node_modules/npm-run-path": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.3.0.tgz", - "integrity": "sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==", + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "dev": true, - "dependencies": { - "path-key": "^4.0.0" - }, "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + "node": ">=8.6" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/npm-run-path/node_modules/path-key": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", - "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", + "node_modules/pidtree": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.3.1.tgz", + "integrity": "sha512-qQbW94hLHEqCg7nhby4yRC7G2+jYHY4Rguc2bjw7Uug4GIJuu1tvf2uHaZv5Q8zdt+WKJ6qK1FOI6amaWUo5FA==", "dev": true, - "engines": { - "node": ">=12" + "bin": { + "pidtree": "bin/pidtree.js" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "engines": { + "node": ">=0.10" } }, - "node_modules/object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "node_modules/pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", "dev": true, "engines": { - "node": ">=0.10.0" + "node": ">=4" } }, - "node_modules/object-hash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", - "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", + "node_modules/pirates": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz", + "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==", "dev": true, "engines": { "node": ">= 6" } }, - "node_modules/object-inspect": { - "version": "1.13.1", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz", - "integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==", + "node_modules/pkg-types": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.3.0.tgz", + "integrity": "sha512-kS7yWjVFCkIw9hqdJBoMxDdzEngmkr5FXeWZZfQ6GoYacjVnsW6l2CcYW/0ThD0vF4LPJgVYnrg4d0uuhwYQbg==", "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "dependencies": { + "confbox": "^0.1.8", + "mlly": "^1.7.3", + "pathe": "^1.1.2" } }, - "node_modules/object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "node_modules/possible-typed-array-names": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz", + "integrity": "sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==", "dev": true, "engines": { "node": ">= 0.4" } }, - "node_modules/object.assign": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.5.tgz", - "integrity": "sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==", + "node_modules/postcss": { + "version": "8.4.49", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.49.tgz", + "integrity": "sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA==", "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], "dependencies": { - "call-bind": "^1.0.5", - "define-properties": "^1.2.1", - "has-symbols": "^1.0.3", - "object-keys": "^1.1.1" + "nanoid": "^3.3.7", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": "^10 || ^12 || >=14" } }, - "node_modules/obliterator": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/obliterator/-/obliterator-2.0.4.tgz", - "integrity": "sha512-lgHwxlxV1qIg1Eap7LgIeoBWIMFibOjbrYPIPJZcI1mmGAI2m3lNYpK12Y+GBdPQ0U1hRwSord7GIaawz962qQ==" - }, - "node_modules/obsidian": { - "version": "1.5.7", - "resolved": "https://registry.npmjs.org/obsidian/-/obsidian-1.5.7.tgz", - "integrity": "sha512-DNcvQJ6TvMflHZqWfO9cLGbOUbKTy2KBi6B6vjo5RG8XsftKZZq1zS/OQFhII2BnXK/DWan/lUcb2JYxfM3p5A==", + "node_modules/postcss-import": { + "version": "15.1.0", + "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz", + "integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==", "dev": true, "dependencies": { - "@types/codemirror": "5.60.8", - "moment": "2.29.4" + "postcss-value-parser": "^4.0.0", + "read-cache": "^1.0.0", + "resolve": "^1.1.7" + }, + "engines": { + "node": ">=14.0.0" }, "peerDependencies": { - "@codemirror/state": "^6.0.0", - "@codemirror/view": "^6.0.0" + "postcss": "^8.0.0" } }, - "node_modules/obsidian-calendar-ui": { - "version": "0.3.12", - "resolved": "https://registry.npmjs.org/obsidian-calendar-ui/-/obsidian-calendar-ui-0.3.12.tgz", - "integrity": "sha512-hdoRqCPnukfRgCARgArXaqMQZ+Iai0eY7f0ZsFHHfywpv4gKg3Tx5p47UsLvRO5DD+4knlbrL7Gel57MkfcLTw==", + "node_modules/postcss-js": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.1.tgz", + "integrity": "sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==", "dev": true, "dependencies": { - "obsidian-daily-notes-interface": "0.8.4", - "svelte": "3.35.0", - "tslib": "2.1.0" + "camelcase-css": "^2.0.1" + }, + "engines": { + "node": "^12 || ^14 || >= 16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + "peerDependencies": { + "postcss": "^8.4.21" } }, - "node_modules/obsidian-calendar-ui/node_modules/svelte": { - "version": "3.35.0", - "resolved": "https://registry.npmjs.org/svelte/-/svelte-3.35.0.tgz", - "integrity": "sha512-gknlZkR2sXheu/X+B7dDImwANVvK1R0QGQLd8CNIfxxGPeXBmePnxfzb6fWwTQRsYQG7lYkZXvpXJvxvpsoB7g==", + "node_modules/postcss-load-config": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.2.tgz", + "integrity": "sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==", "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "lilconfig": "^3.0.0", + "yaml": "^2.3.4" + }, "engines": { - "node": ">= 8" + "node": ">= 14" + }, + "peerDependencies": { + "postcss": ">=8.0.9", + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "postcss": { + "optional": true + }, + "ts-node": { + "optional": true + } } }, - "node_modules/obsidian-calendar-ui/node_modules/tslib": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.1.0.tgz", - "integrity": "sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A==", - "dev": true - }, - "node_modules/obsidian-daily-notes-interface": { - "version": "0.8.4", - "resolved": "https://registry.npmjs.org/obsidian-daily-notes-interface/-/obsidian-daily-notes-interface-0.8.4.tgz", - "integrity": "sha512-REKQtAuIOKDbvNH/th1C1gWmJWCP5tRn9T/mfZGZt4Zncgko7McXK0aSKFtEInipvgbZJ2nScivvyLdiWluSMw==", + "node_modules/postcss-nested": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.2.0.tgz", + "integrity": "sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==", "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], "dependencies": { - "obsidian": "github:obsidianmd/obsidian-api#master", - "tslib": "2.1.0" + "postcss-selector-parser": "^6.1.1" }, - "bin": { - "obsidian-daily-notes-interface": "dist/main.js" + "engines": { + "node": ">=12.0" + }, + "peerDependencies": { + "postcss": "^8.2.14" } }, - "node_modules/obsidian-daily-notes-interface/node_modules/obsidian": { - "version": "1.5.7", - "resolved": "git+ssh://git@github.com/obsidianmd/obsidian-api.git#8b2eda0f24285636c8aa116972643e5233a23dc1", + "node_modules/postcss-selector-parser": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", + "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", "dev": true, - "license": "MIT", "dependencies": { - "@types/codemirror": "5.60.8", - "moment": "2.29.4" + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" }, - "peerDependencies": { - "@codemirror/state": "^6.0.0", - "@codemirror/view": "^6.0.0" + "engines": { + "node": ">=4" } }, - "node_modules/obsidian-daily-notes-interface/node_modules/tslib": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.1.0.tgz", - "integrity": "sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A==", + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", "dev": true }, - "node_modules/obsidian-dataview": { - "version": "0.5.64", - "resolved": "https://registry.npmjs.org/obsidian-dataview/-/obsidian-dataview-0.5.64.tgz", - "integrity": "sha512-byp1bpsdchG3JWngfGb6jqe53gz6L9ou8LcbiBdMGKg2T2Po2ZtJWRoyX69f3CY7/SPZnyumveRaZ72vCDnyRA==", + "node_modules/preact": { + "version": "10.25.4", + "resolved": "https://registry.npmjs.org/preact/-/preact-10.25.4.tgz", + "integrity": "sha512-jLdZDb+Q+odkHJ+MpW/9U5cODzqnB+fy2EiHSZES7ldV5LK7yjlVzTp7R8Xy6W6y75kfK8iWYtFVH7lvjwrCMA==", "dev": true, - "dependencies": { - "@codemirror/language": "git+https://github.com/lishid/cm-language.git", - "@codemirror/state": "^6.0.1", - "@codemirror/view": "^6.0.1", - "emoji-regex": "^10.0.0", - "localforage": "^1.10.0", - "luxon": "^3.2.0", - "obsidian-calendar-ui": "^0.3.12", - "papaparse": "^5.3.1", - "parsimmon": "^1.18.0", - "preact": "^10.6.5" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/preact" } }, - "node_modules/obsidian-dataview/node_modules/emoji-regex": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.3.0.tgz", - "integrity": "sha512-QpLs9D9v9kArv4lfDEgg1X/gN5XLnf/A6l9cs8SPZLRZR3ZkY9+kwIQTxm+fsSej5UMYGE8fdoaZVIBlqG0XTw==", - "dev": true - }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", "dev": true, - "dependencies": { - "wrappy": "1" + "peer": true, + "engines": { + "node": ">= 0.8.0" } }, - "node_modules/onetime": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", - "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", + "node_modules/prettier": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.4.2.tgz", + "integrity": "sha512-e9MewbtFo+Fevyuxn/4rrcDAaq0IYxPGLvObpQjiZBMAzB9IGmzlnG9RZy3FFas+eBMu2vA0CszMeduow5dIuQ==", "dev": true, - "dependencies": { - "mimic-fn": "^4.0.0" + "bin": { + "prettier": "bin/prettier.cjs" }, "engines": { - "node": ">=12" + "node": ">=14" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/prettier/prettier?sponsor=1" } }, - "node_modules/optionator": { - "version": "0.9.3", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", - "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", + "node_modules/prettier-plugin-svelte": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/prettier-plugin-svelte/-/prettier-plugin-svelte-3.3.2.tgz", + "integrity": "sha512-kRPjH8wSj2iu+dO+XaUv4vD8qr5mdDmlak3IT/7AOgGIMRG86z/EHOLauFcClKEnOUf4A4nOA7sre5KrJD4Raw==", "dev": true, - "peer": true, - "dependencies": { - "@aashutoshrathi/word-wrap": "^1.2.3", - "deep-is": "^0.1.3", - "fast-levenshtein": "^2.0.6", - "levn": "^0.4.1", - "prelude-ls": "^1.2.1", - "type-check": "^0.4.0" - }, - "engines": { - "node": ">= 0.8.0" + "peerDependencies": { + "prettier": "^3.0.0", + "svelte": "^3.2.0 || ^4.0.0-next.0 || ^5.0.0-next.0" } }, - "node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "node_modules/prettier-plugin-tailwindcss": { + "version": "0.5.14", + "resolved": "https://registry.npmjs.org/prettier-plugin-tailwindcss/-/prettier-plugin-tailwindcss-0.5.14.tgz", + "integrity": "sha512-Puaz+wPUAhFp8Lo9HuciYKM2Y2XExESjeT+9NQoVFXZsPPnc9VYss2SpxdQ6vbatmt8/4+SN0oe0I1cPDABg9Q==", "dev": true, - "peer": true, - "dependencies": { - "yocto-queue": "^0.1.0" - }, "engines": { - "node": ">=10" + "node": ">=14.21.3" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "peerDependencies": { + "@ianvs/prettier-plugin-sort-imports": "*", + "@prettier/plugin-pug": "*", + "@shopify/prettier-plugin-liquid": "*", + "@trivago/prettier-plugin-sort-imports": "*", + "@zackad/prettier-plugin-twig-melody": "*", + "prettier": "^3.0", + "prettier-plugin-astro": "*", + "prettier-plugin-css-order": "*", + "prettier-plugin-import-sort": "*", + "prettier-plugin-jsdoc": "*", + "prettier-plugin-marko": "*", + "prettier-plugin-organize-attributes": "*", + "prettier-plugin-organize-imports": "*", + "prettier-plugin-sort-imports": "*", + "prettier-plugin-style-order": "*", + "prettier-plugin-svelte": "*" + }, + "peerDependenciesMeta": { + "@ianvs/prettier-plugin-sort-imports": { + "optional": true + }, + "@prettier/plugin-pug": { + "optional": true + }, + "@shopify/prettier-plugin-liquid": { + "optional": true + }, + "@trivago/prettier-plugin-sort-imports": { + "optional": true + }, + "@zackad/prettier-plugin-twig-melody": { + "optional": true + }, + "prettier-plugin-astro": { + "optional": true + }, + "prettier-plugin-css-order": { + "optional": true + }, + "prettier-plugin-import-sort": { + "optional": true + }, + "prettier-plugin-jsdoc": { + "optional": true + }, + "prettier-plugin-marko": { + "optional": true + }, + "prettier-plugin-organize-attributes": { + "optional": true + }, + "prettier-plugin-organize-imports": { + "optional": true + }, + "prettier-plugin-sort-imports": { + "optional": true + }, + "prettier-plugin-style-order": { + "optional": true + }, + "prettier-plugin-svelte": { + "optional": true + } } }, - "node_modules/p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", "dev": true, - "peer": true, "dependencies": { - "p-limit": "^3.0.2" + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, "engines": { "node": ">=10" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/papaparse": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/papaparse/-/papaparse-5.4.1.tgz", - "integrity": "sha512-HipMsgJkZu8br23pW15uvo6sib6wne/4woLZPlFf3rpDyMe9ywEXUsuD7+6K9PRkJlVT51j/sCOYDKGGS3ZJrw==", - "dev": true - }, - "node_modules/parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", "dev": true, "peer": true, - "dependencies": { - "callsites": "^3.0.0" - }, "engines": { "node": ">=6" } }, - "node_modules/parse-json": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==", + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", "dev": true, - "dependencies": { - "error-ex": "^1.3.1", - "json-parse-better-errors": "^1.0.1" - }, - "engines": { - "node": ">=4" - } + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] }, - "node_modules/parsimmon": { - "version": "1.18.1", - "resolved": "https://registry.npmjs.org/parsimmon/-/parsimmon-1.18.1.tgz", - "integrity": "sha512-u7p959wLfGAhJpSDJVYXoyMCXWYwHia78HhRBWqk7AIbxdmlrfdp5wX0l3xv/iTSH5HvhN9K7o26hwwpgS5Nmw==", + "node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", "dev": true }, - "node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "node_modules/read-cache": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", + "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", "dev": true, - "peer": true, - "engines": { - "node": ">=8" + "dependencies": { + "pify": "^2.3.0" } }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "node_modules/read-cache/node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", "dev": true, "engines": { "node": ">=0.10.0" } }, - "node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true - }, - "node_modules/path-scurry": { - "version": "1.10.1", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.10.1.tgz", - "integrity": "sha512-MkhCqzzBEpPvxxQ71Md0b1Kk51W01lrYvlMzSUaIzNsODdd7mqhiimSZlr+VegAz5Z6Vzt9Xg2ttE//XBhH3EQ==", + "node_modules/read-pkg": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", + "integrity": "sha512-BLq/cCO9two+lBgiTYNqD6GdtK8s4NpaWrl6/rCO9w0TUS8oJl7cmToOZfRYllKTISY6nt1U7jQ53brmKqY6BA==", "dev": true, "dependencies": { - "lru-cache": "^9.1.1 || ^10.0.0", - "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + "load-json-file": "^4.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^3.0.0" }, "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": ">=4" } }, - "node_modules/path-scurry/node_modules/lru-cache": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.1.0.tgz", - "integrity": "sha512-/1clY/ui8CzjKFyjdvwPWJUYKiFVXG2I2cY0ssG7h4+hwk+XOIX7ZSG9Q7TW8TW3Kp3BUSqgFWBLgL4PJ+Blag==", + "node_modules/read-pkg/node_modules/path-type": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", + "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", "dev": true, + "dependencies": { + "pify": "^3.0.0" + }, "engines": { - "node": "14 || >=16.14" + "node": ">=4" } }, - "node_modules/path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", "dev": true, + "dependencies": { + "picomatch": "^2.2.1" + }, "engines": { - "node": ">=8" + "node": ">=8.10.0" } }, - "node_modules/pathe": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.2.tgz", - "integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==", - "dev": true - }, - "node_modules/pathval": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", - "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", + "node_modules/reflect.getprototypeof": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz", + "integrity": "sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==", "dev": true, + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.9", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.7", + "get-proto": "^1.0.1", + "which-builtin-type": "^1.2.1" + }, "engines": { - "node": "*" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/periscopic": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/periscopic/-/periscopic-3.1.0.tgz", - "integrity": "sha512-vKiQ8RRtkl9P+r/+oefh25C3fhybptkHKCZSPlcXiJux2tJF55GnEj3BVn4A5gKfq9NWWXXrxkHBwVPUfH0opw==", + "node_modules/regexp.prototype.flags": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz", + "integrity": "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==", + "dev": true, "dependencies": { - "@types/estree": "^1.0.0", - "estree-walker": "^3.0.0", - "is-reference": "^3.0.0" + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-errors": "^1.3.0", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "set-function-name": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", - "dev": true - }, - "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "node_modules/resolve": { + "version": "1.22.10", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", + "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", "dev": true, + "dependencies": { + "is-core-module": "^2.16.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, "engines": { - "node": ">=8.6" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/jonschlinkert" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/pidtree": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.3.1.tgz", - "integrity": "sha512-qQbW94hLHEqCg7nhby4yRC7G2+jYHY4Rguc2bjw7Uug4GIJuu1tvf2uHaZv5Q8zdt+WKJ6qK1FOI6amaWUo5FA==", + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", "dev": true, - "bin": { - "pidtree": "bin/pidtree.js" - }, + "peer": true, "engines": { - "node": ">=0.10" + "node": ">=4" } }, - "node_modules/pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", "dev": true, "engines": { + "iojs": ">=1.0.0", "node": ">=0.10.0" } }, - "node_modules/pirates": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz", - "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==", + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", "dev": true, - "engines": { - "node": ">= 6" + "peer": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/pkg-types": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.0.3.tgz", - "integrity": "sha512-nN7pYi0AQqJnoLPC9eHFQ8AcyaixBUOwvqc5TDnIKCMEE6I0y8P7OKA7fPexsXGCGxQDl/cmrLAp26LhcwxZ4A==", + "node_modules/rollup": { + "version": "4.30.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.30.0.tgz", + "integrity": "sha512-sDnr1pcjTgUT69qBksNF1N1anwfbyYG6TBQ22b03bII8EdiUQ7J0TlozVaTMjT/eEJAO49e1ndV7t+UZfL1+vA==", "dev": true, "dependencies": { - "jsonc-parser": "^3.2.0", - "mlly": "^1.2.0", - "pathe": "^1.1.0" + "@types/estree": "1.0.6" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.30.0", + "@rollup/rollup-android-arm64": "4.30.0", + "@rollup/rollup-darwin-arm64": "4.30.0", + "@rollup/rollup-darwin-x64": "4.30.0", + "@rollup/rollup-freebsd-arm64": "4.30.0", + "@rollup/rollup-freebsd-x64": "4.30.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.30.0", + "@rollup/rollup-linux-arm-musleabihf": "4.30.0", + "@rollup/rollup-linux-arm64-gnu": "4.30.0", + "@rollup/rollup-linux-arm64-musl": "4.30.0", + "@rollup/rollup-linux-loongarch64-gnu": "4.30.0", + "@rollup/rollup-linux-powerpc64le-gnu": "4.30.0", + "@rollup/rollup-linux-riscv64-gnu": "4.30.0", + "@rollup/rollup-linux-s390x-gnu": "4.30.0", + "@rollup/rollup-linux-x64-gnu": "4.30.0", + "@rollup/rollup-linux-x64-musl": "4.30.0", + "@rollup/rollup-win32-arm64-msvc": "4.30.0", + "@rollup/rollup-win32-ia32-msvc": "4.30.0", + "@rollup/rollup-win32-x64-msvc": "4.30.0", + "fsevents": "~2.3.2" } }, - "node_modules/postcss": { - "version": "8.4.35", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.35.tgz", - "integrity": "sha512-u5U8qYpBCpN13BsiEB0CbR1Hhh4Gc0zLFuedrHJKMctHCHAGrMdG0PRM/KErzAL3CU6/eckEtmHNB3x6e3c0vA==", + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", "dev": true, "funding": [ { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" + "type": "github", + "url": "https://github.com/sponsors/feross" }, { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" + "type": "patreon", + "url": "https://www.patreon.com/feross" }, { - "type": "github", - "url": "https://github.com/sponsors/ai" + "type": "consulting", + "url": "https://feross.org/support" } ], "dependencies": { - "nanoid": "^3.3.7", - "picocolors": "^1.0.0", - "source-map-js": "^1.0.2" - }, - "engines": { - "node": "^10 || ^12 || >=14" + "queue-microtask": "^1.2.2" } }, - "node_modules/postcss-import": { - "version": "15.1.0", - "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz", - "integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==", + "node_modules/safe-array-concat": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.3.tgz", + "integrity": "sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==", "dev": true, "dependencies": { - "postcss-value-parser": "^4.0.0", - "read-cache": "^1.0.0", - "resolve": "^1.1.7" + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "get-intrinsic": "^1.2.6", + "has-symbols": "^1.1.0", + "isarray": "^2.0.5" }, "engines": { - "node": ">=14.0.0" + "node": ">=0.4" }, - "peerDependencies": { - "postcss": "^8.0.0" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/postcss-js": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.1.tgz", - "integrity": "sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==", + "node_modules/safe-push-apply": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/safe-push-apply/-/safe-push-apply-1.0.0.tgz", + "integrity": "sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==", "dev": true, "dependencies": { - "camelcase-css": "^2.0.1" + "es-errors": "^1.3.0", + "isarray": "^2.0.5" }, "engines": { - "node": "^12 || ^14 || >= 16" + "node": ">= 0.4" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - "peerDependencies": { - "postcss": "^8.4.21" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/postcss-load-config": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.2.tgz", - "integrity": "sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==", + "node_modules/safe-regex-test": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz", + "integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==", "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], "dependencies": { - "lilconfig": "^3.0.0", - "yaml": "^2.3.4" + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "is-regex": "^1.2.1" }, "engines": { - "node": ">= 14" - }, - "peerDependencies": { - "postcss": ">=8.0.9", - "ts-node": ">=9.0.0" + "node": ">= 0.4" }, - "peerDependenciesMeta": { - "postcss": { - "optional": true - }, - "ts-node": { - "optional": true - } + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/postcss-load-config/node_modules/lilconfig": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.0.0.tgz", - "integrity": "sha512-K2U4W2Ff5ibV7j7ydLr+zLAkIg5JJ4lPn1Ltsdt+Tz/IjQ8buJ55pZAxoP34lqIiwtF9iAvtLv3JGv7CAyAg+g==", + "node_modules/semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", "dev": true, + "bin": { + "semver": "bin/semver.js" + }, "engines": { - "node": ">=14" + "node": ">=10" } }, - "node_modules/postcss-nested": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.0.1.tgz", - "integrity": "sha512-mEp4xPMi5bSWiMbsgoPfcP74lsWLHkQbZc3sY+jWYd65CUwXrUaTp0fmNpa01ZcETKlIgUdFN/MpS2xZtqL9dQ==", + "node_modules/set-function-length": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", "dev": true, "dependencies": { - "postcss-selector-parser": "^6.0.11" + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" }, "engines": { - "node": ">=12.0" + "node": ">= 0.4" + } + }, + "node_modules/set-function-name": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", + "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", + "dev": true, + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "functions-have-names": "^1.2.3", + "has-property-descriptors": "^1.0.2" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/set-proto": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/set-proto/-/set-proto-1.0.0.tgz", + "integrity": "sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==", + "dev": true, + "dependencies": { + "dunder-proto": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0" }, - "peerDependencies": { - "postcss": "^8.2.14" + "engines": { + "node": ">= 0.4" } }, - "node_modules/postcss-selector-parser": { - "version": "6.0.15", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.15.tgz", - "integrity": "sha512-rEYkQOMUCEMhsKbK66tbEU9QVIxbhN18YiniAwA7XQYTVBqrBy+P2p5JcdqsHgKM2zWylp8d7J6eszocfds5Sw==", + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", "dev": true, "dependencies": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" + "shebang-regex": "^3.0.0" }, "engines": { - "node": ">=4" + "node": ">=8" } }, - "node_modules/postcss-value-parser": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", - "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", - "dev": true + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "engines": { + "node": ">=8" + } }, - "node_modules/preact": { - "version": "10.19.3", - "resolved": "https://registry.npmjs.org/preact/-/preact-10.19.3.tgz", - "integrity": "sha512-nHHTeFVBTHRGxJXKkKu5hT8C/YWBkPso4/Gad6xuj5dbptt9iF9NZr9pHbPhBrnT2klheu7mHTxTZ/LjwJiEiQ==", + "node_modules/shell-quote": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.2.tgz", + "integrity": "sha512-AzqKpGKjrj7EM6rKVQEPpB288oCfnrEIuyoT9cyF4nmGa7V8Zk6f7RRqYisX8X9m+Q7bd632aZW4ky7EhbQztA==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", "dev": true, + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/preact" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/prelude-ls": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", "dev": true, - "peer": true, + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, "engines": { - "node": ">= 0.8.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/prettier": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.2.2.tgz", - "integrity": "sha512-HTByuKZzw7utPiDO523Tt2pLtEyK7OibUD9suEJQrPUCYQqrHr74GGX6VidMrovbf/I50mPqr8j/II6oBAuc5A==", + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", "dev": true, - "bin": { - "prettier": "bin/prettier.cjs" + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" }, "engines": { - "node": ">=14" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/prettier/prettier?sponsor=1" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/prettier-plugin-svelte": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/prettier-plugin-svelte/-/prettier-plugin-svelte-3.1.2.tgz", - "integrity": "sha512-7xfMZtwgAWHMT0iZc8jN4o65zgbAQ3+O32V6W7pXrqNvKnHnkoyQCGCbKeUyXKZLbYE0YhFRnamfxfkEGxm8qA==", + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", "dev": true, - "peerDependencies": { - "prettier": "^3.0.0", - "svelte": "^3.2.0 || ^4.0.0-next.0 || ^5.0.0-next.0" + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/prettier-plugin-tailwindcss": { - "version": "0.5.11", - "resolved": "https://registry.npmjs.org/prettier-plugin-tailwindcss/-/prettier-plugin-tailwindcss-0.5.11.tgz", - "integrity": "sha512-AvI/DNyMctyyxGOjyePgi/gqj5hJYClZ1avtQvLlqMT3uDZkRbi4HhGUpok3DRzv9z7Lti85Kdj3s3/1CeNI0w==", + "node_modules/siginfo": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", + "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", + "dev": true + }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", "dev": true, "engines": { - "node": ">=14.21.3" - }, - "peerDependencies": { - "@ianvs/prettier-plugin-sort-imports": "*", - "@prettier/plugin-pug": "*", - "@shopify/prettier-plugin-liquid": "*", - "@trivago/prettier-plugin-sort-imports": "*", - "prettier": "^3.0", - "prettier-plugin-astro": "*", - "prettier-plugin-css-order": "*", - "prettier-plugin-import-sort": "*", - "prettier-plugin-jsdoc": "*", - "prettier-plugin-marko": "*", - "prettier-plugin-organize-attributes": "*", - "prettier-plugin-organize-imports": "*", - "prettier-plugin-style-order": "*", - "prettier-plugin-svelte": "*" + "node": ">=14" }, - "peerDependenciesMeta": { - "@ianvs/prettier-plugin-sort-imports": { - "optional": true - }, - "@prettier/plugin-pug": { - "optional": true - }, - "@shopify/prettier-plugin-liquid": { - "optional": true - }, - "@trivago/prettier-plugin-sort-imports": { - "optional": true - }, - "prettier-plugin-astro": { - "optional": true - }, - "prettier-plugin-css-order": { - "optional": true - }, - "prettier-plugin-import-sort": { - "optional": true - }, - "prettier-plugin-jsdoc": { - "optional": true - }, - "prettier-plugin-marko": { - "optional": true - }, - "prettier-plugin-organize-attributes": { - "optional": true - }, - "prettier-plugin-organize-imports": { - "optional": true - }, - "prettier-plugin-style-order": { - "optional": true - }, - "prettier-plugin-svelte": { - "optional": true - }, - "prettier-plugin-twig-melody": { - "optional": true - } + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/pretty-format": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", - "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "node_modules/sirv": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/sirv/-/sirv-2.0.4.tgz", + "integrity": "sha512-94Bdh3cC2PKrbgSOUqTiGPWVZeSiXfKOVZNJniWoqrWrRkB1CJzBU3NEbiTsPcYy1lDsANA/THzS+9WBiy5nfQ==", "dev": true, "dependencies": { - "@jest/schemas": "^29.6.3", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" + "@polka/url": "^1.0.0-next.24", + "mrmime": "^2.0.0", + "totalist": "^3.0.0" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">= 10" } }, - "node_modules/pretty-format/node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", "dev": true, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "node": ">=8" } }, - "node_modules/punycode": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", - "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", "dev": true, - "peer": true, "engines": { - "node": ">=6" + "node": ">=0.10.0" } }, - "node_modules/queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "node_modules/spdx-correct": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", + "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] + "dependencies": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } }, - "node_modules/react-is": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", - "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", + "node_modules/spdx-exceptions": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz", + "integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==", "dev": true }, - "node_modules/read-cache": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", - "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", + "node_modules/spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", "dev": true, "dependencies": { - "pify": "^2.3.0" + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" } }, - "node_modules/read-pkg": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", - "integrity": "sha512-BLq/cCO9two+lBgiTYNqD6GdtK8s4NpaWrl6/rCO9w0TUS8oJl7cmToOZfRYllKTISY6nt1U7jQ53brmKqY6BA==", + "node_modules/spdx-license-ids": { + "version": "3.0.20", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.20.tgz", + "integrity": "sha512-jg25NiDV/1fLtSgEgyvVyDunvaNHbuwF9lfNV17gSmPFAlYzdfNBlLtLzXTevwkPj7DhGbmN9VnmJIgLnhvaBw==", + "dev": true + }, + "node_modules/stackback": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", + "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", + "dev": true + }, + "node_modules/std-env": { + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.8.0.tgz", + "integrity": "sha512-Bc3YwwCB+OzldMxOXJIIvC6cPRWr/LxOp48CdQTOkPyk/t4JWWJbrilwBd7RJzKV8QW7tJkcgAmeuLLJugl5/w==", + "dev": true + }, + "node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", "dev": true, "dependencies": { - "load-json-file": "^4.0.0", - "normalize-package-data": "^2.3.2", - "path-type": "^3.0.0" + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" }, "engines": { - "node": ">=4" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/read-pkg/node_modules/path-type": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", - "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dev": true, "dependencies": { - "pify": "^3.0.0" + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" }, "engines": { - "node": ">=4" + "node": ">=8" } }, - "node_modules/read-pkg/node_modules/pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", + "node_modules/string-width-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/string-width/node_modules/ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", "dev": true, "engines": { - "node": ">=4" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" } }, - "node_modules/readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "node_modules/string-width/node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true + }, + "node_modules/string-width/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", "dev": true, "dependencies": { - "picomatch": "^2.2.1" + "ansi-regex": "^6.0.1" }, "engines": { - "node": ">=8.10.0" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, - "node_modules/regexp.prototype.flags": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.1.tgz", - "integrity": "sha512-sy6TXMN+hnP/wMy+ISxg3krXx7BAtWVO4UouuCN/ziM9UEne0euamVNafDfvC83bRNr95y0V5iijeDQFUNpvrg==", + "node_modules/string.prototype.padend": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/string.prototype.padend/-/string.prototype.padend-3.1.6.tgz", + "integrity": "sha512-XZpspuSB7vJWhvJc9DLSlrXl1mcA2BdoY5jjnS135ydXqLoqhs96JjDtCkjJEQHvfqZIp9hBuBMgI589peyx9Q==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "set-function-name": "^2.0.0" + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-object-atoms": "^1.0.0" }, "engines": { "node": ">= 0.4" @@ -5595,661 +5799,608 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/resolve": { - "version": "1.22.8", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", - "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", + "node_modules/string.prototype.trim": { + "version": "1.2.10", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.10.tgz", + "integrity": "sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==", "dev": true, "dependencies": { - "is-core-module": "^2.13.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "define-data-property": "^1.1.4", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-object-atoms": "^1.0.0", + "has-property-descriptors": "^1.0.2" }, - "bin": { - "resolve": "bin/resolve" + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true, - "peer": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/reusify": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "node_modules/string.prototype.trimend": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.9.tgz", + "integrity": "sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==", "dev": true, + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, "engines": { - "iojs": ">=1.0.0", - "node": ">=0.10.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "node_modules/string.prototype.trimstart": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz", + "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==", "dev": true, - "peer": true, "dependencies": { - "glob": "^7.1.3" + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" }, - "bin": { - "rimraf": "bin.js" + "engines": { + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/isaacs" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/rollup": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.13.0.tgz", - "integrity": "sha512-3YegKemjoQnYKmsBlOHfMLVPPA5xLkQ8MHLLSw/fBrFaVkEayL51DilPpNNLq1exr98F2B1TzrV0FUlN3gWRPg==", + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, "dependencies": { - "@types/estree": "1.0.5" - }, - "bin": { - "rollup": "dist/bin/rollup" + "ansi-regex": "^5.0.1" }, "engines": { - "node": ">=18.0.0", - "npm": ">=8.0.0" - }, - "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.13.0", - "@rollup/rollup-android-arm64": "4.13.0", - "@rollup/rollup-darwin-arm64": "4.13.0", - "@rollup/rollup-darwin-x64": "4.13.0", - "@rollup/rollup-linux-arm-gnueabihf": "4.13.0", - "@rollup/rollup-linux-arm64-gnu": "4.13.0", - "@rollup/rollup-linux-arm64-musl": "4.13.0", - "@rollup/rollup-linux-riscv64-gnu": "4.13.0", - "@rollup/rollup-linux-x64-gnu": "4.13.0", - "@rollup/rollup-linux-x64-musl": "4.13.0", - "@rollup/rollup-win32-arm64-msvc": "4.13.0", - "@rollup/rollup-win32-ia32-msvc": "4.13.0", - "@rollup/rollup-win32-x64-msvc": "4.13.0", - "fsevents": "~2.3.2" + "node": ">=8" } }, - "node_modules/run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], "dependencies": { - "queue-microtask": "^1.2.2" + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" } }, - "node_modules/safe-array-concat": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.0.1.tgz", - "integrity": "sha512-6XbUAseYE2KtOuGueyeobCySj9L4+66Tn6KQMOPQJrAJEowYKW/YR/MGJZl7FdydUdaFu4LYyDZjxf4/Nmo23Q==", + "node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.2.1", - "has-symbols": "^1.0.3", - "isarray": "^2.0.5" - }, "engines": { - "node": ">=0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=4" } }, - "node_modules/safe-regex-test": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.2.tgz", - "integrity": "sha512-83S9w6eFq12BBIJYvjMux6/dkirb8+4zJRA9cxNBVb7Wq5fJBW+Xze48WqR8pxua7bDuAaaAxtVVd4Idjp1dBQ==", + "node_modules/strip-final-newline": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", + "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", "dev": true, - "dependencies": { - "call-bind": "^1.0.5", - "get-intrinsic": "^1.2.2", - "is-regex": "^1.1.4" - }, "engines": { - "node": ">= 0.4" + "node": ">=12" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/sander": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/sander/-/sander-0.5.1.tgz", - "integrity": "sha512-3lVqBir7WuKDHGrKRDn/1Ye3kwpXaDOMsiRP1wd6wpZW56gJhsbp5RqQpA6JG/P+pkXizygnr1dKR8vzWaVsfA==", + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", "dev": true, - "dependencies": { - "es6-promise": "^3.1.2", - "graceful-fs": "^4.1.3", - "mkdirp": "^0.5.1", - "rimraf": "^2.5.2" + "peer": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/sander/node_modules/rimraf": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", - "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "node_modules/strip-literal": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/strip-literal/-/strip-literal-2.1.1.tgz", + "integrity": "sha512-631UJ6O00eNGfMiWG78ck80dfBab8X6IVFB51jZK5Icd7XAs60Z5y7QdSd/wGIklnWvRbUNloVzhOKKmutxQ6Q==", "dev": true, "dependencies": { - "glob": "^7.1.3" + "js-tokens": "^9.0.1" }, - "bin": { - "rimraf": "bin.js" + "funding": { + "url": "https://github.com/sponsors/antfu" } }, - "node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "node_modules/style-mod": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/style-mod/-/style-mod-4.1.2.tgz", + "integrity": "sha512-wnD1HyVqpJUI2+eKZ+eo1UwghftP6yuFheBqqe+bWCotBjC2K1YnteJILRMs3SM4V/0dLEW1SC27MWP5y+mwmw==", + "dev": true + }, + "node_modules/sucrase": { + "version": "3.35.0", + "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.0.tgz", + "integrity": "sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==", "dev": true, "dependencies": { - "lru-cache": "^6.0.0" + "@jridgewell/gen-mapping": "^0.3.2", + "commander": "^4.0.0", + "glob": "^10.3.10", + "lines-and-columns": "^1.1.6", + "mz": "^2.7.0", + "pirates": "^4.0.1", + "ts-interface-checker": "^0.1.9" }, "bin": { - "semver": "bin/semver.js" + "sucrase": "bin/sucrase", + "sucrase-node": "bin/sucrase-node" }, "engines": { - "node": ">=10" + "node": ">=16 || 14 >=14.17" } }, - "node_modules/set-function-length": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.1.1.tgz", - "integrity": "sha512-VoaqjbBJKiWtg4yRcKBQ7g7wnGnLV3M8oLvVWwOk2PdYY6PEFegR1vezXR0tw6fZGF9csVakIRjrJiy2veSBFQ==", + "node_modules/sucrase/node_modules/glob": { + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", "dev": true, "dependencies": { - "define-data-property": "^1.1.1", - "get-intrinsic": "^1.2.1", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.0" + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" }, - "engines": { - "node": ">= 0.4" + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/set-function-name": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.1.tgz", - "integrity": "sha512-tMNCiqYVkXIZgc2Hnoy2IvC/f8ezc5koaRFkCjrpWzGpCd3qbZXPzVy9MAZzK1ch/X0jvSkojys3oqJN0qCmdA==", + "node_modules/sucrase/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", "dev": true, "dependencies": { - "define-data-property": "^1.0.1", - "functions-have-names": "^1.2.3", - "has-property-descriptors": "^1.0.0" + "brace-expansion": "^2.0.1" }, "engines": { - "node": ">= 0.4" + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, "dependencies": { - "shebang-regex": "^3.0.0" + "has-flag": "^4.0.0" }, "engines": { "node": ">=8" } }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", "dev": true, "engines": { - "node": ">=8" - } - }, - "node_modules/shell-quote": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.1.tgz", - "integrity": "sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA==", - "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", - "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.0", - "get-intrinsic": "^1.0.2", - "object-inspect": "^1.9.0" + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/siginfo": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", - "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", - "dev": true - }, - "node_modules/signal-exit": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", - "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", - "dev": true, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/sirv": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/sirv/-/sirv-2.0.4.tgz", - "integrity": "sha512-94Bdh3cC2PKrbgSOUqTiGPWVZeSiXfKOVZNJniWoqrWrRkB1CJzBU3NEbiTsPcYy1lDsANA/THzS+9WBiy5nfQ==", - "dev": true, - "dependencies": { - "@polka/url": "^1.0.0-next.24", - "mrmime": "^2.0.0", - "totalist": "^3.0.0" + "node_modules/svelte": { + "version": "5.16.4", + "resolved": "https://registry.npmjs.org/svelte/-/svelte-5.16.4.tgz", + "integrity": "sha512-bYQ4Ai0aryxE80/kRkC/zDnh5d8BgrSUM3/1FuGxknnijPq4aF+HEV7cTPVtrI+5AdT13dL4L9XWhUgARINS8Q==", + "dependencies": { + "@ampproject/remapping": "^2.3.0", + "@jridgewell/sourcemap-codec": "^1.5.0", + "@types/estree": "^1.0.5", + "acorn": "^8.12.1", + "acorn-typescript": "^1.4.13", + "aria-query": "^5.3.1", + "axobject-query": "^4.1.0", + "clsx": "^2.1.1", + "esm-env": "^1.2.1", + "esrap": "^1.3.2", + "is-reference": "^3.0.3", + "locate-character": "^3.0.0", + "magic-string": "^0.30.11", + "zimmerframe": "^1.1.2" }, "engines": { - "node": ">= 10" + "node": ">=18" } }, - "node_modules/slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "node_modules/svelte-preprocess": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/svelte-preprocess/-/svelte-preprocess-6.0.3.tgz", + "integrity": "sha512-PLG2k05qHdhmRG7zR/dyo5qKvakhm8IJ+hD2eFRQmMLHp7X3eJnjeupUtvuRpbNiF31RjVw45W+abDwHEmP5OA==", "dev": true, + "hasInstallScript": true, "engines": { - "node": ">=8" + "node": ">= 18.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.10.2", + "coffeescript": "^2.5.1", + "less": "^3.11.3 || ^4.0.0", + "postcss": "^7 || ^8", + "postcss-load-config": ">=3", + "pug": "^3.0.0", + "sass": "^1.26.8", + "stylus": ">=0.55", + "sugarss": "^2.0.0 || ^3.0.0 || ^4.0.0", + "svelte": "^4.0.0 || ^5.0.0-next.100 || ^5.0.0", + "typescript": "^5.0.0" + }, + "peerDependenciesMeta": { + "@babel/core": { + "optional": true + }, + "coffeescript": { + "optional": true + }, + "less": { + "optional": true + }, + "postcss": { + "optional": true + }, + "postcss-load-config": { + "optional": true + }, + "pug": { + "optional": true + }, + "sass": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "typescript": { + "optional": true + } } }, - "node_modules/sorcery": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/sorcery/-/sorcery-0.11.0.tgz", - "integrity": "sha512-J69LQ22xrQB1cIFJhPfgtLuI6BpWRiWu1Y3vSsIwK/eAScqJxd/+CJlUuHQRdX2C9NGFamq+KqNywGgaThwfHw==", + "node_modules/tailwindcss": { + "version": "3.4.17", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.17.tgz", + "integrity": "sha512-w33E2aCvSDP0tW9RZuNXadXlkHXqFzSkQew/aIa2i/Sj8fThxwovwlXHSPXTbAHwEIhBFXAedUhP2tueAKP8Og==", "dev": true, "dependencies": { - "@jridgewell/sourcemap-codec": "^1.4.14", - "buffer-crc32": "^0.2.5", - "minimist": "^1.2.0", - "sander": "^0.5.0" + "@alloc/quick-lru": "^5.2.0", + "arg": "^5.0.2", + "chokidar": "^3.6.0", + "didyoumean": "^1.2.2", + "dlv": "^1.1.3", + "fast-glob": "^3.3.2", + "glob-parent": "^6.0.2", + "is-glob": "^4.0.3", + "jiti": "^1.21.6", + "lilconfig": "^3.1.3", + "micromatch": "^4.0.8", + "normalize-path": "^3.0.0", + "object-hash": "^3.0.0", + "picocolors": "^1.1.1", + "postcss": "^8.4.47", + "postcss-import": "^15.1.0", + "postcss-js": "^4.0.1", + "postcss-load-config": "^4.0.2", + "postcss-nested": "^6.2.0", + "postcss-selector-parser": "^6.1.2", + "resolve": "^1.22.8", + "sucrase": "^3.35.0" }, "bin": { - "sorcery": "bin/sorcery" - } - }, - "node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, + "tailwind": "lib/cli.js", + "tailwindcss": "lib/cli.js" + }, "engines": { - "node": ">=0.10.0" + "node": ">=14.0.0" } }, - "node_modules/source-map-js": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", - "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", + "node_modules/test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "dev": true, + "dependencies": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + }, "engines": { - "node": ">=0.10.0" + "node": ">=8" } }, - "node_modules/spdx-correct": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", - "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", + "node_modules/test-exclude/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, "dependencies": { - "spdx-expression-parse": "^3.0.0", - "spdx-license-ids": "^3.0.0" + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" } }, - "node_modules/spdx-exceptions": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", - "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==", - "dev": true - }, - "node_modules/spdx-expression-parse": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", - "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "node_modules/test-exclude/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, "dependencies": { - "spdx-exceptions": "^2.1.0", - "spdx-license-ids": "^3.0.0" + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" } }, - "node_modules/spdx-license-ids": { - "version": "3.0.16", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.16.tgz", - "integrity": "sha512-eWN+LnM3GR6gPu35WxNgbGl8rmY1AEmoMDvL/QD6zYmPWgywxWqJWNdLGT+ke8dKNWrcYgYjPpG5gbTfghP8rw==", - "dev": true - }, - "node_modules/stackback": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", - "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", - "dev": true - }, - "node_modules/std-env": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.7.0.tgz", - "integrity": "sha512-JPbdCEQLj1w5GilpiHAx3qJvFndqybBysA3qUOnznweH4QbNYUsW/ea8QzSrnh0vNsezMMw5bcVool8lM0gwzg==", - "dev": true + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true, + "peer": true }, - "node_modules/string-width": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", - "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "node_modules/thenify": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", + "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", "dev": true, "dependencies": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^9.2.2", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "any-promise": "^1.0.0" } }, - "node_modules/string-width-cjs": { - "name": "string-width", - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "node_modules/thenify-all": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", + "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", "dev": true, "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" + "thenify": ">= 3.1.0 < 4" }, "engines": { - "node": ">=8" + "node": ">=0.8" } }, - "node_modules/string-width-cjs/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "node_modules/tinybench": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", + "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", "dev": true }, - "node_modules/string-width/node_modules/ansi-regex": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "node_modules/tinypool": { + "version": "0.8.4", + "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-0.8.4.tgz", + "integrity": "sha512-i11VH5gS6IFeLY3gMBQ00/MmLncVP7JLXOw1vlgkytLmJK7QnEr7NXf0LBdxfmNPAeyetukOk0bOYrJrFGjYJQ==", "dev": true, "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" + "node": ">=14.0.0" } }, - "node_modules/string-width/node_modules/strip-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "node_modules/tinyspy": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-2.2.1.tgz", + "integrity": "sha512-KYad6Vy5VDWV4GH3fjpseMQ/XU2BhIYP7Vzd0LG44qRWm/Yt2WCOTicFdvmgo6gWaqooMQCawTtILVQJupKu7A==", "dev": true, - "dependencies": { - "ansi-regex": "^6.0.1" - }, "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" + "node": ">=14.0.0" } }, - "node_modules/string.prototype.padend": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/string.prototype.padend/-/string.prototype.padend-3.1.5.tgz", - "integrity": "sha512-DOB27b/2UTTD+4myKUFh+/fXWcu/UDyASIXfg+7VzoCNNGOfWvoyU/x5pvVHr++ztyt/oSYI1BcWBBG/hmlNjA==", + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" + "is-number": "^7.0.0" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=8.0" } }, - "node_modules/string.prototype.trim": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.8.tgz", - "integrity": "sha512-lfjY4HcixfQXOfaqCvcBuOIapyaroTXhbkfJN3gcB1OtyupngWK4sEET9Knd0cXd28kTUqu/kHoV4HKSJdnjiQ==", + "node_modules/totalist": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/totalist/-/totalist-3.0.1.tgz", + "integrity": "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==", "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" - }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=6" } }, - "node_modules/string.prototype.trimend": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.7.tgz", - "integrity": "sha512-Ni79DqeB72ZFq1uH/L6zJ+DKZTkOtPIHovb3YZHQViE+HDouuU4mBrLOLDn5Dde3RF8qw5qVETEjhu9locMLvA==", + "node_modules/ts-api-utils": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.4.3.tgz", + "integrity": "sha512-i3eMG77UTMD0hZhgRS562pv83RC6ukSAC2GMNWc+9dieh/+jDM5u5YG+NHX6VNDRHQcHwmsTHctP9LhbC3WxVw==", "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" + "engines": { + "node": ">=16" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "peerDependencies": { + "typescript": ">=4.2.0" } }, - "node_modules/string.prototype.trimstart": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.7.tgz", - "integrity": "sha512-NGhtDFu3jCEm7B4Fy0DpLewdJQOZcQ0rGbwQ/+stjnrp2i+rlKeCvos9hOIeCmqwratM47OBxY7uFZzjxHXmrg==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } + "node_modules/ts-interface-checker": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", + "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", + "dev": true }, - "node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } + "node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "dev": true }, - "node_modules/strip-ansi-cjs": { - "name": "strip-ansi", - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", "dev": true, + "peer": true, "dependencies": { - "ansi-regex": "^5.0.1" + "prelude-ls": "^1.2.1" }, "engines": { - "node": ">=8" + "node": ">= 0.8.0" } }, - "node_modules/strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "node_modules/type-detect": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.1.0.tgz", + "integrity": "sha512-Acylog8/luQ8L7il+geoSxhEkazvkslg7PSNKOX59mbB9cOveP5aq9h74Y7YU8yDpJwetzQQrfIwtf4Wp4LKcw==", "dev": true, "engines": { "node": ">=4" } }, - "node_modules/strip-final-newline": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", - "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", + "node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", "dev": true, + "peer": true, "engines": { - "node": ">=12" + "node": ">=10" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/strip-indent": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", - "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", + "node_modules/typed-array-buffer": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz", + "integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==", "dev": true, "dependencies": { - "min-indent": "^1.0.0" + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-typed-array": "^1.1.14" }, "engines": { - "node": ">=8" + "node": ">= 0.4" } }, - "node_modules/strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "node_modules/typed-array-byte-length": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.3.tgz", + "integrity": "sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==", "dev": true, - "peer": true, + "dependencies": { + "call-bind": "^1.0.8", + "for-each": "^0.3.3", + "gopd": "^1.2.0", + "has-proto": "^1.2.0", + "is-typed-array": "^1.1.14" + }, "engines": { - "node": ">=8" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/strip-literal": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-literal/-/strip-literal-2.0.0.tgz", - "integrity": "sha512-f9vHgsCWBq2ugHAkGMiiYY+AYG0D/cbloKKg0nhaaaSNsujdGIpVXCNsrJpCKr5M0f4aI31mr13UjY6GAuXCKA==", + "node_modules/typed-array-byte-offset": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.4.tgz", + "integrity": "sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==", "dev": true, "dependencies": { - "js-tokens": "^8.0.2" + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "for-each": "^0.3.3", + "gopd": "^1.2.0", + "has-proto": "^1.2.0", + "is-typed-array": "^1.1.15", + "reflect.getprototypeof": "^1.0.9" + }, + "engines": { + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/antfu" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/strip-literal/node_modules/js-tokens": { - "version": "8.0.3", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-8.0.3.tgz", - "integrity": "sha512-UfJMcSJc+SEXEl9lH/VLHSZbThQyLpw1vLO1Lb+j4RWDvG3N2f7yj3PVQA3cmkTBNldJ9eFnM+xEXxHIXrYiJw==", - "dev": true - }, - "node_modules/style-mod": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/style-mod/-/style-mod-4.1.0.tgz", - "integrity": "sha512-Ca5ib8HrFn+f+0n4N4ScTIA9iTOQ7MaGS1ylHcoVqW9J7w2w8PzN6g9gKmTYgGEBH8e120+RCmhpje6jC5uGWA==", - "dev": true - }, - "node_modules/sucrase": { - "version": "3.35.0", - "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.0.tgz", - "integrity": "sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==", + "node_modules/typed-array-length": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.7.tgz", + "integrity": "sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==", "dev": true, "dependencies": { - "@jridgewell/gen-mapping": "^0.3.2", - "commander": "^4.0.0", - "glob": "^10.3.10", - "lines-and-columns": "^1.1.6", - "mz": "^2.7.0", - "pirates": "^4.0.1", - "ts-interface-checker": "^0.1.9" - }, - "bin": { - "sucrase": "bin/sucrase", - "sucrase-node": "bin/sucrase-node" + "call-bind": "^1.0.7", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "is-typed-array": "^1.1.13", + "possible-typed-array-names": "^1.0.0", + "reflect.getprototypeof": "^1.0.6" }, "engines": { - "node": ">=16 || 14 >=14.17" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/sucrase/node_modules/glob": { - "version": "10.3.10", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz", - "integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==", + "node_modules/typescript": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.2.tgz", + "integrity": "sha512-i5t66RHxDvVN40HfDd1PsEThGNnlMCMT3jMUuoh9/0TaqWevNontacunWyN02LA9/fIbEWlcHZcgTKb9QoaLfg==", "dev": true, - "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^2.3.5", - "minimatch": "^9.0.1", - "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0", - "path-scurry": "^1.10.1" - }, "bin": { - "glob": "dist/esm/bin.mjs" + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" }, "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": ">=14.17" } }, - "node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "node_modules/ufo": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.5.4.tgz", + "integrity": "sha512-UsUk3byDzKd04EyoZ7U4DOlxQaD14JUKQl6/P7wiX4FNvUfm3XL246n9W5AmqwW5RSFJ27NAuM0iLscAOYUiGQ==", + "dev": true + }, + "node_modules/unbox-primitive": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz", + "integrity": "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==", "dev": true, "dependencies": { - "has-flag": "^4.0.0" + "call-bound": "^1.0.3", + "has-bigints": "^1.0.2", + "has-symbols": "^1.1.0", + "which-boxed-primitive": "^1.1.1" }, - "engines": { - "node": ">=8" - } - }, - "node_modules/supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "dev": true, "engines": { "node": ">= 0.4" }, @@ -6257,79 +6408,84 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/svelte": { - "version": "4.2.8", - "resolved": "https://registry.npmjs.org/svelte/-/svelte-4.2.8.tgz", - "integrity": "sha512-hU6dh1MPl8gh6klQZwK/n73GiAHiR95IkFsesLPbMeEZi36ydaXL/ZAb4g9sayT0MXzpxyZjR28yderJHxcmYA==", + "node_modules/undici-types": { + "version": "6.19.8", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", + "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", + "dev": true + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "peer": true, "dependencies": { - "@ampproject/remapping": "^2.2.1", - "@jridgewell/sourcemap-codec": "^1.4.15", - "@jridgewell/trace-mapping": "^0.3.18", - "acorn": "^8.9.0", - "aria-query": "^5.3.0", - "axobject-query": "^3.2.1", - "code-red": "^1.0.3", - "css-tree": "^2.3.1", - "estree-walker": "^3.0.3", - "is-reference": "^3.0.1", - "locate-character": "^3.0.0", - "magic-string": "^0.30.4", - "periscopic": "^3.1.0" - }, - "engines": { - "node": ">=16" + "punycode": "^2.1.0" } }, - "node_modules/svelte-preprocess": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/svelte-preprocess/-/svelte-preprocess-5.1.3.tgz", - "integrity": "sha512-xxAkmxGHT+J/GourS5mVJeOXZzne1FR5ljeOUAMXUkfEhkLEllRreXpbl3dIYJlcJRfL1LO1uIAPpBpBfiqGPw==", + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true + }, + "node_modules/validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", "dev": true, - "hasInstallScript": true, "dependencies": { - "@types/pug": "^2.0.6", - "detect-indent": "^6.1.0", - "magic-string": "^0.30.5", - "sorcery": "^0.11.0", - "strip-indent": "^3.0.0" + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "node_modules/vite": { + "version": "5.4.11", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.11.tgz", + "integrity": "sha512-c7jFQRklXua0mTzneGW9QVyxFjUgwcihC4bXEtujIo2ouWCe1Ajt/amn2PCxYnhYfd5k09JX3SB7OYWFKYqj8Q==", + "dev": true, + "dependencies": { + "esbuild": "^0.21.3", + "postcss": "^8.4.43", + "rollup": "^4.20.0" + }, + "bin": { + "vite": "bin/vite.js" }, "engines": { - "node": ">= 16.0.0", - "pnpm": "^8.0.0" + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" }, "peerDependencies": { - "@babel/core": "^7.10.2", - "coffeescript": "^2.5.1", - "less": "^3.11.3 || ^4.0.0", - "postcss": "^7 || ^8", - "postcss-load-config": "^2.1.0 || ^3.0.0 || ^4.0.0 || ^5.0.0", - "pug": "^3.0.0", - "sass": "^1.26.8", - "stylus": "^0.55.0", - "sugarss": "^2.0.0 || ^3.0.0 || ^4.0.0", - "svelte": "^3.23.0 || ^4.0.0-next.0 || ^4.0.0 || ^5.0.0-next.0", - "typescript": ">=3.9.5 || ^4.0.0 || ^5.0.0" + "@types/node": "^18.0.0 || >=20.0.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.4.0" }, "peerDependenciesMeta": { - "@babel/core": { - "optional": true - }, - "coffeescript": { + "@types/node": { "optional": true }, "less": { "optional": true }, - "postcss": { - "optional": true - }, - "postcss-load-config": { + "lightningcss": { "optional": true }, - "pug": { + "sass": { "optional": true }, - "sass": { + "sass-embedded": { "optional": true }, "stylus": { @@ -6338,551 +6494,450 @@ "sugarss": { "optional": true }, - "typescript": { + "terser": { "optional": true } } }, - "node_modules/tailwindcss": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.1.tgz", - "integrity": "sha512-qAYmXRfk3ENzuPBakNK0SRrUDipP8NQnEY6772uDhflcQz5EhRdD7JNZxyrFHVQNCwULPBn6FNPp9brpO7ctcA==", - "dev": true, - "dependencies": { - "@alloc/quick-lru": "^5.2.0", - "arg": "^5.0.2", - "chokidar": "^3.5.3", - "didyoumean": "^1.2.2", - "dlv": "^1.1.3", - "fast-glob": "^3.3.0", - "glob-parent": "^6.0.2", - "is-glob": "^4.0.3", - "jiti": "^1.19.1", - "lilconfig": "^2.1.0", - "micromatch": "^4.0.5", - "normalize-path": "^3.0.0", - "object-hash": "^3.0.0", - "picocolors": "^1.0.0", - "postcss": "^8.4.23", - "postcss-import": "^15.1.0", - "postcss-js": "^4.0.1", - "postcss-load-config": "^4.0.1", - "postcss-nested": "^6.0.1", - "postcss-selector-parser": "^6.0.11", - "resolve": "^1.22.2", - "sucrase": "^3.32.0" - }, - "bin": { - "tailwind": "lib/cli.js", - "tailwindcss": "lib/cli.js" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/test-exclude": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", - "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", - "dev": true, - "dependencies": { - "@istanbuljs/schema": "^0.1.2", - "glob": "^7.1.4", - "minimatch": "^3.0.4" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/test-exclude/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/test-exclude/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "node_modules/vite-node": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-1.6.0.tgz", + "integrity": "sha512-de6HJgzC+TFzOu0NTC4RAIsyf/DY/ibWDYQUcuEA84EMHhcefTUGkjFHKKEJhQN4A+6I0u++kr3l36ZF2d7XRw==", "dev": true, "dependencies": { - "brace-expansion": "^1.1.7" + "cac": "^6.7.14", + "debug": "^4.3.4", + "pathe": "^1.1.1", + "picocolors": "^1.0.0", + "vite": "^5.0.0" + }, + "bin": { + "vite-node": "vite-node.mjs" }, "engines": { - "node": "*" + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" } }, - "node_modules/text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", - "dev": true, - "peer": true - }, - "node_modules/thenify": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", - "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", + "node_modules/vite/node_modules/@esbuild/aix-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", + "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", + "cpu": [ + "ppc64" + ], "dev": true, - "dependencies": { - "any-promise": "^1.0.0" + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=12" } }, - "node_modules/thenify-all": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", - "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", + "node_modules/vite/node_modules/@esbuild/android-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", + "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", + "cpu": [ + "arm" + ], "dev": true, - "dependencies": { - "thenify": ">= 3.1.0 < 4" - }, + "optional": true, + "os": [ + "android" + ], "engines": { - "node": ">=0.8" + "node": ">=12" } }, - "node_modules/tinybench": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.6.0.tgz", - "integrity": "sha512-N8hW3PG/3aOoZAN5V/NSAEDz0ZixDSSt5b/a05iqtpgfLWMSVuCo7w0k2vVvEjdrIoeGqZzweX2WlyioNIHchA==", - "dev": true - }, - "node_modules/tinypool": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-0.8.2.tgz", - "integrity": "sha512-SUszKYe5wgsxnNOVlBYO6IC+8VGWdVGZWAqUxp3UErNBtptZvWbwyUOyzNL59zigz2rCA92QiL3wvG+JDSdJdQ==", + "node_modules/vite/node_modules/@esbuild/android-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", + "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", + "cpu": [ + "arm64" + ], "dev": true, + "optional": true, + "os": [ + "android" + ], "engines": { - "node": ">=14.0.0" + "node": ">=12" } }, - "node_modules/tinyspy": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-2.2.1.tgz", - "integrity": "sha512-KYad6Vy5VDWV4GH3fjpseMQ/XU2BhIYP7Vzd0LG44qRWm/Yt2WCOTicFdvmgo6gWaqooMQCawTtILVQJupKu7A==", + "node_modules/vite/node_modules/@esbuild/android-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", + "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", + "cpu": [ + "x64" + ], "dev": true, + "optional": true, + "os": [ + "android" + ], "engines": { - "node": ">=14.0.0" + "node": ">=12" } }, - "node_modules/to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", + "node_modules/vite/node_modules/@esbuild/darwin-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", + "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", + "cpu": [ + "arm64" + ], "dev": true, + "optional": true, + "os": [ + "darwin" + ], "engines": { - "node": ">=4" + "node": ">=12" } }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "node_modules/vite/node_modules/@esbuild/darwin-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", + "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", + "cpu": [ + "x64" + ], "dev": true, - "dependencies": { - "is-number": "^7.0.0" - }, + "optional": true, + "os": [ + "darwin" + ], "engines": { - "node": ">=8.0" + "node": ">=12" } }, - "node_modules/totalist": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/totalist/-/totalist-3.0.1.tgz", - "integrity": "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==", + "node_modules/vite/node_modules/@esbuild/freebsd-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", + "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", + "cpu": [ + "arm64" + ], "dev": true, + "optional": true, + "os": [ + "freebsd" + ], "engines": { - "node": ">=6" + "node": ">=12" } }, - "node_modules/ts-api-utils": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.0.3.tgz", - "integrity": "sha512-wNMeqtMz5NtwpT/UZGY5alT+VoKdSsOOP/kqHFcUW1P/VRhH2wJ48+DN2WwUliNbQ976ETwDL0Ifd2VVvgonvg==", + "node_modules/vite/node_modules/@esbuild/freebsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", + "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", + "cpu": [ + "x64" + ], "dev": true, + "optional": true, + "os": [ + "freebsd" + ], "engines": { - "node": ">=16.13.0" - }, - "peerDependencies": { - "typescript": ">=4.2.0" + "node": ">=12" } }, - "node_modules/ts-interface-checker": { - "version": "0.1.13", - "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", - "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", - "dev": true - }, - "node_modules/ts-node": { - "version": "10.9.1", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", - "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", + "node_modules/vite/node_modules/@esbuild/linux-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", + "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", + "cpu": [ + "arm" + ], "dev": true, "optional": true, - "peer": true, - "dependencies": { - "@cspotcode/source-map-support": "^0.8.0", - "@tsconfig/node10": "^1.0.7", - "@tsconfig/node12": "^1.0.7", - "@tsconfig/node14": "^1.0.0", - "@tsconfig/node16": "^1.0.2", - "acorn": "^8.4.1", - "acorn-walk": "^8.1.1", - "arg": "^4.1.0", - "create-require": "^1.1.0", - "diff": "^4.0.1", - "make-error": "^1.1.1", - "v8-compile-cache-lib": "^3.0.1", - "yn": "3.1.1" - }, - "bin": { - "ts-node": "dist/bin.js", - "ts-node-cwd": "dist/bin-cwd.js", - "ts-node-esm": "dist/bin-esm.js", - "ts-node-script": "dist/bin-script.js", - "ts-node-transpile-only": "dist/bin-transpile.js", - "ts-script": "dist/bin-script-deprecated.js" - }, - "peerDependencies": { - "@swc/core": ">=1.2.50", - "@swc/wasm": ">=1.2.50", - "@types/node": "*", - "typescript": ">=2.7" - }, - "peerDependenciesMeta": { - "@swc/core": { - "optional": true - }, - "@swc/wasm": { - "optional": true - } + "os": [ + "linux" + ], + "engines": { + "node": ">=12" } }, - "node_modules/ts-node/node_modules/arg": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", - "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "node_modules/vite/node_modules/@esbuild/linux-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", + "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", + "cpu": [ + "arm64" + ], "dev": true, "optional": true, - "peer": true - }, - "node_modules/tslib": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", - "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", - "dev": true - }, - "node_modules/type-check": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", - "dev": true, - "peer": true, - "dependencies": { - "prelude-ls": "^1.2.1" - }, + "os": [ + "linux" + ], "engines": { - "node": ">= 0.8.0" + "node": ">=12" } }, - "node_modules/type-detect": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", - "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "node_modules/vite/node_modules/@esbuild/linux-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", + "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", + "cpu": [ + "ia32" + ], "dev": true, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=4" + "node": ">=12" } }, - "node_modules/type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "node_modules/vite/node_modules/@esbuild/linux-loong64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", + "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", + "cpu": [ + "loong64" + ], "dev": true, - "peer": true, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=12" } }, - "node_modules/typed-array-buffer": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.0.tgz", - "integrity": "sha512-Y8KTSIglk9OZEr8zywiIHG/kmQ7KWyjseXs1CbSo8vC42w7hg2HgYTxSWwP0+is7bWDc1H+Fo026CpHFwm8tkw==", + "node_modules/vite/node_modules/@esbuild/linux-mips64el": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", + "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", + "cpu": [ + "mips64el" + ], "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.2.1", - "is-typed-array": "^1.1.10" - }, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">= 0.4" + "node": ">=12" } }, - "node_modules/typed-array-byte-length": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.0.tgz", - "integrity": "sha512-Or/+kvLxNpeQ9DtSydonMxCx+9ZXOswtwJn17SNLvhptaXYDJvkFFP5zbfU/uLmvnBJlI4yrnXRxpdWH/M5tNA==", + "node_modules/vite/node_modules/@esbuild/linux-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", + "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", + "cpu": [ + "ppc64" + ], "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "for-each": "^0.3.3", - "has-proto": "^1.0.1", - "is-typed-array": "^1.1.10" - }, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=12" } }, - "node_modules/typed-array-byte-offset": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.0.tgz", - "integrity": "sha512-RD97prjEt9EL8YgAgpOkf3O4IF9lhJFr9g0htQkm0rchFp/Vx7LW5Q8fSXXub7BXAODyUQohRMyOc3faCPd0hg==", + "node_modules/vite/node_modules/@esbuild/linux-riscv64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", + "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", + "cpu": [ + "riscv64" + ], "dev": true, - "dependencies": { - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", - "for-each": "^0.3.3", - "has-proto": "^1.0.1", - "is-typed-array": "^1.1.10" - }, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=12" } }, - "node_modules/typed-array-length": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.4.tgz", - "integrity": "sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng==", + "node_modules/vite/node_modules/@esbuild/linux-s390x": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", + "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", + "cpu": [ + "s390x" + ], "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "for-each": "^0.3.3", - "is-typed-array": "^1.1.9" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" } }, - "node_modules/typescript": { - "version": "5.3.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz", - "integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==", + "node_modules/vite/node_modules/@esbuild/linux-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", + "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", + "cpu": [ + "x64" + ], "dev": true, - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=14.17" + "node": ">=12" } }, - "node_modules/ufo": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.4.0.tgz", - "integrity": "sha512-Hhy+BhRBleFjpJ2vchUNN40qgkh0366FWJGqVLYBHev0vpHTrXSA0ryT+74UiW6KWsldNurQMKGqCm1M2zBciQ==", - "dev": true - }, - "node_modules/unbox-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", - "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", + "node_modules/vite/node_modules/@esbuild/netbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", + "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", + "cpu": [ + "x64" + ], "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "has-bigints": "^1.0.2", - "has-symbols": "^1.0.3", - "which-boxed-primitive": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" } }, - "node_modules/undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", - "dev": true - }, - "node_modules/update-browserslist-db": { - "version": "1.0.13", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz", - "integrity": "sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } + "node_modules/vite/node_modules/@esbuild/openbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", + "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", + "cpu": [ + "x64" ], + "dev": true, "optional": true, - "peer": true, - "dependencies": { - "escalade": "^3.1.1", - "picocolors": "^1.0.0" - }, - "bin": { - "update-browserslist-db": "cli.js" - }, - "peerDependencies": { - "browserslist": ">= 4.21.0" + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" } }, - "node_modules/uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "node_modules/vite/node_modules/@esbuild/sunos-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", + "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", + "cpu": [ + "x64" + ], "dev": true, - "peer": true, - "dependencies": { - "punycode": "^2.1.0" + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" } }, - "node_modules/util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", - "dev": true - }, - "node_modules/v8-compile-cache-lib": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", - "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", + "node_modules/vite/node_modules/@esbuild/win32-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", + "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", + "cpu": [ + "arm64" + ], "dev": true, "optional": true, - "peer": true - }, - "node_modules/v8-to-istanbul": { - "version": "9.2.0", - "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.2.0.tgz", - "integrity": "sha512-/EH/sDgxU2eGxajKdwLCDmQ4FWq+kpi3uCmBGpw1xJtnAxEjlD8j8PEiGWpCIMIs3ciNAgH0d3TTJiUkYzyZjA==", - "dev": true, - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.12", - "@types/istanbul-lib-coverage": "^2.0.1", - "convert-source-map": "^2.0.0" - }, + "os": [ + "win32" + ], "engines": { - "node": ">=10.12.0" + "node": ">=12" } }, - "node_modules/validate-npm-package-license": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", - "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "node_modules/vite/node_modules/@esbuild/win32-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", + "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", + "cpu": [ + "ia32" + ], "dev": true, - "dependencies": { - "spdx-correct": "^3.0.0", - "spdx-expression-parse": "^3.0.0" + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" } }, - "node_modules/vite": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/vite/-/vite-5.1.6.tgz", - "integrity": "sha512-yYIAZs9nVfRJ/AiOLCA91zzhjsHUgMjB+EigzFb6W2XTLO8JixBCKCjvhKZaye+NKYHCrkv3Oh50dH9EdLU2RA==", + "node_modules/vite/node_modules/@esbuild/win32-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", + "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", + "cpu": [ + "x64" + ], "dev": true, - "dependencies": { - "esbuild": "^0.19.3", - "postcss": "^8.4.35", - "rollup": "^4.2.0" - }, - "bin": { - "vite": "bin/vite.js" - }, + "optional": true, + "os": [ + "win32" + ], "engines": { - "node": "^18.0.0 || >=20.0.0" - }, - "funding": { - "url": "https://github.com/vitejs/vite?sponsor=1" - }, - "optionalDependencies": { - "fsevents": "~2.3.3" - }, - "peerDependencies": { - "@types/node": "^18.0.0 || >=20.0.0", - "less": "*", - "lightningcss": "^1.21.0", - "sass": "*", - "stylus": "*", - "sugarss": "*", - "terser": "^5.4.0" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - }, - "less": { - "optional": true - }, - "lightningcss": { - "optional": true - }, - "sass": { - "optional": true - }, - "stylus": { - "optional": true - }, - "sugarss": { - "optional": true - }, - "terser": { - "optional": true - } + "node": ">=12" } }, - "node_modules/vite-node": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-1.3.1.tgz", - "integrity": "sha512-azbRrqRxlWTJEVbzInZCTchx0X69M/XPTCz4H+TLvlTcR/xH/3hkRqhOakT41fMJCMzXTu4UvegkZiEoJAWvng==", + "node_modules/vite/node_modules/esbuild": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", + "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", "dev": true, - "dependencies": { - "cac": "^6.7.14", - "debug": "^4.3.4", - "pathe": "^1.1.1", - "picocolors": "^1.0.0", - "vite": "^5.0.0" - }, + "hasInstallScript": true, "bin": { - "vite-node": "vite-node.mjs" + "esbuild": "bin/esbuild" }, "engines": { - "node": "^18.0.0 || >=20.0.0" + "node": ">=12" }, - "funding": { - "url": "https://opencollective.com/vitest" + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.21.5", + "@esbuild/android-arm": "0.21.5", + "@esbuild/android-arm64": "0.21.5", + "@esbuild/android-x64": "0.21.5", + "@esbuild/darwin-arm64": "0.21.5", + "@esbuild/darwin-x64": "0.21.5", + "@esbuild/freebsd-arm64": "0.21.5", + "@esbuild/freebsd-x64": "0.21.5", + "@esbuild/linux-arm": "0.21.5", + "@esbuild/linux-arm64": "0.21.5", + "@esbuild/linux-ia32": "0.21.5", + "@esbuild/linux-loong64": "0.21.5", + "@esbuild/linux-mips64el": "0.21.5", + "@esbuild/linux-ppc64": "0.21.5", + "@esbuild/linux-riscv64": "0.21.5", + "@esbuild/linux-s390x": "0.21.5", + "@esbuild/linux-x64": "0.21.5", + "@esbuild/netbsd-x64": "0.21.5", + "@esbuild/openbsd-x64": "0.21.5", + "@esbuild/sunos-x64": "0.21.5", + "@esbuild/win32-arm64": "0.21.5", + "@esbuild/win32-ia32": "0.21.5", + "@esbuild/win32-x64": "0.21.5" } }, "node_modules/vitest": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-1.3.1.tgz", - "integrity": "sha512-/1QJqXs8YbCrfv/GPQ05wAZf2eakUPLPa18vkJAKE7RXOKfVHqMZZ1WlTjiwl6Gcn65M5vpNUB6EFLnEdRdEXQ==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-1.6.0.tgz", + "integrity": "sha512-H5r/dN06swuFnzNFhq/dnz37bPXnq8xB2xB5JOVk8K09rUtoeNN+LHWkoQ0A/i3hvbUKKcCei9KpbxqHMLhLLA==", "dev": true, "dependencies": { - "@vitest/expect": "1.3.1", - "@vitest/runner": "1.3.1", - "@vitest/snapshot": "1.3.1", - "@vitest/spy": "1.3.1", - "@vitest/utils": "1.3.1", + "@vitest/expect": "1.6.0", + "@vitest/runner": "1.6.0", + "@vitest/snapshot": "1.6.0", + "@vitest/spy": "1.6.0", + "@vitest/utils": "1.6.0", "acorn-walk": "^8.3.2", "chai": "^4.3.10", "debug": "^4.3.4", @@ -6894,9 +6949,9 @@ "std-env": "^3.5.0", "strip-literal": "^2.0.0", "tinybench": "^2.5.1", - "tinypool": "^0.8.2", + "tinypool": "^0.8.3", "vite": "^5.0.0", - "vite-node": "1.3.1", + "vite-node": "1.6.0", "why-is-node-running": "^2.2.2" }, "bin": { @@ -6911,8 +6966,8 @@ "peerDependencies": { "@edge-runtime/vm": "*", "@types/node": "^18.0.0 || >=20.0.0", - "@vitest/browser": "1.3.1", - "@vitest/ui": "1.3.1", + "@vitest/browser": "1.6.0", + "@vitest/ui": "1.6.0", "happy-dom": "*", "jsdom": "*" }, @@ -6959,32 +7014,81 @@ } }, "node_modules/which-boxed-primitive": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz", + "integrity": "sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==", + "dev": true, + "dependencies": { + "is-bigint": "^1.1.0", + "is-boolean-object": "^1.2.1", + "is-number-object": "^1.1.1", + "is-string": "^1.1.1", + "is-symbol": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-builtin-type": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.2.1.tgz", + "integrity": "sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.2", + "function.prototype.name": "^1.1.6", + "has-tostringtag": "^1.0.2", + "is-async-function": "^2.0.0", + "is-date-object": "^1.1.0", + "is-finalizationregistry": "^1.1.0", + "is-generator-function": "^1.0.10", + "is-regex": "^1.2.1", + "is-weakref": "^1.0.2", + "isarray": "^2.0.5", + "which-boxed-primitive": "^1.1.0", + "which-collection": "^1.0.2", + "which-typed-array": "^1.1.16" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-collection": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", - "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", + "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz", + "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==", "dev": true, "dependencies": { - "is-bigint": "^1.0.1", - "is-boolean-object": "^1.1.0", - "is-number-object": "^1.0.4", - "is-string": "^1.0.5", - "is-symbol": "^1.0.3" + "is-map": "^2.0.3", + "is-set": "^2.0.3", + "is-weakmap": "^2.0.2", + "is-weakset": "^2.0.3" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/which-typed-array": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.13.tgz", - "integrity": "sha512-P5Nra0qjSncduVPEAr7xhoF5guty49ArDTwzJ/yNuPIbZppyRxFQsRCWrocxIY+CnMVG+qfbU2FmDKyvSGClow==", + "version": "1.1.18", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.18.tgz", + "integrity": "sha512-qEcY+KJYlWyLH9vNbsr6/5j59AXk5ni5aakf8ldzBvGde6Iz4sxZGkJyWSAueTG7QhOvNRYb1lDdFmL5Td0QKA==", "dev": true, "dependencies": { - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.4", + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-tostringtag": "^1.0.0" + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -6994,9 +7098,9 @@ } }, "node_modules/why-is-node-running": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.2.2.tgz", - "integrity": "sha512-6tSwToZxTOcotxHeA+qGCq1mVzKR3CwcJGmVcY+QE8SHy6TnpFnh8PAvPNHYr7EcuVeG0QSMxtYCuO1ta/G/oA==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", + "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", "dev": true, "dependencies": { "siginfo": "^2.0.0", @@ -7009,6 +7113,16 @@ "node": ">=8" } }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/wrap-ansi": { "version": "8.1.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", @@ -7065,9 +7179,9 @@ } }, "node_modules/wrap-ansi/node_modules/ansi-regex": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", "dev": true, "engines": { "node": ">=12" @@ -7109,32 +7223,18 @@ "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", "dev": true }, - "node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, "node_modules/yaml": { - "version": "2.3.4", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.3.4.tgz", - "integrity": "sha512-8aAvwVUSHpfEqTQ4w/KMlf3HcRdt50E5ODIQJBw1fQ5RL34xabzxtUlzTXVqc4rkZsPbvrXKWnABCD7kWSmocA==", + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.7.0.tgz", + "integrity": "sha512-+hSoy/QHluxmC9kCIJyL/uyFmLmc+e5CFR5Wa+bpIhIj85LVb9ZH2nVnqrHoSvKogwODv0ClqZkmiSSaIH5LTA==", "dev": true, + "bin": { + "yaml": "bin.mjs" + }, "engines": { "node": ">= 14" } }, - "node_modules/yn": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", - "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", - "dev": true, - "optional": true, - "peer": true, - "engines": { - "node": ">=6" - } - }, "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", @@ -7148,10 +7248,15 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/zimmerframe": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/zimmerframe/-/zimmerframe-1.1.2.tgz", + "integrity": "sha512-rAbqEGa8ovJy4pyBxZM70hg4pE6gDgaQ0Sl9M3enG3I0d6H4XSAM3GeNGLKnsBpuijUow064sf7ww1nutC5/3w==" + }, "node_modules/zod": { - "version": "3.23.3", - "resolved": "https://registry.npmjs.org/zod/-/zod-3.23.3.tgz", - "integrity": "sha512-tPvq1B/2Yu/dh2uAIH2/BhUlUeLIUvAjr6dpL/75I0pCYefHgjhXk1o1Kob3kTU8C7yU1j396jFHlsVWFi9ogg==", + "version": "3.24.1", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.24.1.tgz", + "integrity": "sha512-muH7gBL9sI1nciMZV67X5fTKKBLtwpZ5VBp1vsOQzj1MhrBZ4wlVCm3gedKZWLp0Oyel8sIGfeiz54Su+OVT+A==", "funding": { "url": "https://github.com/sponsors/colinhacks" } diff --git a/package.json b/package.json index 0c54681f..8b8bcf34 100644 --- a/package.json +++ b/package.json @@ -28,7 +28,7 @@ "license": "MIT", "devDependencies": { "@aidenlx/folder-note-core": "^1.3.6", - "@tsconfig/svelte": "^5.0.2", + "@tsconfig/svelte": "^5.0.4", "@types/luxon": "^3.4.2", "@types/node": "^20.11.0", "@types/obsidian-typings": "npm:obsidian-typings@^1.0.6", @@ -37,16 +37,16 @@ "@vitest/coverage-v8": "^1.3.1", "@vitest/ui": "^1.3.1", "builtin-modules": "3.3.0", - "esbuild": "0.19.11", - "esbuild-svelte": "^0.8.0", + "esbuild": "0.24.2", + "esbuild-svelte": "^0.9.0", "npm-run-all": "^4.1.5", - "obsidian": "^1.5.7-1", + "obsidian": "^1.7.2", "obsidian-dataview": "^0.5.64", - "prettier": "^3.2.2", - "prettier-plugin-svelte": "^3.1.2", + "prettier": "^3.4.2", + "prettier-plugin-svelte": "^3.3.2", "prettier-plugin-tailwindcss": "^0.5.11", - "svelte": "^4.2.8", - "svelte-preprocess": "^5.1.3", + "svelte": "^5.16.2", + "svelte-preprocess": "^6.0.3", "tailwindcss": "^3.4.1", "tslib": "2.6.2", "typescript": "^5.3.3", @@ -56,6 +56,6 @@ "graphology": "^0.25.4", "lucide-svelte": "^0.360.0", "luxon": "^3.4.4", - "zod": "^3.23.3" + "zod": "^3.24.1" } } diff --git a/src/commands/list_index/index.ts b/src/commands/list_index/index.ts index 8b889289..8f1f3d0f 100644 --- a/src/commands/list_index/index.ts +++ b/src/commands/list_index/index.ts @@ -4,7 +4,7 @@ import type { ShowNodeOptions } from "src/interfaces/settings"; import type BreadcrumbsPlugin from "src/main"; import { Links } from "src/utils/links"; import { toNodeStringifyOptions, type EdgeAttribute } from "src/graph/utils"; -import { TraversalOptions, create_edge_sorter, type RecTraversalData } from "wasm/pkg/breadcrumbs_graph_wasm"; +import { FlatTraversalData, FlatTraversalResult, TraversalOptions, TraversalPostprocessOptions, create_edge_sorter } from "wasm/pkg/breadcrumbs_graph_wasm"; export namespace ListIndex { export type Options = { @@ -15,7 +15,7 @@ export namespace ListIndex { max_depth?: number; link_kind: LinkKind; edge_sort_id: EdgeSortId; - field_group_labels: string[]; + field_group_labels: string[]; show_attributes: EdgeAttribute[]; show_node_options: ShowNodeOptions; }; @@ -37,9 +37,24 @@ export namespace ListIndex { }, }; + // TODO(Rust): This should probably be moved to the Rust side export const edge_tree_to_list_index = ( plugin: BreadcrumbsPlugin, - tree: RecTraversalData[], + tree: FlatTraversalResult, + options: Pick< + Options, + "link_kind" | "indent" | "show_node_options" | "show_attributes" + >, + ) => { + const all_traversal_data = tree.data; + const current_nodes = Array.from(tree.entry_nodes).map((node_index) => all_traversal_data[node_index]); + return edge_tree_to_list_index_inner(plugin, all_traversal_data, current_nodes, options); + }; + + export const edge_tree_to_list_index_inner = ( + plugin: BreadcrumbsPlugin, + all_traversal_data: FlatTraversalData[], + current_nodes: FlatTraversalData[], options: Pick< Options, "link_kind" | "indent" | "show_node_options" | "show_attributes" @@ -48,7 +63,7 @@ export namespace ListIndex { let index = ""; const real_indent = options.indent.replace(/\\t/g, "\t"); - tree.forEach(({ children, depth, edge }) => { + current_nodes.forEach(({ children, depth, edge }) => { const display = edge.stringify_target( plugin.graph, toNodeStringifyOptions(plugin, options.show_node_options) @@ -62,7 +77,14 @@ export namespace ListIndex { index += real_indent.repeat(depth) + `- ${link}${attr}\n`; - index += edge_tree_to_list_index(plugin, children, options); + const new_children = Array.from(children).map((child_id) => all_traversal_data[child_id]); + + index += edge_tree_to_list_index_inner( + plugin, + all_traversal_data, + new_children, + options + ); }); return index; @@ -80,13 +102,16 @@ export namespace ListIndex { false, ); - const traversal_result = plugin.graph.rec_traverse(traversal_options); - const edge_sorter = create_edge_sorter(options.edge_sort_id.field, options.edge_sort_id.order === -1); - traversal_result.sort(plugin.graph, edge_sorter); + const postprocess_options = new TraversalPostprocessOptions( + create_edge_sorter(options.edge_sort_id.field, options.edge_sort_id.order === -1), + false, + ); + + const traversal_result = plugin.graph.rec_traverse_and_process(traversal_options, postprocess_options); return edge_tree_to_list_index( plugin, - traversal_result.data, + traversal_result, options, ); } diff --git a/src/components/NestedEdgeList.svelte b/src/components/NestedEdgeList.svelte index dc1b2b18..b02b1022 100644 --- a/src/components/NestedEdgeList.svelte +++ b/src/components/NestedEdgeList.svelte @@ -4,13 +4,13 @@ import EdgeLink from "./EdgeLink.svelte"; import ChevronOpener from "./button/ChevronOpener.svelte"; import TreeItemFlair from "./obsidian/TreeItemFlair.svelte"; - import { FlatRecTraversalData } from "wasm/pkg/breadcrumbs_graph_wasm"; + import { FlatTraversalData } from "wasm/pkg/breadcrumbs_graph_wasm"; import { toNodeStringifyOptions, type EdgeAttribute } from "src/graph/utils"; export let plugin: BreadcrumbsPlugin; // export let tree: RecTraversalData[]; - export let data: FlatRecTraversalData[]; + export let data: FlatTraversalData[]; export let items: Uint32Array; export let open_signal: boolean | null; diff --git a/src/components/codeblocks/CodeblockTree.svelte b/src/components/codeblocks/CodeblockTree.svelte index ff9abd6f..0b6682a0 100644 --- a/src/components/codeblocks/CodeblockTree.svelte +++ b/src/components/codeblocks/CodeblockTree.svelte @@ -8,10 +8,10 @@ import { Timer } from "src/utils/timer"; import { onMount } from "svelte"; import { - FlatRecTraversalResult, + FlatTraversalResult, NoteGraphError, - RecTraversalResult, TraversalOptions, + TraversalPostprocessOptions, create_edge_sorter, } from "wasm/pkg/breadcrumbs_graph_wasm"; import NestedEdgeList from "../NestedEdgeList.svelte"; @@ -31,8 +31,7 @@ const DEFAULT_MAX_DEPTH = 10; - let tree: RecTraversalResult | undefined = undefined; - let data: FlatRecTraversalResult | undefined = undefined; + let data: FlatTraversalResult | undefined = undefined; let error: string | undefined = undefined; export const update = () => { @@ -41,7 +40,7 @@ const source_path = options["start-note"] || file_path || $active_file_store?.path || ""; if (!plugin.graph.has_node(source_path)) { - tree = undefined; + data = undefined; error = "The file does not exist in the graph."; return; } @@ -53,17 +52,18 @@ !options["merge-fields"], ); + const postprocess_options = new TraversalPostprocessOptions( + sort, + options.flat + ); + try { - tree = plugin.graph.rec_traverse(traversal_options); - if (options.flat) tree.flatten(plugin.graph); - tree.sort(plugin.graph, sort); - data = tree.to_flat(); + data = plugin.graph.rec_traverse_and_process(traversal_options, postprocess_options); error = undefined; } catch (e) { log.error("Error updating codeblock tree", e); - tree = undefined; data = undefined; if (e instanceof NoteGraphError) { error = e.message; @@ -73,7 +73,6 @@ } } - tree = tree; data = data; }; @@ -95,12 +94,12 @@ {/if} - {#if tree && !tree.is_empty() && data} + {#if data && !data.is_empty()}
path.truncate(depth)); - - // // Remove duplicates by the target_ids of the path. - // $: deduped_paths = - // // There are no duplicates if the depth is the max depth. - // // The traversal wouldn't add them in the first place. - // depth === MAX_DEPTH - // ? truncated_paths - // : remove_duplicates_by_equals(truncated_paths, (a, b) => a.equals(b)); - - // // NOTE: Only sort after slicing, so that the depth is taken into account. - // $: sorted_paths = deduped_paths.sort((a, b) => { - // const len_diff = b.length() - a.length(); - - // // Focus on run-length first - // if (len_diff !== 0) { - // return len_diff; - // } - // // Then focus on the alphabetical order of the target_ids - // else { - // const a_target = a.get_first_target(); - // const b_target = b.get_first_target(); - - // if (a_target === undefined && b_target === undefined) return 0; - // if (a_target === undefined) return -1; - // if (b_target === undefined) return 1; - // else return a_target.localeCompare(b_target); - // } - // }); - $: sorted_paths = selected_paths?.process(plugin.graph, depth); diff --git a/src/components/side_views/TreeView.svelte b/src/components/side_views/TreeView.svelte index e5fd4e90..18cd6c1d 100644 --- a/src/components/side_views/TreeView.svelte +++ b/src/components/side_views/TreeView.svelte @@ -10,49 +10,52 @@ import FieldGroupLabelsSelector from "../selector/FieldGroupLabelsSelector.svelte"; import ShowAttributesSelectorMenu from "../selector/ShowAttributesSelectorMenu.svelte"; import { - FlatRecTraversalResult, TraversalOptions, + TraversalPostprocessOptions, create_edge_sorter, } from "wasm/pkg/breadcrumbs_graph_wasm"; - - export let plugin: BreadcrumbsPlugin; + import { derived } from "svelte/store"; let { - edge_sort_id, - merge_fields, - show_attributes, - show_node_options, - field_group_labels, - collapse, - } = plugin.settings.views.side.tree; + plugin + }: { + plugin: BreadcrumbsPlugin; + } = $props(); + + let edge_sort_id = $state(plugin.settings.views.side.tree.edge_sort_id); + let merge_fields = $state(plugin.settings.views.side.tree.merge_fields); + let show_attributes = $state(plugin.settings.views.side.tree.show_attributes); + let show_node_options = $state(plugin.settings.views.side.tree.show_node_options); + let field_group_labels = $state(plugin.settings.views.side.tree.field_group_labels); + let collapse = $state(plugin.settings.views.side.tree.collapse); - const edge_field_labels = resolve_field_group_labels( + let edge_field_labels = $derived(resolve_field_group_labels( plugin.settings.edge_field_groups, field_group_labels, - ); + )); - let data: FlatRecTraversalResult | undefined = undefined; + let sort = $derived(create_edge_sorter(edge_sort_id.field, edge_sort_id.order === -1)); - $: sort = create_edge_sorter(edge_sort_id.field, edge_sort_id.order === -1); - - $: tree = $active_file_store && plugin.graph.has_node($active_file_store.path) - ? plugin.graph.rec_traverse( + let tree = $derived.by(() => { + if ($active_file_store && plugin.graph.has_node($active_file_store.path)) { + return plugin.graph.rec_traverse_and_process( new TraversalOptions( [$active_file_store!.path], edge_field_labels, 5, !merge_fields, ), - ) - : undefined; - - $: { - tree?.sort(plugin.graph, sort); - - data = tree?.to_flat(); + new TraversalPostprocessOptions( + undefined, + false, + ), + ); + } else { + return undefined; + } + }); - // console.trace(data, data?.data.length); - } + $effect(() => tree?.sort(plugin.graph, sort));
@@ -93,14 +96,14 @@
- {#key data} - {#if data && !data.is_empty()} + {#key tree} + {#if tree && !tree.is_empty()} {:else} diff --git a/wasm/src/edge_sorting.rs b/wasm/src/edge_sorting.rs index a57d7f9a..0b483f94 100644 --- a/wasm/src/edge_sorting.rs +++ b/wasm/src/edge_sorting.rs @@ -7,7 +7,7 @@ use wasm_bindgen::prelude::*; use crate::{ graph::NoteGraph, graph_data::EdgeStruct, - graph_traversal::RecTraversalData, + graph_traversal::TraversalData, utils::{NoteGraphError, Result}, }; @@ -46,13 +46,13 @@ pub fn create_edge_sorter(field: String, reverse: bool) -> Result { #[wasm_bindgen] pub fn sort_traversal_data( graph: &NoteGraph, - traversal_data: Vec, + traversal_data: Vec, sorter: &EdgeSorter, -) -> Vec { +) -> Result> { let mut traversal_data = traversal_data.clone(); - sorter.sort_traversal_data(graph, &mut traversal_data); + sorter.sort_traversal_data(graph, &mut traversal_data)?; - traversal_data + Ok(traversal_data) } #[wasm_bindgen] @@ -60,13 +60,13 @@ pub fn sort_edges( graph: &NoteGraph, edges: Vec, sorter: &EdgeSorter, -) -> Vec { +) -> Result> { // utils::log(format!("Sorting edges: {:?}", edges)); let mut edges = edges.clone(); - sorter.sort_edges(graph, &mut edges); + sorter.sort_edges(graph, &mut edges)?; - edges + Ok(edges) } #[wasm_bindgen] @@ -81,16 +81,49 @@ impl EdgeSorter { EdgeSorter { field, reverse } } - pub fn sort_edges(&self, graph: &NoteGraph, edges: &mut [EdgeStruct]) { + pub fn sort_edges(&self, graph: &NoteGraph, edges: &mut [EdgeStruct]) -> Result<()> { let comparer = self.get_edge_comparer(graph); + // Check that all edges are still valid. The comparers will panic on any errors. + for edge in edges.iter() { + edge.check_revision(graph)?; + } + edges.sort_by(|a, b| self.apply_edge_ordering(graph, &comparer, a, b)); + Ok(()) + } + + pub fn sort_traversal_data( + &self, + graph: &NoteGraph, + data: &mut [TraversalData], + ) -> Result<()> { + let comparer = self.get_edge_comparer(graph); + + // Check that all edges are still valid. The comparers will panic on any errors. + for datum in data.iter() { + datum.edge.check_revision(graph)?; + } + + data.sort_by(|a, b| self.apply_edge_ordering(graph, &comparer, &a.edge, &b.edge)); + Ok(()) } - pub fn sort_traversal_data(&self, graph: &NoteGraph, edges: &mut [RecTraversalData]) { + pub fn sort_flat_traversal_data( + &self, + graph: &NoteGraph, + edges: &[EdgeStruct], + data: &mut [usize], + ) -> Result<()> { let comparer = self.get_edge_comparer(graph); - edges.sort_by(|a, b| self.apply_edge_ordering(graph, &comparer, &a.edge, &b.edge)); + // Check that all edges are still valid. The comparers will panic on any errors. + for index in data.iter() { + edges[*index].check_revision(graph)?; + } + + data.sort_by(|a, b| self.apply_edge_ordering(graph, &comparer, &edges[*a], &edges[*b])); + Ok(()) } fn get_edge_comparer<'a>(&self, graph: &'a NoteGraph) -> Comparer<'a> { @@ -141,7 +174,9 @@ pub struct PathComparer; impl EdgeComparer for PathComparer { fn compare(&self, graph: &NoteGraph, a: &EdgeStruct, b: &EdgeStruct) -> std::cmp::Ordering { - a.target_path_ref(graph).cmp(b.target_path_ref(graph)) + a.target_path_ref(graph) + .unwrap() + .cmp(b.target_path_ref(graph).unwrap()) } } @@ -150,8 +185,8 @@ pub struct BasenameComparer; impl EdgeComparer for BasenameComparer { fn compare(&self, graph: &NoteGraph, a: &EdgeStruct, b: &EdgeStruct) -> std::cmp::Ordering { - let a_target = a.target_path_ref(graph); - let b_target = b.target_path_ref(graph); + let a_target = a.target_path_ref(graph).unwrap(); + let b_target = b.target_path_ref(graph).unwrap(); let a_basename = a_target.split('/').last().unwrap(); let b_basename = b_target.split('/').last().unwrap(); @@ -173,9 +208,11 @@ pub struct ImpliedComparer; impl EdgeComparer for ImpliedComparer { fn compare(&self, graph: &NoteGraph, a: &EdgeStruct, b: &EdgeStruct) -> std::cmp::Ordering { - if a.explicit(graph) == b.explicit(graph) { - a.target_path_ref(graph).cmp(b.target_path_ref(graph)) - } else if a.explicit(graph) { + if a.explicit(graph).unwrap() == b.explicit(graph).unwrap() { + a.target_path_ref(graph) + .unwrap() + .cmp(b.target_path_ref(graph).unwrap()) + } else if a.explicit(graph).unwrap() { std::cmp::Ordering::Less } else { std::cmp::Ordering::Greater @@ -223,7 +260,10 @@ impl EdgeComparer for NeighbourComparer<'_> { (Some(a_neighbour), Some(b_neighbour)) => a_neighbour.path.cmp(&b_neighbour.path), (Some(_), None) => std::cmp::Ordering::Less, (None, Some(_)) => std::cmp::Ordering::Greater, - (None, None) => a.target_path_ref(graph).cmp(b.target_path_ref(graph)), + (None, None) => a + .target_path_ref(graph) + .unwrap() + .cmp(b.target_path_ref(graph).unwrap()), } } } diff --git a/wasm/src/graph.rs b/wasm/src/graph.rs index 26453a7f..06b362c5 100644 --- a/wasm/src/graph.rs +++ b/wasm/src/graph.rs @@ -46,6 +46,9 @@ pub struct NoteGraph { #[wasm_bindgen(skip)] pub node_hash: HashMap, update_callback: Option, + /// A revision number that is incremented after every update. + /// This can be used to check if the graph has changed. + revision: u32, } #[wasm_bindgen] @@ -57,6 +60,7 @@ impl NoteGraph { edge_types: VecSet::empty(), node_hash: HashMap::new(), update_callback: None, + revision: 0, } } @@ -129,6 +133,7 @@ impl NoteGraph { self.int_rebuild_edge_type_tracker(); self.int_build_implied_edges(&mut perf_logger); + self.revision += 1; perf_logger.start_split("Update notification callback".to_owned()); @@ -172,7 +177,7 @@ impl NoteGraph { match node_index { Some(node_index) => self .int_iter_outgoing_edges(node_index) - .map(|edge| EdgeStruct::from_edge_ref(edge)) + .map(|edge| EdgeStruct::from_edge_ref(edge, self)) .collect(), None => Vec::new(), } @@ -192,7 +197,7 @@ impl NoteGraph { .filter(|edge_ref| { edge_matches_edge_filter_string(edge_ref.weight(), edge_types.as_ref()) }) - .map(|edge| EdgeStruct::from_edge_ref(edge)) + .map(|edge| EdgeStruct::from_edge_ref(edge, self)) .collect(), None => Vec::new(), }) @@ -205,7 +210,7 @@ impl NoteGraph { match node_index { Some(node_index) => self .int_iter_incoming_edges(node_index) - .map(|edge| EdgeStruct::from_edge_ref(edge)) + .map(|edge| EdgeStruct::from_edge_ref(edge, self)) .collect(), None => Vec::new(), } @@ -250,6 +255,10 @@ impl Default for NoteGraph { /// Internal methods, not exposed to the wasm interface. /// All of these methods are prefixed with `int_`. impl NoteGraph { + pub fn get_revision(&self) -> u32 { + self.revision + } + /// Builds the implied edges based on the transitive rules. pub fn int_build_implied_edges(&mut self, perf_logger: &mut PerfLogger) { let perf_split = perf_logger.start_split("Building Implied Edges".to_owned()); diff --git a/wasm/src/graph_data.rs b/wasm/src/graph_data.rs index f092255c..131b80c4 100644 --- a/wasm/src/graph_data.rs +++ b/wasm/src/graph_data.rs @@ -12,6 +12,7 @@ use crate::{ edge_sorting::EdgeSorter, graph::{edge_matches_edge_filter_string, NoteGraph}, graph_construction::GCNodeData, + utils::{NoteGraphError, Result}, }; pub type NGEdgeIndex = EdgeIndex; @@ -190,44 +191,54 @@ pub struct EdgeStruct { pub edge_index: NGEdgeIndex, #[wasm_bindgen(skip)] pub edge_type: Rc, + /// refers to the revision of the graph when this edge was created + revision: u32, } #[wasm_bindgen] impl EdgeStruct { - pub fn source_data(&self, graph: &NoteGraph) -> NodeData { - self.source_data_ref(graph).clone() + pub fn source_data(&self, graph: &NoteGraph) -> Result { + Ok(self.source_data_ref(graph)?.clone()) } - pub fn target_data(&self, graph: &NoteGraph) -> NodeData { - self.target_data_ref(graph).clone() + pub fn target_data(&self, graph: &NoteGraph) -> Result { + Ok(self.target_data_ref(graph)?.clone()) } - pub fn source_path(&self, graph: &NoteGraph) -> String { - self.source_data_ref(graph).path.clone() + pub fn source_path(&self, graph: &NoteGraph) -> Result { + Ok(self.source_data_ref(graph)?.path.clone()) } - pub fn target_path(&self, graph: &NoteGraph) -> String { - self.target_data_ref(graph).path.clone() + pub fn target_path(&self, graph: &NoteGraph) -> Result { + Ok(self.target_data_ref(graph)?.path.clone()) } - pub fn source_resolved(&self, graph: &NoteGraph) -> bool { - self.source_data_ref(graph).resolved + pub fn source_resolved(&self, graph: &NoteGraph) -> Result { + Ok(self.source_data_ref(graph)?.resolved) } - pub fn target_resolved(&self, graph: &NoteGraph) -> bool { - self.target_data_ref(graph).resolved + pub fn target_resolved(&self, graph: &NoteGraph) -> Result { + Ok(self.target_data_ref(graph)?.resolved) } - pub fn stringify_target(&self, graph: &NoteGraph, options: &NodeStringifyOptions) -> String { - options.stringify_node(self.target_data_ref(graph)) + pub fn stringify_target( + &self, + graph: &NoteGraph, + options: &NodeStringifyOptions, + ) -> Result { + Ok(options.stringify_node(self.target_data_ref(graph)?)) } - pub fn stringify_source(&self, graph: &NoteGraph, options: &NodeStringifyOptions) -> String { - options.stringify_node(self.source_data_ref(graph)) + pub fn stringify_source( + &self, + graph: &NoteGraph, + options: &NodeStringifyOptions, + ) -> Result { + Ok(options.stringify_node(self.source_data_ref(graph)?)) } - pub fn edge_data(&self, graph: &NoteGraph) -> EdgeData { - graph.graph.edge_weight(self.edge_index).unwrap().clone() + pub fn edge_data(&self, graph: &NoteGraph) -> Result { + Ok(self.edge_data_ref(graph)?.clone()) } #[wasm_bindgen(getter)] @@ -235,24 +246,35 @@ impl EdgeStruct { self.edge_type.to_string() } - pub fn edge_source(&self, graph: &NoteGraph) -> String { - self.edge_data_ref(graph).get_edge_source() + pub fn edge_source(&self, graph: &NoteGraph) -> Result { + Ok(self.edge_data_ref(graph)?.get_edge_source()) } - pub fn explicit(&self, graph: &NoteGraph) -> bool { - self.edge_data_ref(graph).explicit + pub fn explicit(&self, graph: &NoteGraph) -> Result { + Ok(self.edge_data_ref(graph)?.explicit) } - pub fn round(&self, graph: &NoteGraph) -> u8 { - self.edge_data_ref(graph).round + pub fn round(&self, graph: &NoteGraph) -> Result { + Ok(self.edge_data_ref(graph)?.round) } - pub fn get_attribute_label(&self, graph: &NoteGraph, attributes: Vec) -> String { - self.edge_data_ref(graph).get_attribute_label(&attributes) + pub fn get_attribute_label( + &self, + graph: &NoteGraph, + attributes: Vec, + ) -> Result { + Ok(self.edge_data_ref(graph)?.get_attribute_label(&attributes)) } - pub fn matches_edge_filter(&self, graph: &NoteGraph, edge_types: Option>) -> bool { - edge_matches_edge_filter_string(self.edge_data_ref(graph), edge_types.as_ref()) + pub fn matches_edge_filter( + &self, + graph: &NoteGraph, + edge_types: Option>, + ) -> Result { + Ok(edge_matches_edge_filter_string( + self.edge_data_ref(graph)?, + edge_types.as_ref(), + )) } pub fn is_self_loop(&self) -> bool { @@ -271,16 +293,18 @@ impl EdgeStruct { target_index: NGNodeIndex, edge_index: NGEdgeIndex, edge_type: Rc, + revision: u32, ) -> EdgeStruct { EdgeStruct { source_index, target_index, edge_index, edge_type, + revision, } } - pub fn from_edge_ref(edge_ref: NGEdgeRef) -> EdgeStruct { + pub fn from_edge_ref(edge_ref: NGEdgeRef, graph: &NoteGraph) -> EdgeStruct { let source_index = edge_ref.source(); let target_index = edge_ref.target(); @@ -289,13 +313,14 @@ impl EdgeStruct { target_index, edge_ref.id(), Rc::clone(&edge_ref.weight().edge_type), + graph.get_revision(), ) } pub fn from_edge_data( edge_index: NGEdgeIndex, edge_data: &EdgeData, - graph: &crate::graph::NoteGraph, + graph: &NoteGraph, ) -> Option { let (source_index, target_index) = graph.graph.edge_endpoints(edge_index)?; @@ -304,27 +329,51 @@ impl EdgeStruct { target_index, edge_index, Rc::clone(&edge_data.edge_type), + graph.get_revision(), )) } - pub fn edge_data_ref<'a>(&self, graph: &'a NoteGraph) -> &'a EdgeData { - graph.graph.edge_weight(self.edge_index).unwrap() + pub fn edge_data_ref<'a>(&self, graph: &'a NoteGraph) -> Result<&'a EdgeData> { + self.check_revision(graph)?; + graph + .graph + .edge_weight(self.edge_index) + .ok_or(NoteGraphError::new("Edge not found")) } - pub fn source_data_ref<'a>(&self, graph: &'a NoteGraph) -> &'a NodeData { - graph.graph.node_weight(self.source_index).unwrap() + pub fn source_data_ref<'a>(&self, graph: &'a NoteGraph) -> Result<&'a NodeData> { + self.check_revision(graph)?; + graph + .graph + .node_weight(self.source_index) + .ok_or(NoteGraphError::new("Source node not found")) } - pub fn target_data_ref<'a>(&self, graph: &'a NoteGraph) -> &'a NodeData { - graph.graph.node_weight(self.target_index).unwrap() + pub fn target_data_ref<'a>(&self, graph: &'a NoteGraph) -> Result<&'a NodeData> { + self.check_revision(graph)?; + graph + .graph + .node_weight(self.target_index) + .ok_or(NoteGraphError::new("Source node not found")) } - pub fn target_path_ref<'a>(&self, graph: &'a NoteGraph) -> &'a str { - &self.target_data_ref(graph).path + pub fn target_path_ref<'a>(&self, graph: &'a NoteGraph) -> Result<&'a str> { + Ok(&self.target_data_ref(graph)?.path) } - pub fn source_path_ref<'a>(&self, graph: &'a NoteGraph) -> &'a str { - &self.source_data_ref(graph).path + pub fn source_path_ref<'a>(&self, graph: &'a NoteGraph) -> Result<&'a str> { + Ok(&self.source_data_ref(graph)?.path) + } + + pub fn check_revision(&self, graph: &NoteGraph) -> Result<()> { + match graph.get_revision() == self.revision { + true => Ok(()), + false => Err(NoteGraphError::new(&format!( + "Revision mismatch. Edge was created in revision {}, but current revision is {}", + self.revision, + graph.get_revision() + ))), + } } } @@ -341,11 +390,15 @@ impl EdgeList { self.edges.clone() } - pub fn get_sorted_edges(&self, graph: &NoteGraph, sorter: &EdgeSorter) -> Vec { + pub fn get_sorted_edges( + &self, + graph: &NoteGraph, + sorter: &EdgeSorter, + ) -> Result> { let mut edges = self.edges.clone(); - sorter.sort_edges(graph, &mut edges); + sorter.sort_edges(graph, &mut edges)?; - edges + Ok(edges) } pub fn group_by_type(&self) -> GroupedEdgeList { @@ -394,10 +447,13 @@ impl GroupedEdgeList { edge_type: &str, graph: &NoteGraph, sorter: &EdgeSorter, - ) -> Option> { - self.edges - .get(edge_type) - .map(|edge_list| edge_list.get_sorted_edges(graph, sorter)) + ) -> Result>> { + let edges = self.edges.get(edge_type); + + match edges { + Some(edge_list) => Ok(Some(edge_list.get_sorted_edges(graph, sorter)?)), + None => Ok(None), + } } #[wasm_bindgen(js_name = toString)] diff --git a/wasm/src/graph_mermaid.rs b/wasm/src/graph_mermaid.rs index 2b690a64..d56508fd 100644 --- a/wasm/src/graph_mermaid.rs +++ b/wasm/src/graph_mermaid.rs @@ -114,11 +114,11 @@ impl NoteGraph { let (nodes, edges) = self.int_traverse_basic(&traversal_options)?; let mut edge_structs = edges .iter() - .map(|edge| EdgeStruct::from_edge_ref(edge.1)) + .map(|edge| EdgeStruct::from_edge_ref(edge.1, self)) .collect::>(); if let Some(edge_sorter) = &diagram_options.edge_sorter { - edge_sorter.sort_edges(self, &mut edge_structs); + edge_sorter.sort_edges(self, &mut edge_structs)?; } // utils::log(format!("{:#?}", nodes)); @@ -139,7 +139,7 @@ impl NoteGraph { ); // accumulate edges by direction, so that we can collapse them in the next step - let accumulated_edges = NoteGraph::int_accumulate_edges(self, edge_structs); + let accumulated_edges = NoteGraph::int_accumulate_edges(self, edge_structs)?; // utils::log(format!("{:#?}", accumulated_edges)); @@ -299,18 +299,20 @@ impl NoteGraph { pub fn int_accumulate_edges( graph: &NoteGraph, edges: Vec, - ) -> AccumulatedEdgeHashMap<'_> { + ) -> Result> { let mut accumulated_edges = AccumulatedEdgeHashMap::default(); // sorting the two node indices in the edge tuple could be a speedup, since then only one lookup is needed for edge_struct in edges { + edge_struct.check_revision(graph)?; + let forward_dir = (edge_struct.source_index, edge_struct.target_index); let entry1 = accumulated_edges.map.get_mut(&forward_dir); match entry1 { Some((_, _, forward, _)) => { - forward.push(edge_struct.edge_data_ref(graph)); + forward.push(edge_struct.edge_data_ref(graph).unwrap()); } None => { let backward_dir = (edge_struct.target_index, edge_struct.source_index); @@ -318,7 +320,7 @@ impl NoteGraph { let entry2 = accumulated_edges.map.get_mut(&backward_dir); match entry2 { Some((_, _, _, backward)) => { - backward.push(edge_struct.edge_data_ref(graph)); + backward.push(edge_struct.edge_data_ref(graph).unwrap()); } None => { accumulated_edges.map.insert( @@ -326,7 +328,7 @@ impl NoteGraph { ( edge_struct.source_index, edge_struct.target_index, - vec![edge_struct.edge_data_ref(graph)], + vec![edge_struct.edge_data_ref(graph).unwrap()], Vec::new(), ), ); @@ -336,6 +338,6 @@ impl NoteGraph { } } - accumulated_edges + Ok(accumulated_edges) } } diff --git a/wasm/src/graph_traversal.rs b/wasm/src/graph_traversal.rs index d87b546c..cf21bad2 100644 --- a/wasm/src/graph_traversal.rs +++ b/wasm/src/graph_traversal.rs @@ -58,6 +58,28 @@ impl TraversalOptions { } } +#[wasm_bindgen] +#[derive(Clone, Debug)] +pub struct TraversalPostprocessOptions { + #[wasm_bindgen(getter_with_clone)] + pub sorter: Option, + #[wasm_bindgen(getter_with_clone)] + pub flatten: bool, +} + +#[wasm_bindgen] +impl TraversalPostprocessOptions { + #[wasm_bindgen(constructor)] + pub fn new(sorter: Option, flatten: bool) -> TraversalPostprocessOptions { + TraversalPostprocessOptions { sorter, flatten } + } + + #[wasm_bindgen(js_name = toString)] + pub fn to_fancy_string(&self) -> String { + format!("{:#?}", self) + } +} + #[wasm_bindgen] #[derive(Clone, Debug, PartialEq)] pub struct Path { @@ -86,8 +108,12 @@ impl Path { self.edges == other.edges } - pub fn get_first_target(&self, graph: &NoteGraph) -> Option { - self.edges.first().map(|edge| edge.target_path(graph)) + pub fn get_first_target(&self, graph: &NoteGraph) -> Result> { + let first = self.edges.first(); + match first { + Some(edge) => Ok(Some(edge.target_path(graph)?)), + None => Ok(None), + } } #[wasm_bindgen(js_name = toString)] @@ -139,20 +165,33 @@ impl PathList { .unwrap_or(0) } - pub fn process(&self, graph: &NoteGraph, depth: usize) -> Vec { - self.paths + pub fn process(&self, graph: &NoteGraph, depth: usize) -> Result> { + let paths = self + .paths .iter() .map(|path| path.truncate(depth)) + .collect_vec(); + + for path in &paths { + for edge in &path.edges { + edge.check_revision(graph)?; + } + } + + Ok(paths + .into_iter() .sorted_by(|a, b| { let a_len = a.edges.len(); let b_len = b.edges.len(); - a_len - .cmp(&b_len) - .then_with(|| a.get_first_target(graph).cmp(&b.get_first_target(graph))) + a_len.cmp(&b_len).then_with(|| { + a.get_first_target(graph) + .unwrap() + .cmp(&b.get_first_target(graph).unwrap()) + }) }) .dedup() - .collect_vec() + .collect_vec()) } } @@ -181,7 +220,7 @@ impl PathList { #[wasm_bindgen] #[derive(Clone, Debug)] -pub struct RecTraversalData { +pub struct TraversalData { /// the edge struct that was traversed #[wasm_bindgen(getter_with_clone)] pub edge: EdgeStruct, @@ -191,19 +230,19 @@ pub struct RecTraversalData { pub number_of_children: u32, /// the children of the node #[wasm_bindgen(getter_with_clone)] - pub children: Vec, + pub children: Vec, } #[wasm_bindgen] -impl RecTraversalData { +impl TraversalData { #[wasm_bindgen(constructor)] pub fn new( edge: EdgeStruct, depth: u32, number_of_children: u32, - children: Vec, - ) -> RecTraversalData { - RecTraversalData { + children: Vec, + ) -> TraversalData { + TraversalData { edge, depth, number_of_children, @@ -211,12 +250,12 @@ impl RecTraversalData { } } - pub fn rec_sort_children(&mut self, graph: &NoteGraph, sorter: &EdgeSorter) { + pub fn rec_sort_children(&mut self, graph: &NoteGraph, sorter: &EdgeSorter) -> Result<()> { for child in &mut self.children { - child.rec_sort_children(graph, sorter); + child.rec_sort_children(graph, sorter)?; } - sorter.sort_traversal_data(graph, &mut self.children); + sorter.sort_traversal_data(graph, &mut self.children) } #[wasm_bindgen(js_name = toString)] @@ -225,7 +264,7 @@ impl RecTraversalData { } } -impl RecTraversalData { +impl TraversalData { fn to_paths(&self) -> Vec { let mut paths = Vec::new(); @@ -249,24 +288,24 @@ impl RecTraversalData { #[wasm_bindgen] #[derive(Clone, Debug)] -pub struct RecTraversalResult { +pub struct TraversalResult { #[wasm_bindgen(getter_with_clone)] - pub data: Vec, + pub data: Vec, pub node_count: u32, pub max_depth: u32, pub traversal_time: u64, } #[wasm_bindgen] -impl RecTraversalResult { +impl TraversalResult { #[wasm_bindgen(constructor)] pub fn new( - data: Vec, + data: Vec, node_count: u32, max_depth: u32, traversal_time: u64, - ) -> RecTraversalResult { - RecTraversalResult { + ) -> TraversalResult { + TraversalResult { data, node_count, max_depth, @@ -299,34 +338,40 @@ impl RecTraversalResult { PathList::new(paths) } +} +impl TraversalResult { /// Flattens the traversal data by removing the tree structure and deduplicating the edges by their target_path - pub fn flatten(&mut self, graph: &NoteGraph) { + pub fn flatten(&mut self, graph: &NoteGraph) -> Result<()> { let mut data = Vec::new(); for datum in self.data.drain(..) { rec_flatten_traversal_data(datum, &mut data); } - data.dedup_by(|a, b| a.edge.target_path(graph) == b.edge.target_path(graph)); + for datum in &data { + datum.edge.check_revision(graph)?; + } + + data.dedup_by(|a, b| { + a.edge.target_path(graph).unwrap() == b.edge.target_path(graph).unwrap() + }); self.data = data; + + Ok(()) } - pub fn sort(&mut self, graph: &NoteGraph, sorter: &EdgeSorter) { + pub fn sort(&mut self, graph: &NoteGraph, sorter: &EdgeSorter) -> Result<()> { for datum in &mut self.data { - datum.rec_sort_children(graph, sorter); + datum.rec_sort_children(graph, sorter)?; } - sorter.sort_traversal_data(graph, &mut self.data); - } - - pub fn to_flat(&self) -> FlatRecTraversalResult { - FlatRecTraversalResult::from_rec_traversal_result(self.clone()) + sorter.sort_traversal_data(graph, &mut self.data) } } -fn rec_flatten_traversal_data(mut data: RecTraversalData, result: &mut Vec) { +fn rec_flatten_traversal_data(mut data: TraversalData, result: &mut Vec) { for child in data.children.drain(..) { rec_flatten_traversal_data(child, result); } @@ -336,7 +381,7 @@ fn rec_flatten_traversal_data(mut data: RecTraversalData, result: &mut Vec, } -impl FlatRecTraversalData { +impl FlatTraversalData { pub fn new( edge: EdgeStruct, depth: u32, number_of_children: u32, children: Vec, - ) -> FlatRecTraversalData { - FlatRecTraversalData { + ) -> FlatTraversalData { + FlatTraversalData { edge, depth, number_of_children, @@ -366,17 +411,21 @@ impl FlatRecTraversalData { } #[wasm_bindgen] -impl FlatRecTraversalData { - pub fn get_attribute_label(&self, graph: &NoteGraph, attributes: Vec) -> String { +impl FlatTraversalData { + pub fn get_attribute_label( + &self, + graph: &NoteGraph, + attributes: Vec, + ) -> Result { self.edge.get_attribute_label(graph, attributes) } } #[wasm_bindgen] #[derive(Clone, Debug)] -pub struct FlatRecTraversalResult { +pub struct FlatTraversalResult { #[wasm_bindgen(getter_with_clone)] - pub data: Vec, + pub data: Vec, pub node_count: u32, pub max_depth: u32, pub traversal_time: u64, @@ -384,15 +433,15 @@ pub struct FlatRecTraversalResult { pub entry_nodes: Vec, } -impl FlatRecTraversalResult { +impl FlatTraversalResult { pub fn new( - data: Vec, + data: Vec, node_count: u32, max_depth: u32, traversal_time: u64, entry_nodes: Vec, - ) -> FlatRecTraversalResult { - FlatRecTraversalResult { + ) -> FlatTraversalResult { + FlatTraversalResult { data, node_count, max_depth, @@ -401,7 +450,7 @@ impl FlatRecTraversalResult { } } - pub fn from_rec_traversal_result(result: RecTraversalResult) -> FlatRecTraversalResult { + pub fn from_rec_traversal_result(result: TraversalResult) -> FlatTraversalResult { let mut flat_data = Vec::new(); let mut entry_nodes = Vec::new(); @@ -409,7 +458,7 @@ impl FlatRecTraversalResult { entry_nodes.push(rec_flatten_traversal_data_to_flat(datum, &mut flat_data)); } - FlatRecTraversalResult::new( + FlatTraversalResult::new( flat_data, result.node_count, result.max_depth, @@ -420,7 +469,7 @@ impl FlatRecTraversalResult { } #[wasm_bindgen] -impl FlatRecTraversalResult { +impl FlatTraversalResult { #[wasm_bindgen(js_name = toString)] pub fn to_fancy_string(&self) -> String { format!("{:#?}", self) @@ -430,14 +479,28 @@ impl FlatRecTraversalResult { self.data.is_empty() } - pub fn data_at_index(&self, index: usize) -> Option { + pub fn data_at_index(&self, index: usize) -> Option { self.data.get(index).cloned() } + + pub fn sort(&mut self, graph: &NoteGraph, sorter: &EdgeSorter) -> Result<()> { + let cloned_edges = self.data.iter() + .map(|datum| datum.edge.clone()).collect_vec(); + + + for datum in &mut self.data { + sorter.sort_flat_traversal_data(graph, &cloned_edges, &mut datum.children)?; + } + + sorter.sort_flat_traversal_data(graph, &cloned_edges, &mut self.entry_nodes)?; + + Ok(()) + } } fn rec_flatten_traversal_data_to_flat( - mut data: RecTraversalData, - result: &mut Vec, + mut data: TraversalData, + result: &mut Vec, ) -> usize { let children = data .children @@ -445,7 +508,7 @@ fn rec_flatten_traversal_data_to_flat( .map(|datum| rec_flatten_traversal_data_to_flat(datum, result)) .collect(); - result.push(FlatRecTraversalData::new( + result.push(FlatTraversalData::new( data.edge, data.depth, data.number_of_children, @@ -456,7 +519,8 @@ fn rec_flatten_traversal_data_to_flat( #[wasm_bindgen] impl NoteGraph { - pub fn rec_traverse(&self, options: TraversalOptions) -> Result { + /// Runs a recursive traversal of the graph. + pub fn rec_traverse(&self, options: TraversalOptions) -> Result { let now = Instant::now(); let mut result = Vec::new(); @@ -487,6 +551,7 @@ impl NoteGraph { target, edge.id(), edge.weight().edge_type.clone(), + self.get_revision(), ); traversal_count += 1; @@ -518,13 +583,29 @@ impl NoteGraph { let total_elapsed = now.elapsed(); - Ok(RecTraversalResult::new( + Ok(TraversalResult::new( result, node_count, max_depth, total_elapsed.as_millis() as u64, )) } + + /// Runs a recursive traversal of the graph and post-processes the result. + /// The post-processed result is more efficient to work with from JavaScript. + pub fn rec_traverse_and_process(&self, options: TraversalOptions, postprocess_options: TraversalPostprocessOptions) -> Result { + let mut result = self.rec_traverse(options)?; + + if postprocess_options.flatten { + result.flatten(self)?; + } + + if let Some(sorter) = &postprocess_options.sorter { + result.sort(self, sorter)?; + } + + Ok(FlatTraversalResult::from_rec_traversal_result(result)) + } } impl NoteGraph { @@ -539,7 +620,7 @@ impl NoteGraph { depth: u32, max_depth: u32, traversal_count: &mut u32, - ) -> Result { + ) -> Result { let mut new_children = Vec::new(); if depth < max_depth { @@ -556,6 +637,7 @@ impl NoteGraph { target, outgoing_edge.id(), edge_data.edge_type.clone(), + self.get_revision(), ); *traversal_count += 1; @@ -576,10 +658,10 @@ impl NoteGraph { } } - Ok(RecTraversalData::new(edge, depth, 0, new_children)) + Ok(TraversalData::new(edge, depth, 0, new_children)) } - pub fn int_rec_traversal_data_count_children(data: &mut [RecTraversalData]) -> u32 { + pub fn int_rec_traversal_data_count_children(data: &mut [TraversalData]) -> u32 { let mut total_children = 0; for datum in data.iter_mut() { @@ -591,7 +673,7 @@ impl NoteGraph { total_children } - pub fn int_rec_traversal_data_max_depth(data: &[RecTraversalData]) -> u32 { + pub fn int_rec_traversal_data_max_depth(data: &[TraversalData]) -> u32 { data.iter() .map(|datum| { u32::max( From 26c2c5f211ebf64ec4c8874b6f8218630b032c21 Mon Sep 17 00:00:00 2001 From: Moritz Jung Date: Mon, 6 Jan 2025 23:11:56 +0100 Subject: [PATCH 51/65] migration to Svelte 5 --- package.json | 2 +- src/codeblocks/MDRC.ts | 63 ++++++++++--------- src/commands/init.ts | 15 ++--- src/components/EdgeLink.svelte | 17 +++-- src/components/NestedEdgeList.svelte | 49 ++++++++++----- src/components/ObsidianLink.svelte | 34 ++++++---- .../button/ChevronCollapseButton.svelte | 10 ++- src/components/button/ChevronOpener.svelte | 6 +- .../button/CopyToClipboardButton.svelte | 21 +++++-- .../button/MergeFieldsButton.svelte | 10 ++- .../button/RebuildGraphButton.svelte | 10 ++- .../codeblocks/CodeblockErrors.svelte | 8 ++- .../codeblocks/CodeblockMermaid.svelte | 25 +++++--- .../codeblocks/CodeblockTree.svelte | 21 +++++-- src/components/input/SimpleInput.svelte | 12 ++-- .../obsidian/RenderExternalCodeblock.svelte | 20 ++++-- src/components/obsidian/RenderMarkdown.svelte | 25 ++++++-- src/components/obsidian/TreeItemFlair.svelte | 10 ++- src/components/obsidian/tag.svelte | 23 ++++--- src/components/page_views/PrevNextView.svelte | 8 ++- src/components/page_views/TrailView.svelte | 41 +++++++----- .../page_views/TrailViewGrid.svelte | 8 ++- .../page_views/TrailViewPath.svelte | 8 ++- src/components/page_views/index.svelte | 11 +++- .../selector/EdgeFieldSelector.svelte | 12 ++-- .../selector/EdgeSortIdSelector.svelte | 12 ++-- .../selector/FieldGroupLabelsSelector.svelte | 12 ++-- .../ShowAttributesSelectorMenu.svelte | 12 ++-- .../settings/EdgeFieldSettings.svelte | 38 ++++++----- .../settings/EdgeSortIdSettingItem.svelte | 16 +++-- .../FieldGroupLabelsSettingItem.svelte | 28 ++++++--- src/components/settings/SettingItem.svelte | 11 +++- .../settings/ShowAttributesSettingItem.svelte | 18 ++++-- .../TransitiveImpliedRelations.svelte | 40 ++++++------ src/components/side_views/Matrix.svelte | 20 +++--- .../side_views/MatrixEdgeField.svelte | 27 +++++--- src/modals/CreateListIndexModal.ts | 31 ++++----- src/settings/GridSettings.ts | 17 ++--- src/settings/ListIndexSettings.ts | 45 ++++++------- src/settings/MatrixSettings.ts | 39 ++++++------ src/settings/PrevNextSettings.ts | 45 ++++++------- src/settings/SettingsTab.ts | 25 ++++---- src/settings/TreeViewSettings.ts | 37 +++++------ src/views/matrix.ts | 9 +-- src/views/page.ts | 9 +-- src/views/tree.ts | 9 +-- 46 files changed, 600 insertions(+), 369 deletions(-) diff --git a/package.json b/package.json index 8b8bcf34..e747fdd3 100644 --- a/package.json +++ b/package.json @@ -49,7 +49,7 @@ "svelte-preprocess": "^6.0.3", "tailwindcss": "^3.4.1", "tslib": "2.6.2", - "typescript": "^5.3.3", + "typescript": "^5.5.0", "vitest": "^1.3.1" }, "dependencies": { diff --git a/src/codeblocks/MDRC.ts b/src/codeblocks/MDRC.ts index 5e155028..7b83bbf4 100644 --- a/src/codeblocks/MDRC.ts +++ b/src/codeblocks/MDRC.ts @@ -8,6 +8,7 @@ import type BreadcrumbsPlugin from "src/main"; import { Timer } from "src/utils/timer"; import { Codeblocks } from "."; import { BCEvent } from "src/main"; +import { mount } from "svelte"; export class CodeblockMDRC extends MarkdownRenderChild { source: string; @@ -61,10 +62,10 @@ export class CodeblockMDRC extends MarkdownRenderChild { if (!parsed) { log.warn("fatal codeblock errors", errors); - new CodeblockErrors({ - target: this.containerEl, - props: { errors, plugin: this.plugin }, - }); + mount(CodeblockErrors, { + target: this.containerEl, + props: { errors, plugin: this.plugin }, + }); return; } @@ -86,35 +87,35 @@ export class CodeblockMDRC extends MarkdownRenderChild { if (errors.length) log.warn("non-fatal codeblock errors", errors); if (options.type === "tree") { - this.component = new CodeblockTree({ - target: this.containerEl, - props: { - errors, - options, - file_path, - plugin: this.plugin, - }, - }); + this.component = mount(CodeblockTree, { + target: this.containerEl, + props: { + errors, + options, + file_path, + plugin: this.plugin, + }, + }); } else if (options.type === "mermaid") { - this.component = new CodeblockMermaid({ - target: this.containerEl, - props: { - errors, - options, - file_path, - plugin: this.plugin, - }, - }); + this.component = mount(CodeblockMermaid, { + target: this.containerEl, + props: { + errors, + options, + file_path, + plugin: this.plugin, + }, + }); } else if (options.type === "markmap") { - this.component = new CodeblockMarkmap({ - target: this.containerEl, - props: { - errors, - options, - file_path, - plugin: this.plugin, - }, - }); + this.component = mount(CodeblockMarkmap, { + target: this.containerEl, + props: { + errors, + options, + file_path, + plugin: this.plugin, + }, + }); } else { log.error("CodeblockMDRC unknown type", options.type); } diff --git a/src/commands/init.ts b/src/commands/init.ts index 56df63ba..41881533 100644 --- a/src/commands/init.ts +++ b/src/commands/init.ts @@ -12,6 +12,7 @@ import { freeze_implied_edges_to_note } from "./freeze_edges"; import { jump_to_neighbour } from "./jump"; import { get_graph_stats } from "./stats"; import { thread } from "./thread"; +import { mount } from "svelte"; export const init_all_commands = (plugin: BreadcrumbsPlugin) => { plugin.addCommand({ @@ -91,13 +92,13 @@ export const init_all_commands = (plugin: BreadcrumbsPlugin) => { const PROMPT_TARGET = "FREEZE TO VAULT"; new GenericModal(plugin.app, (modal) => { - new SimpleInput({ - target: modal.contentEl, - props: { - label: `Type '${PROMPT_TARGET}' to confirm`, - disabled_cb: (value: string) => value !== PROMPT_TARGET, - }, - }).$on("submit", async (e) => { + mount(SimpleInput, { + target: modal.contentEl, + props: { + label: `Type '${PROMPT_TARGET}' to confirm`, + disabled_cb: (value: string) => value !== PROMPT_TARGET, + }, + }).$on("submit", async (e) => { if (e.detail !== PROMPT_TARGET) { new Notice("Command cancelled"); } else { diff --git a/src/components/EdgeLink.svelte b/src/components/EdgeLink.svelte index ac7f12f5..74b48a7d 100644 --- a/src/components/EdgeLink.svelte +++ b/src/components/EdgeLink.svelte @@ -3,10 +3,19 @@ import BreadcrumbsPlugin from "src/main"; import type { EdgeStruct, NodeStringifyOptions } from "wasm/pkg/breadcrumbs_graph_wasm"; - export let edge: EdgeStruct; - export let plugin: BreadcrumbsPlugin; - export let node_stringify_options: NodeStringifyOptions; - export let cls = ""; + interface Props { + edge: EdgeStruct; + plugin: BreadcrumbsPlugin; + node_stringify_options: NodeStringifyOptions; + cls?: string; + } + + let { + edge, + plugin, + node_stringify_options, + cls = "" + }: Props = $props(); const display = edge.stringify_target(plugin.graph, node_stringify_options); diff --git a/src/components/NestedEdgeList.svelte b/src/components/NestedEdgeList.svelte index b02b1022..71d47128 100644 --- a/src/components/NestedEdgeList.svelte +++ b/src/components/NestedEdgeList.svelte @@ -1,4 +1,7 @@ @@ -61,7 +78,7 @@ {#if children.length}
- - - - + + + { + onmouseover={(event) => { // SOURCE: https://discord.com/channels/686053708261228577/840286264964022302/1225823461901860924 plugin.app.workspace.trigger("hover-link", { event, @@ -36,7 +46,7 @@ hoverParent: event.currentTarget.parentElement, }); }} - on:contextmenu={(e) => { + oncontextmenu={(e) => { const menu = new Menu(); // SOURCE: https://discord.com/channels/686053708261228577/840286264964022302/1225828755252052068 @@ -44,14 +54,14 @@ menu.showAtMouseEvent(e); }} - on:auxclick={(e) => { + onauxclick={(e) => { log.debug("on:auxclick e.button", e.button); if (e.button === 1) { plugin.app.workspace.openLinkText(path, "", "tab"); } }} - on:click={(e) => { + onclick={(e) => { // NOTE: We openLinkText from vault root, since it's a full path already plugin.app.workspace.openLinkText(path, "", Keymap.isModEvent(e)); }} diff --git a/src/components/button/ChevronCollapseButton.svelte b/src/components/button/ChevronCollapseButton.svelte index 3c10e600..e72c8f02 100644 --- a/src/components/button/ChevronCollapseButton.svelte +++ b/src/components/button/ChevronCollapseButton.svelte @@ -2,14 +2,18 @@ import { ChevronsDownUp, ChevronsUpDown } from "lucide-svelte"; import { ICON_SIZE } from "src/const"; - export let cls = ""; - export let collapse: boolean | null; + interface Props { + cls?: string; + collapse: boolean | null; + } + + let { cls = "", collapse = $bindable() }: Props = $props(); diff --git a/src/components/codeblocks/CodeblockErrors.svelte b/src/components/codeblocks/CodeblockErrors.svelte index 9e944392..780ec041 100644 --- a/src/components/codeblocks/CodeblockErrors.svelte +++ b/src/components/codeblocks/CodeblockErrors.svelte @@ -3,8 +3,12 @@ import type BreadcrumbsPlugin from "src/main"; import RenderMarkdown from "../obsidian/RenderMarkdown.svelte"; - export let plugin: BreadcrumbsPlugin; - export let errors: BreadcrumbsError[]; + interface Props { + plugin: BreadcrumbsPlugin; + errors: BreadcrumbsError[]; + } + + let { plugin, errors }: Props = $props(); const markdown = errors .map((e) => `- **\`${e.path}\`**: ${e.message}`) diff --git a/src/components/codeblocks/CodeblockMermaid.svelte b/src/components/codeblocks/CodeblockMermaid.svelte index 7f8d2726..ed53a250 100644 --- a/src/components/codeblocks/CodeblockMermaid.svelte +++ b/src/components/codeblocks/CodeblockMermaid.svelte @@ -22,15 +22,24 @@ import { Links } from "src/utils/links"; import { active_file_store } from "src/stores/active_file"; - export let plugin: BreadcrumbsPlugin; - export let options: ICodeblock["Options"]; - export let errors: BreadcrumbsError[]; - export let file_path: string; + interface Props { + plugin: BreadcrumbsPlugin; + options: ICodeblock["Options"]; + errors: BreadcrumbsError[]; + file_path: string; + } + + let { + plugin, + options, + errors, + file_path + }: Props = $props(); const DEFAULT_MAX_DEPTH = 10; - let code: string = ""; - let error: string | undefined = undefined; + let code: string = $state(""); + let error: string | undefined = $state(undefined); export const update = () => { const max_depth = options.depth[1] === Infinity ? DEFAULT_MAX_DEPTH : (options.depth[1] ?? DEFAULT_MAX_DEPTH); @@ -136,7 +145,7 @@ role="link" aria-label="View Image on mermaid.ink" class="clickable-icon nav-action-button" - on:click={() => { + onclick={() => { window.open(Mermaid.to_image_link(code), "_blank"); }} > @@ -147,7 +156,7 @@ role="link" aria-label="Live Edit on mermaid.live" class="clickable-icon nav-action-button" - on:click={() => { + onclick={() => { window.open(Mermaid.to_live_edit_link(code), "_blank"); }} > diff --git a/src/components/codeblocks/CodeblockTree.svelte b/src/components/codeblocks/CodeblockTree.svelte index 0b6682a0..4d1300a3 100644 --- a/src/components/codeblocks/CodeblockTree.svelte +++ b/src/components/codeblocks/CodeblockTree.svelte @@ -18,10 +18,19 @@ import CopyToClipboardButton from "../button/CopyToClipboardButton.svelte"; import CodeblockErrors from "./CodeblockErrors.svelte"; - export let plugin: BreadcrumbsPlugin; - export let options: ICodeblock["Options"]; - export let errors: BreadcrumbsError[]; - export let file_path: string; + interface Props { + plugin: BreadcrumbsPlugin; + options: ICodeblock["Options"]; + errors: BreadcrumbsError[]; + file_path: string; + } + + let { + plugin, + options, + errors, + file_path + }: Props = $props(); const sort = create_edge_sorter( options.sort.field, @@ -31,8 +40,8 @@ const DEFAULT_MAX_DEPTH = 10; - let data: FlatTraversalResult | undefined = undefined; - let error: string | undefined = undefined; + let data: FlatTraversalResult | undefined = $state(undefined); + let error: string | undefined = $state(undefined); export const update = () => { const max_depth = options.depth[1] === Infinity ? DEFAULT_MAX_DEPTH : (options.depth[1] ?? DEFAULT_MAX_DEPTH); diff --git a/src/components/input/SimpleInput.svelte b/src/components/input/SimpleInput.svelte index a3a76784..6d74a172 100644 --- a/src/components/input/SimpleInput.svelte +++ b/src/components/input/SimpleInput.svelte @@ -1,10 +1,14 @@ @@ -20,7 +24,7 @@ diff --git a/src/components/obsidian/RenderExternalCodeblock.svelte b/src/components/obsidian/RenderExternalCodeblock.svelte index cc00e20a..ee7532f3 100644 --- a/src/components/obsidian/RenderExternalCodeblock.svelte +++ b/src/components/obsidian/RenderExternalCodeblock.svelte @@ -3,11 +3,21 @@ import { wrap_in_codeblock } from "src/utils/strings"; import RenderMarkdown from "../obsidian/RenderMarkdown.svelte"; - /** **not** wrapped in a codeblock */ - export let code: string; - export let type: string; - export let plugin: BreadcrumbsPlugin; - export let source_path: string | undefined = undefined; + + interface Props { + /** **not** wrapped in a codeblock */ + code: string; + type: string; + plugin: BreadcrumbsPlugin; + source_path?: string | undefined; + } + + let { + code, + type, + plugin = $bindable(), + source_path = undefined + }: Props = $props(); + import { run } from 'svelte/legacy'; + import { MarkdownRenderer } from "obsidian"; import { log } from "src/logger"; import type BreadcrumbsPlugin from "src/main"; import { active_file_store } from "src/stores/active_file"; - export let cls = ""; - export let markdown: string; - export let plugin: BreadcrumbsPlugin; - export let source_path: string | undefined = undefined; + interface Props { + cls?: string; + markdown: string; + plugin: BreadcrumbsPlugin; + source_path?: string | undefined; + } + + let { + cls = "", + markdown, + plugin, + source_path = undefined + }: Props = $props(); - let el: HTMLElement | undefined; + let el: HTMLElement | undefined = $state(); // we need to pass both the mermaid string and the target element, so that it re-renders when the mermaid string changes // and for the initial render the target element is undefined, so we need to check for that @@ -29,7 +40,9 @@ ); }; - $: render(markdown, el); + run(() => { + render(markdown, el); + });
diff --git a/src/components/obsidian/TreeItemFlair.svelte b/src/components/obsidian/TreeItemFlair.svelte index 2fc585f3..f93a5488 100644 --- a/src/components/obsidian/TreeItemFlair.svelte +++ b/src/components/obsidian/TreeItemFlair.svelte @@ -1,7 +1,11 @@
diff --git a/src/components/obsidian/tag.svelte b/src/components/obsidian/tag.svelte index b78b316b..dd7b28c5 100644 --- a/src/components/obsidian/tag.svelte +++ b/src/components/obsidian/tag.svelte @@ -1,23 +1,30 @@ {#if href !== undefined} - + {tag} {:else} - + {tag} diff --git a/src/components/page_views/PrevNextView.svelte b/src/components/page_views/PrevNextView.svelte index e592f2e5..400384f8 100644 --- a/src/components/page_views/PrevNextView.svelte +++ b/src/components/page_views/PrevNextView.svelte @@ -5,8 +5,12 @@ import EdgeLink from "../EdgeLink.svelte"; import { toNodeStringifyOptions } from "src/graph/utils"; - export let file_path: string; - export let plugin: BreadcrumbsPlugin; + interface Props { + file_path: string; + plugin: BreadcrumbsPlugin; + } + + let { file_path, plugin }: Props = $props(); const { field_group_labels, show_node_options } = plugin.settings.views.page.prev_next; diff --git a/src/components/page_views/TrailView.svelte b/src/components/page_views/TrailView.svelte index 6c8ad72b..19395e55 100644 --- a/src/components/page_views/TrailView.svelte +++ b/src/components/page_views/TrailView.svelte @@ -1,4 +1,6 @@
@@ -63,7 +72,7 @@ await plugin.saveSettings()} + onchange={async () => await plugin.saveSettings()} > {#each ["all", "shortest", "longest"] as s} @@ -90,7 +99,7 @@ class="aspect-square text-lg" aria-label="Decrease max depth" disabled={depth <= 1} - on:click={() => (depth = Math.max(1, depth - 1))} + onclick={() => (depth = Math.max(1, depth - 1))} > - @@ -103,7 +112,7 @@ class="aspect-square text-lg" aria-label="Increase max depth" disabled={depth >= MAX_DEPTH} - on:click={() => (depth = Math.min(MAX_DEPTH, depth + 1))} + onclick={() => (depth = Math.min(MAX_DEPTH, depth + 1))} > + diff --git a/src/components/page_views/TrailViewGrid.svelte b/src/components/page_views/TrailViewGrid.svelte index e89a190f..4ac19e74 100644 --- a/src/components/page_views/TrailViewGrid.svelte +++ b/src/components/page_views/TrailViewGrid.svelte @@ -9,8 +9,12 @@ import type { Path } from "wasm/pkg/breadcrumbs_graph_wasm"; import { toNodeStringifyOptions } from "src/graph/utils"; - export let plugin: BreadcrumbsPlugin; - export let all_paths: Path[]; + interface Props { + plugin: BreadcrumbsPlugin; + all_paths: Path[]; + } + + let { plugin, all_paths }: Props = $props(); const reversed = all_paths.map((path) => path.reverse_edges); diff --git a/src/components/page_views/TrailViewPath.svelte b/src/components/page_views/TrailViewPath.svelte index 6b183a26..775a0f5d 100644 --- a/src/components/page_views/TrailViewPath.svelte +++ b/src/components/page_views/TrailViewPath.svelte @@ -3,8 +3,12 @@ import EdgeLink from "../EdgeLink.svelte"; import { NodeStringifyOptions, type Path } from "wasm/pkg/breadcrumbs_graph_wasm"; - export let plugin: BreadcrumbsPlugin; - export let all_paths: Path[]; + interface Props { + plugin: BreadcrumbsPlugin; + all_paths: Path[]; + } + + let { plugin, all_paths }: Props = $props(); const { dendron_note } = plugin.settings.explicit_edge_sources; diff --git a/src/components/page_views/index.svelte b/src/components/page_views/index.svelte index eeb2a62a..8fb7a8e3 100644 --- a/src/components/page_views/index.svelte +++ b/src/components/page_views/index.svelte @@ -3,9 +3,14 @@ import PrevNextView from "./PrevNextView.svelte"; import TrailView from "./TrailView.svelte"; - export let plugin: BreadcrumbsPlugin; - // NOTE: We can't rely on $active_file_store, since there may be multiple notes open at once, only one of which is active - export let file_path: string; + + interface Props { + plugin: BreadcrumbsPlugin; + // NOTE: We can't rely on $active_file_store, since there may be multiple notes open at once, only one of which is active + file_path: string; + } + + let { plugin, file_path }: Props = $props(); const enabled_views = { grid: plugin.settings.views.page.trail.enabled, diff --git a/src/components/selector/EdgeFieldSelector.svelte b/src/components/selector/EdgeFieldSelector.svelte index 1be79b12..aa7940a1 100644 --- a/src/components/selector/EdgeFieldSelector.svelte +++ b/src/components/selector/EdgeFieldSelector.svelte @@ -2,9 +2,13 @@ import type { EdgeField } from "src/interfaces/settings"; import { createEventDispatcher } from "svelte"; - export let fields: EdgeField[]; - export let undefine_on_change = true; - export let field: EdgeField | undefined = undefined; + interface Props { + fields: EdgeField[]; + undefine_on_change?: boolean; + field?: EdgeField | undefined; + } + + let { fields, undefine_on_change = true, field = $bindable(undefined) }: Props = $props(); const dispatch = createEventDispatcher<{ select: typeof field | undefined; @@ -14,7 +18,7 @@ { + onchange={(e) => { if (e.currentTarget.value) { actions.groups.add_field( settings.edge_field_groups.find( @@ -418,7 +422,7 @@
{/each} - @@ -439,7 +443,7 @@ class="w-8" aria-label="Clear Filter" disabled={filters.groups === ""} - on:click={() => (filters.groups = "")} + onclick={() => (filters.groups = "")} > X @@ -449,7 +453,7 @@ @@ -507,7 +511,7 @@ + onclick={(e) => actions.set_close_reversed( rule_i, e.currentTarget.checked, @@ -401,7 +405,7 @@ min={0} max={100} value={rule.rounds} - on:blur={(e) => + onblur={(e) => actions.set_rounds( rule_i, +e.currentTarget.value, @@ -417,7 +421,7 @@ type="text" value={rule.name} placeholder="Rule Name" - on:blur={(e) => + onblur={(e) => actions.rename_transitive( rule_i, e.currentTarget.value, @@ -426,7 +430,7 @@
diff --git a/src/components/side_views/Matrix.svelte b/src/components/side_views/Matrix.svelte index bb442290..fa701181 100644 --- a/src/components/side_views/Matrix.svelte +++ b/src/components/side_views/Matrix.svelte @@ -10,18 +10,22 @@ import ShowAttributesSelectorMenu from "../selector/ShowAttributesSelectorMenu.svelte"; import MatrixEdgeField from "./MatrixEdgeField.svelte"; - export let plugin: BreadcrumbsPlugin; + interface Props { + plugin: BreadcrumbsPlugin; + } + + let { plugin }: Props = $props(); let { edge_sort_id, field_group_labels, show_attributes, collapse } = - plugin.settings.views.side.matrix; + $state(plugin.settings.views.side.matrix); - $: edge_field_labels = resolve_field_group_labels( + let edge_field_labels = $derived(resolve_field_group_labels( plugin.settings.edge_field_groups, field_group_labels, - ); + )); - $: grouped_out_edges = - $active_file_store && + let grouped_out_edges = + $derived($active_file_store && // Even tho we ensure the graph is built before the views are registered, // Existing views still try render before the graph is built. plugin.graph.has_node($active_file_store.path) @@ -29,9 +33,9 @@ $active_file_store.path, edge_field_labels, ) - : null; + : null); - $: sort = create_edge_sorter(edge_sort_id.field, edge_sort_id.order === -1); + let sort = $derived(create_edge_sorter(edge_sort_id.field, edge_sort_id.order === -1));
diff --git a/src/components/side_views/MatrixEdgeField.svelte b/src/components/side_views/MatrixEdgeField.svelte index 7a565ba7..84f08823 100644 --- a/src/components/side_views/MatrixEdgeField.svelte +++ b/src/components/side_views/MatrixEdgeField.svelte @@ -7,13 +7,24 @@ import TreeItemFlair from "../obsidian/TreeItemFlair.svelte"; import { toNodeStringifyOptions, type EdgeAttribute } from "src/graph/utils"; - export let open: boolean; - export let field: EdgeField; - export let edges: EdgeStruct[]; - export let plugin: BreadcrumbsPlugin; // NOTE: These are available on settings, but they're modified in the parent component, - // so rather pass them in to receive updates - export let show_attributes: EdgeAttribute[]; + + interface Props { + open: boolean; + field: EdgeField; + edges: EdgeStruct[]; + plugin: BreadcrumbsPlugin; + // so rather pass them in to receive updates + show_attributes: EdgeAttribute[]; + } + + let { + open = $bindable(), + field, + edges, + plugin, + show_attributes + }: Props = $props(); let { show_node_options } = plugin.settings.views.side.matrix; @@ -24,8 +35,8 @@ class="BC-matrix-view-field BC-matrix-view-field-{field.label} tree-item" bind:open > - - + +
diff --git a/src/modals/CreateListIndexModal.ts b/src/modals/CreateListIndexModal.ts index 6719b665..53cd1ba5 100644 --- a/src/modals/CreateListIndexModal.ts +++ b/src/modals/CreateListIndexModal.ts @@ -11,6 +11,7 @@ import { active_file_store } from "src/stores/active_file"; import { resolve_field_group_labels } from "src/utils/edge_fields"; import { new_setting } from "src/utils/settings"; import { get } from "svelte/store"; +import { mount } from "svelte"; export class CreateListIndexModal extends Modal { plugin: BreadcrumbsPlugin; @@ -37,13 +38,13 @@ export class CreateListIndexModal extends Modal { text: "Create List Index", }); - new FieldGroupLabelsSettingItem({ - target: contentEl, - props: { - field_group_labels: this.options.field_group_labels, - edge_field_groups: plugin.settings.edge_field_groups, - }, - }).$on("select", (e) => { + mount(FieldGroupLabelsSettingItem, { + target: contentEl, + props: { + field_group_labels: this.options.field_group_labels, + edge_field_groups: plugin.settings.edge_field_groups, + }, + }).$on("select", (e) => { // Tracking groups for the UI this.options.field_group_labels = e.detail; @@ -73,17 +74,17 @@ export class CreateListIndexModal extends Modal { }, }); - new EdgeSortIdSettingItem({ - target: contentEl, - props: { edge_sort_id: this.options.edge_sort_id }, - }).$on("select", (e) => { + mount(EdgeSortIdSettingItem, { + target: contentEl, + props: { edge_sort_id: this.options.edge_sort_id }, + }).$on("select", (e) => { this.options.edge_sort_id = e.detail; }); - new ShowAttributesSettingItem({ - target: contentEl, - props: { show_attributes: this.options.show_attributes }, - }).$on("select", (e) => { + mount(ShowAttributesSettingItem, { + target: contentEl, + props: { show_attributes: this.options.show_attributes }, + }).$on("select", (e) => { this.options.show_attributes = e.detail; }); diff --git a/src/settings/GridSettings.ts b/src/settings/GridSettings.ts index 723df6ee..456c4a5a 100644 --- a/src/settings/GridSettings.ts +++ b/src/settings/GridSettings.ts @@ -3,6 +3,7 @@ import FieldGroupLabelsSettingItem from "src/components/settings/FieldGroupLabel import type BreadcrumbsPlugin from "src/main"; import { new_setting } from "src/utils/settings"; import { _add_settings_show_node_options } from "./ShowNodeOptions"; +import { mount } from "svelte"; export const _add_settings_trail_view = ( plugin: BreadcrumbsPlugin, @@ -80,14 +81,14 @@ export const _add_settings_trail_view = ( }, }); - new FieldGroupLabelsSettingItem({ - target: containerEl, - props: { - edge_field_groups: plugin.settings.edge_field_groups, - field_group_labels: - plugin.settings.views.page.trail.field_group_labels, - }, - }).$on("select", async (e) => { + mount(FieldGroupLabelsSettingItem, { + target: containerEl, + props: { + edge_field_groups: plugin.settings.edge_field_groups, + field_group_labels: + plugin.settings.views.page.trail.field_group_labels, + }, + }).$on("select", async (e) => { plugin.settings.views.page.trail.field_group_labels = e.detail; await Promise.all([ diff --git a/src/settings/ListIndexSettings.ts b/src/settings/ListIndexSettings.ts index 5eadfd2e..9c107f57 100644 --- a/src/settings/ListIndexSettings.ts +++ b/src/settings/ListIndexSettings.ts @@ -6,6 +6,7 @@ import type BreadcrumbsPlugin from "src/main"; import { resolve_field_group_labels } from "src/utils/edge_fields"; import { new_setting } from "src/utils/settings"; import { _add_settings_show_node_options } from "./ShowNodeOptions"; +import { mount } from "svelte"; export const _add_settings_list_index = ( plugin: BreadcrumbsPlugin, @@ -13,14 +14,14 @@ export const _add_settings_list_index = ( ) => { const { settings } = plugin; - new FieldGroupLabelsSettingItem({ - target: contentEl, - props: { - edge_field_groups: plugin.settings.edge_field_groups, - field_group_labels: - settings.commands.list_index.default_options.field_group_labels, - }, - }).$on("select", async (e) => { + mount(FieldGroupLabelsSettingItem, { + target: contentEl, + props: { + edge_field_groups: plugin.settings.edge_field_groups, + field_group_labels: + settings.commands.list_index.default_options.field_group_labels, + }, + }).$on("select", async (e) => { // Tracking groups for the UI settings.commands.list_index.default_options.field_group_labels = e.detail; @@ -62,25 +63,25 @@ export const _add_settings_list_index = ( }, }); - new EdgeSortIdSettingItem({ - target: contentEl, - props: { - edge_sort_id: - settings.commands.list_index.default_options.edge_sort_id, - }, - }).$on("select", async (e) => { + mount(EdgeSortIdSettingItem, { + target: contentEl, + props: { + edge_sort_id: + settings.commands.list_index.default_options.edge_sort_id, + }, + }).$on("select", async (e) => { settings.commands.list_index.default_options.edge_sort_id = e.detail; await plugin.saveSettings(); }); - new ShowAttributesSettingItem({ - target: contentEl, - props: { - show_attributes: - settings.commands.list_index.default_options.show_attributes, - }, - }).$on("select", async (e) => { + mount(ShowAttributesSettingItem, { + target: contentEl, + props: { + show_attributes: + settings.commands.list_index.default_options.show_attributes, + }, + }).$on("select", async (e) => { settings.commands.list_index.default_options.show_attributes = e.detail; await plugin.saveSettings(); diff --git a/src/settings/MatrixSettings.ts b/src/settings/MatrixSettings.ts index dfd41077..236fb1a4 100644 --- a/src/settings/MatrixSettings.ts +++ b/src/settings/MatrixSettings.ts @@ -4,6 +4,7 @@ import ShowAttributesSettingItem from "src/components/settings/ShowAttributesSet import type BreadcrumbsPlugin from "src/main"; import { _add_settings_show_node_options } from "./ShowNodeOptions"; import { new_setting } from "src/utils/settings"; +import { mount } from "svelte"; export const _add_settings_matrix = ( plugin: BreadcrumbsPlugin, @@ -25,10 +26,10 @@ export const _add_settings_matrix = ( }, }); - new EdgeSortIdSettingItem({ - target: containerEl, - props: { edge_sort_id: plugin.settings.views.side.matrix.edge_sort_id }, - }).$on("select", async (e) => { + mount(EdgeSortIdSettingItem, { + target: containerEl, + props: { edge_sort_id: plugin.settings.views.side.matrix.edge_sort_id }, + }).$on("select", async (e) => { plugin.settings.views.side.matrix.edge_sort_id = e.detail; await Promise.all([ @@ -37,13 +38,13 @@ export const _add_settings_matrix = ( ]); }); - new ShowAttributesSettingItem({ - target: containerEl, - props: { - exclude_attributes: ["field", "explicit"], - show_attributes: plugin.settings.views.side.matrix.show_attributes, - }, - }).$on("select", async (e) => { + mount(ShowAttributesSettingItem, { + target: containerEl, + props: { + exclude_attributes: ["field", "explicit"], + show_attributes: plugin.settings.views.side.matrix.show_attributes, + }, + }).$on("select", async (e) => { plugin.settings.views.side.matrix.show_attributes = e.detail; await Promise.all([ @@ -52,14 +53,14 @@ export const _add_settings_matrix = ( ]); }); - new FieldGroupLabelsSettingItem({ - target: containerEl, - props: { - edge_field_groups: plugin.settings.edge_field_groups, - field_group_labels: - plugin.settings.views.side.matrix.field_group_labels, - }, - }).$on("select", async (e) => { + mount(FieldGroupLabelsSettingItem, { + target: containerEl, + props: { + edge_field_groups: plugin.settings.edge_field_groups, + field_group_labels: + plugin.settings.views.side.matrix.field_group_labels, + }, + }).$on("select", async (e) => { plugin.settings.views.side.matrix.field_group_labels = e.detail; await Promise.all([ diff --git a/src/settings/PrevNextSettings.ts b/src/settings/PrevNextSettings.ts index ba65e2ea..0ce14b9f 100644 --- a/src/settings/PrevNextSettings.ts +++ b/src/settings/PrevNextSettings.ts @@ -2,6 +2,7 @@ import { Setting } from "obsidian"; import FieldGroupLabelsSettingItem from "src/components/settings/FieldGroupLabelsSettingItem.svelte"; import type BreadcrumbsPlugin from "src/main"; import { _add_settings_show_node_options } from "./ShowNodeOptions"; +import { mount } from "svelte"; export const _add_settings_prev_next_view = ( plugin: BreadcrumbsPlugin, @@ -22,17 +23,17 @@ export const _add_settings_prev_next_view = ( }); }); - new FieldGroupLabelsSettingItem({ - target: containerEl, - props: { - name: "Field Groups for Left", - description: - "Select the field groups to show in the left side of this view", - edge_field_groups: plugin.settings.edge_field_groups, - field_group_labels: - plugin.settings.views.page.prev_next.field_group_labels.prev, - }, - }).$on("select", async (e) => { + mount(FieldGroupLabelsSettingItem, { + target: containerEl, + props: { + name: "Field Groups for Left", + description: + "Select the field groups to show in the left side of this view", + edge_field_groups: plugin.settings.edge_field_groups, + field_group_labels: + plugin.settings.views.page.prev_next.field_group_labels.prev, + }, + }).$on("select", async (e) => { plugin.settings.views.page.prev_next.field_group_labels.prev = e.detail; await Promise.all([ @@ -41,17 +42,17 @@ export const _add_settings_prev_next_view = ( ]); }); - new FieldGroupLabelsSettingItem({ - target: containerEl, - props: { - name: "Field Groups for Right", - description: - "Select the field groups to show in the right side of this view", - edge_field_groups: plugin.settings.edge_field_groups, - field_group_labels: - plugin.settings.views.page.prev_next.field_group_labels.next, - }, - }).$on("select", async (e) => { + mount(FieldGroupLabelsSettingItem, { + target: containerEl, + props: { + name: "Field Groups for Right", + description: + "Select the field groups to show in the right side of this view", + edge_field_groups: plugin.settings.edge_field_groups, + field_group_labels: + plugin.settings.views.page.prev_next.field_group_labels.next, + }, + }).$on("select", async (e) => { plugin.settings.views.page.prev_next.field_group_labels.next = e.detail; await Promise.all([ diff --git a/src/settings/SettingsTab.ts b/src/settings/SettingsTab.ts index 4efc4ccb..a98b3a18 100644 --- a/src/settings/SettingsTab.ts +++ b/src/settings/SettingsTab.ts @@ -20,6 +20,7 @@ import { _add_settings_regex_note } from "./RegexNoteSettings"; import { _add_settings_tag_note } from "./TagNoteSettings"; import { _add_settings_thread } from "./ThreadSettings"; import { _add_settings_tree_view } from "./TreeViewSettings"; +import { mount } from "svelte"; const make_details_el = ( parent: HTMLElement, @@ -65,12 +66,12 @@ export class BreadcrumbsSettingTab extends PluginSettingTab { containerEl.addClass("BC-settings-tab"); this.components.push( - new EdgeFieldSettings({ - props: { plugin }, - target: make_details_el(containerEl, { - s: { text: "> Edge Fields" }, - }).children, - }), + mount(EdgeFieldSettings, { + props: { plugin }, + target: make_details_el(containerEl, { + s: { text: "> Edge Fields" }, + }).children, + }), ); // Implied Relations @@ -78,12 +79,12 @@ export class BreadcrumbsSettingTab extends PluginSettingTab { containerEl.createEl("h3", { text: "Implied Relations" }); this.components.push( - new TransitiveImpliedRelations({ - props: { plugin }, - target: make_details_el(containerEl, { - s: { text: "> Transitive" }, - }).children, - }), + mount(TransitiveImpliedRelations, { + props: { plugin }, + target: make_details_el(containerEl, { + s: { text: "> Transitive" }, + }).children, + }), ); // Edge Sources diff --git a/src/settings/TreeViewSettings.ts b/src/settings/TreeViewSettings.ts index 5bde8463..997d0634 100644 --- a/src/settings/TreeViewSettings.ts +++ b/src/settings/TreeViewSettings.ts @@ -4,6 +4,7 @@ import type BreadcrumbsPlugin from "src/main"; import { new_setting } from "src/utils/settings"; import ShowAttributesSettingItem from "../components/settings/ShowAttributesSettingItem.svelte"; import { _add_settings_show_node_options } from "./ShowNodeOptions"; +import { mount } from "svelte"; export const _add_settings_tree_view = ( plugin: BreadcrumbsPlugin, @@ -25,10 +26,10 @@ export const _add_settings_tree_view = ( }, }); - new EdgeSortIdSettingItem({ - target: containerEl, - props: { edge_sort_id: plugin.settings.views.side.tree.edge_sort_id }, - }).$on("select", async (e) => { + mount(EdgeSortIdSettingItem, { + target: containerEl, + props: { edge_sort_id: plugin.settings.views.side.tree.edge_sort_id }, + }).$on("select", async (e) => { plugin.settings.views.side.tree.edge_sort_id = e.detail; await Promise.all([ @@ -37,12 +38,12 @@ export const _add_settings_tree_view = ( ]); }); - new ShowAttributesSettingItem({ - target: containerEl, - props: { - show_attributes: plugin.settings.views.side.tree.show_attributes, - }, - }).$on("select", async (e) => { + mount(ShowAttributesSettingItem, { + target: containerEl, + props: { + show_attributes: plugin.settings.views.side.tree.show_attributes, + }, + }).$on("select", async (e) => { plugin.settings.views.side.tree.show_attributes = e.detail; await Promise.all([ @@ -51,14 +52,14 @@ export const _add_settings_tree_view = ( ]); }); - new FieldGroupLabelsSettingItem({ - target: containerEl, - props: { - edge_field_groups: plugin.settings.edge_field_groups, - field_group_labels: - plugin.settings.views.side.tree.field_group_labels, - }, - }).$on("select", async (e) => { + mount(FieldGroupLabelsSettingItem, { + target: containerEl, + props: { + edge_field_groups: plugin.settings.edge_field_groups, + field_group_labels: + plugin.settings.views.side.tree.field_group_labels, + }, + }).$on("select", async (e) => { plugin.settings.views.side.tree.field_group_labels = e.detail; await Promise.all([ diff --git a/src/views/matrix.ts b/src/views/matrix.ts index 44378cd5..cc08a0e4 100644 --- a/src/views/matrix.ts +++ b/src/views/matrix.ts @@ -2,6 +2,7 @@ import BreadcrumbsPlugin, { BCEvent } from "src/main"; import { ItemView, WorkspaceLeaf } from "obsidian"; import MatrixComponent from "src/components/side_views/Matrix.svelte"; import { VIEW_IDS } from "src/const/views"; +import { mount } from "svelte"; export class MatrixView extends ItemView { plugin: BreadcrumbsPlugin; @@ -34,10 +35,10 @@ export class MatrixView extends ItemView { const container = this.containerEl.children[1]; container.empty(); - this.component = new MatrixComponent({ - target: this.contentEl, - props: { plugin: this.plugin }, - }); + this.component = mount(MatrixComponent, { + target: this.contentEl, + props: { plugin: this.plugin }, + }); } async onClose() { diff --git a/src/views/page.ts b/src/views/page.ts index af671d96..350f53a1 100644 --- a/src/views/page.ts +++ b/src/views/page.ts @@ -2,6 +2,7 @@ import { MarkdownView } from "obsidian"; import PageViewsComponent from "src/components/page_views/index.svelte"; import { log } from "src/logger"; import type BreadcrumbsPlugin from "src/main"; +import { mount } from "svelte"; export const redraw_page_views = (plugin: BreadcrumbsPlugin) => { const markdown_views = plugin.app.workspace.getLeavesOfType("markdown"); @@ -73,9 +74,9 @@ export const redraw_page_views = (plugin: BreadcrumbsPlugin) => { } // Render the component into the container - new PageViewsComponent({ - target: page_views_el, - props: { plugin, file_path: markdown_view.file?.path ?? "" }, - }); + mount(PageViewsComponent, { + target: page_views_el, + props: { plugin, file_path: markdown_view.file?.path ?? "" }, + }); }); }; diff --git a/src/views/tree.ts b/src/views/tree.ts index 1a6f52b0..9044b346 100644 --- a/src/views/tree.ts +++ b/src/views/tree.ts @@ -2,6 +2,7 @@ import { ItemView, WorkspaceLeaf } from "obsidian"; import TreeViewComponent from "src/components/side_views/TreeView.svelte"; import { VIEW_IDS } from "src/const/views"; import BreadcrumbsPlugin, { BCEvent } from "src/main"; +import { mount } from "svelte"; export class TreeView extends ItemView { plugin: BreadcrumbsPlugin; @@ -34,10 +35,10 @@ export class TreeView extends ItemView { const container = this.containerEl.children[1]; container.empty(); - this.component = new TreeViewComponent({ - target: this.contentEl, - props: { plugin: this.plugin }, - }); + this.component = mount(TreeViewComponent, { + target: this.contentEl, + props: { plugin: this.plugin }, + }); } async onClose() { From 2bf37a855125f45282a08b4639c0324e256fcc78 Mon Sep 17 00:00:00 2001 From: Moritz Jung Date: Tue, 7 Jan 2025 00:40:47 +0100 Subject: [PATCH 52/65] fix svelte migration --- esbuild.config.mjs | 1 + src/codeblocks/MDRC.ts | 27 ++++--- src/commands/init.ts | 79 ++++++++++--------- src/components/NestedEdgeList.svelte | 4 +- src/components/input/SimpleInput.svelte | 9 +-- src/components/obsidian/tag.svelte | 23 ++++-- src/components/page_views/TrailView.svelte | 23 ++---- .../settings/EdgeFieldSettings.svelte | 8 +- .../settings/EdgeSortIdSettingItem.svelte | 15 ++-- .../FieldGroupLabelsSettingItem.svelte | 14 ++-- .../settings/ShowAttributesSettingItem.svelte | 13 +-- .../TransitiveImpliedRelations.svelte | 2 +- src/components/side_views/TreeView.svelte | 17 +++- src/modals/CreateListIndexModal.ts | 53 +++++++------ src/settings/GridSettings.ts | 27 ++++--- src/settings/ListIndexSettings.ts | 72 +++++++++-------- src/settings/MatrixSettings.ts | 70 ++++++++-------- src/settings/PrevNextSettings.ts | 62 ++++++++------- src/settings/SettingsTab.ts | 26 +++--- src/settings/TreeViewSettings.ts | 54 +++++++------ src/views/matrix.ts | 8 +- src/views/tree.ts | 8 +- wasm/src/edge_sorting.rs | 6 +- wasm/src/graph_traversal.rs | 32 ++++++-- 24 files changed, 357 insertions(+), 296 deletions(-) diff --git a/esbuild.config.mjs b/esbuild.config.mjs index 7bfe2c19..3db044ae 100644 --- a/esbuild.config.mjs +++ b/esbuild.config.mjs @@ -70,6 +70,7 @@ const context = await esbuild.context({ outfile: "main.js", plugins: [ esbuildSvelte({ + cache: false, compilerOptions: { css: 'injected', dev: !prod }, preprocess: sveltePreprocess(), }), diff --git a/src/codeblocks/MDRC.ts b/src/codeblocks/MDRC.ts index 7b83bbf4..bcdc3ef3 100644 --- a/src/codeblocks/MDRC.ts +++ b/src/codeblocks/MDRC.ts @@ -8,12 +8,12 @@ import type BreadcrumbsPlugin from "src/main"; import { Timer } from "src/utils/timer"; import { Codeblocks } from "."; import { BCEvent } from "src/main"; -import { mount } from "svelte"; +import { mount, unmount } from "svelte"; export class CodeblockMDRC extends MarkdownRenderChild { source: string; plugin: BreadcrumbsPlugin; - component: CodeblockTree | CodeblockMermaid | undefined; + component: ReturnType | ReturnType | undefined; file_path: string; id: string; @@ -107,15 +107,16 @@ export class CodeblockMDRC extends MarkdownRenderChild { }, }); } else if (options.type === "markmap") { - this.component = mount(CodeblockMarkmap, { - target: this.containerEl, - props: { - errors, - options, - file_path, - plugin: this.plugin, - }, - }); + // TODO + // this.component = mount(CodeblockMarkmap, { + // target: this.containerEl, + // props: { + // errors, + // options, + // file_path, + // plugin: this.plugin, + // }, + // }); } else { log.error("CodeblockMDRC unknown type", options.type); } @@ -131,6 +132,8 @@ export class CodeblockMDRC extends MarkdownRenderChild { onunload(): void { log.debug("CodeblockMDRC.unload"); - this.component?.$destroy(); + if (this.component) { + unmount(this.component); + } } } diff --git a/src/commands/init.ts b/src/commands/init.ts index 41881533..f6f59785 100644 --- a/src/commands/init.ts +++ b/src/commands/init.ts @@ -93,45 +93,46 @@ export const init_all_commands = (plugin: BreadcrumbsPlugin) => { new GenericModal(plugin.app, (modal) => { mount(SimpleInput, { - target: modal.contentEl, - props: { - label: `Type '${PROMPT_TARGET}' to confirm`, - disabled_cb: (value: string) => value !== PROMPT_TARGET, - }, - }).$on("submit", async (e) => { - if (e.detail !== PROMPT_TARGET) { - new Notice("Command cancelled"); - } else { - const timer = new Timer(); - - const notice = new Notice( - "Freezing implied edges to all notes in vault...", - ); - - await Promise.all( - plugin.app.vault - .getMarkdownFiles() - .map((file) => - freeze_implied_edges_to_note( - plugin, - file, - plugin.settings.commands - .freeze_implied_edges - .default_options, - ), - ), - ); - - log.debug( - `freeze-implied-edges-to-vault > took ${timer.elapsed_str()}ms`, - ); - - notice.setMessage( - `Implied edges frozen to all notes in ${timer.elapsed_str()}ms`, - ); - } - - modal.close(); + target: modal.contentEl, + props: { + label: `Type '${PROMPT_TARGET}' to confirm`, + disabled_cb: (value: string) => value !== PROMPT_TARGET, + submit_cb: async (value: string) => { + if (value !== PROMPT_TARGET) { + new Notice("Command cancelled"); + } else { + const timer = new Timer(); + + const notice = new Notice( + "Freezing implied edges to all notes in vault...", + ); + + await Promise.all( + plugin.app.vault + .getMarkdownFiles() + .map((file) => + freeze_implied_edges_to_note( + plugin, + file, + plugin.settings.commands + .freeze_implied_edges + .default_options, + ), + ), + ); + + log.debug( + `freeze-implied-edges-to-vault > took ${timer.elapsed_str()}ms`, + ); + + notice.setMessage( + `Implied edges frozen to all notes in ${timer.elapsed_str()}ms`, + ); + } + + modal.close(); + } + }, }); }).open(); }, diff --git a/src/components/NestedEdgeList.svelte b/src/components/NestedEdgeList.svelte index 71d47128..e8ad5b18 100644 --- a/src/components/NestedEdgeList.svelte +++ b/src/components/NestedEdgeList.svelte @@ -10,9 +10,6 @@ import { FlatTraversalData } from "wasm/pkg/breadcrumbs_graph_wasm"; import { toNodeStringifyOptions, type EdgeAttribute } from "src/graph/utils"; - - - interface Props { plugin: BreadcrumbsPlugin; // export let tree: RecTraversalData[]; @@ -83,6 +80,7 @@ {show_attributes} {show_node_options} {data} + {open_signal} items={children} />
diff --git a/src/components/input/SimpleInput.svelte b/src/components/input/SimpleInput.svelte index 6d74a172..d416ec92 100644 --- a/src/components/input/SimpleInput.svelte +++ b/src/components/input/SimpleInput.svelte @@ -1,16 +1,13 @@
@@ -24,7 +21,7 @@ diff --git a/src/components/obsidian/tag.svelte b/src/components/obsidian/tag.svelte index dd7b28c5..373aeb9d 100644 --- a/src/components/obsidian/tag.svelte +++ b/src/components/obsidian/tag.svelte @@ -1,18 +1,25 @@ {#if href !== undefined} - + {tag} {:else} @@ -22,9 +29,9 @@ class="tag" tabindex="0" role="button" - onclick={bubble('click')} - onkeydown={bubble('keydown')} - oncontextmenu={bubble('contextmenu')} + onclick={onclick} + onkeydown={onkeydown} + oncontextmenu={oncontextmenu} > {tag} diff --git a/src/components/page_views/TrailView.svelte b/src/components/page_views/TrailView.svelte index 19395e55..987313de 100644 --- a/src/components/page_views/TrailView.svelte +++ b/src/components/page_views/TrailView.svelte @@ -1,14 +1,10 @@ @@ -72,7 +65,7 @@ -
diff --git a/src/components/obsidian/RenderExternalCodeblock.svelte b/src/components/obsidian/RenderExternalCodeblock.svelte index ee7532f3..a11b4129 100644 --- a/src/components/obsidian/RenderExternalCodeblock.svelte +++ b/src/components/obsidian/RenderExternalCodeblock.svelte @@ -3,7 +3,6 @@ import { wrap_in_codeblock } from "src/utils/strings"; import RenderMarkdown from "../obsidian/RenderMarkdown.svelte"; - interface Props { /** **not** wrapped in a codeblock */ code: string; @@ -16,7 +15,7 @@ code, type, plugin = $bindable(), - source_path = undefined + source_path = undefined, }: Props = $props(); diff --git a/src/components/obsidian/RenderMarkdown.svelte b/src/components/obsidian/RenderMarkdown.svelte index c4a581c9..f9a85093 100644 --- a/src/components/obsidian/RenderMarkdown.svelte +++ b/src/components/obsidian/RenderMarkdown.svelte @@ -1,5 +1,5 @@ {#if href !== undefined} - + {tag} {:else} @@ -29,9 +29,9 @@ class="tag" tabindex="0" role="button" - onclick={onclick} - onkeydown={onkeydown} - oncontextmenu={oncontextmenu} + {onclick} + {onkeydown} + {oncontextmenu} > {tag} diff --git a/src/components/page_views/PrevNextView.svelte b/src/components/page_views/PrevNextView.svelte index 400384f8..a273b70f 100644 --- a/src/components/page_views/PrevNextView.svelte +++ b/src/components/page_views/PrevNextView.svelte @@ -15,7 +15,10 @@ const { field_group_labels, show_node_options } = plugin.settings.views.page.prev_next; - let node_stringify_options = toNodeStringifyOptions(plugin, show_node_options); + let node_stringify_options = toNodeStringifyOptions( + plugin, + show_node_options, + ); const edge_field_labels = { prev: resolve_field_group_labels( @@ -35,9 +38,14 @@ const grouped_out_edges = plugin.graph.has_node(file_path) ? group_by( - plugin.graph.get_outgoing_edges(file_path).filter((e) => - e.matches_edge_filter(plugin.graph, merged_field_labels), - ), + plugin.graph + .get_outgoing_edges(file_path) + .filter((e) => + e.matches_edge_filter( + plugin.graph, + merged_field_labels, + ), + ), (e) => edge_field_labels.prev.includes(e.edge_type) ? ("prev" as const) @@ -56,7 +64,12 @@
{edge.edge_type} - +
{/each}
@@ -67,7 +80,12 @@ > {#each grouped_out_edges?.next ?? [] as edge}
- + {edge.edge_type}
diff --git a/src/components/page_views/TrailView.svelte b/src/components/page_views/TrailView.svelte index 987313de..9c50a59b 100644 --- a/src/components/page_views/TrailView.svelte +++ b/src/components/page_views/TrailView.svelte @@ -4,7 +4,10 @@ import MergeFieldsButton from "../button/MergeFieldsButton.svelte"; import TrailViewGrid from "./TrailViewGrid.svelte"; import TrailViewPath from "./TrailViewPath.svelte"; - import { PathList, TraversalOptions } from "wasm/pkg/breadcrumbs_graph_wasm"; + import { + PathList, + TraversalOptions, + } from "wasm/pkg/breadcrumbs_graph_wasm"; interface Props { plugin: BreadcrumbsPlugin; @@ -39,18 +42,17 @@ let all_paths = traversal_data.to_paths(); - return all_paths.select( - plugin.settings.views.page.trail.selection, - ); + return all_paths.select(plugin.settings.views.page.trail.selection); }); let MAX_DEPTH = $derived(Math.max(0, selected_paths?.max_depth() ?? 0)); let depth = $state(0); $effect(() => { depth = Math.min( - MAX_DEPTH, - plugin.settings.views.page.trail.default_depth, - )}); + MAX_DEPTH, + plugin.settings.views.page.trail.default_depth, + ); + }); let sorted_paths = $derived(selected_paths?.process(plugin.graph, depth)); diff --git a/src/components/page_views/TrailViewGrid.svelte b/src/components/page_views/TrailViewGrid.svelte index 4ac19e74..18de867d 100644 --- a/src/components/page_views/TrailViewGrid.svelte +++ b/src/components/page_views/TrailViewGrid.svelte @@ -26,8 +26,12 @@ gather_by_runs(col, (e) => (e ? e.target_path(plugin.graph) : null)), ); - const show_node_options = plugin.settings.views.page.trail.show_node_options - const node_stringify_options = toNodeStringifyOptions(plugin, show_node_options); + const show_node_options = + plugin.settings.views.page.trail.show_node_options; + const node_stringify_options = toNodeStringifyOptions( + plugin, + show_node_options, + ); await plugin.saveSettings()} > {#each ["grid", "path"] as format} @@ -77,7 +90,7 @@ - +
- {#if plugin.settings.views.page.trail.format === "grid"} + {#if format === "grid"} - {:else if plugin.settings.views.page.trail.format === "path"} + {:else if format === "path"} {/if} {:else if plugin.settings.views.page.trail.no_path_message} diff --git a/src/components/side_views/Matrix.svelte b/src/components/side_views/Matrix.svelte index 52dd650f..7430e482 100644 --- a/src/components/side_views/Matrix.svelte +++ b/src/components/side_views/Matrix.svelte @@ -26,13 +26,15 @@ ), ); + let active_file = $derived($active_file_store); + let grouped_out_edges = $derived( - $active_file_store && + active_file && // Even tho we ensure the graph is built before the views are registered, // Existing views still try render before the graph is built. - plugin.graph.has_node($active_file_store.path) + plugin.graph.has_node(active_file.path) ? plugin.graph.get_filtered_grouped_outgoing_edges( - $active_file_store.path, + active_file.path, edge_field_labels, ) : null, diff --git a/src/components/side_views/TreeView.svelte b/src/components/side_views/TreeView.svelte index b6fec9e8..cff312fb 100644 --- a/src/components/side_views/TreeView.svelte +++ b/src/components/side_views/TreeView.svelte @@ -56,14 +56,13 @@ create_edge_sorter(edge_sort_id.field, edge_sort_id.order === -1), ); + let active_file = $derived($active_file_store); + let tree: FlatTraversalResult | undefined = $derived.by(() => { - if ( - $active_file_store && - plugin.graph.has_node($active_file_store.path) - ) { + if (active_file && plugin.graph.has_node(active_file.path)) { return plugin.graph.rec_traverse_and_process( new TraversalOptions( - [$active_file_store!.path], + [active_file!.path], edge_field_labels, 5, !merge_fields, @@ -82,7 +81,7 @@ untrack(() => tree?.sort(plugin.graph, s)); return { tree: tree, - } + }; }); diff --git a/src/main.ts b/src/main.ts index de75e726..140f79fa 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,12 +1,4 @@ -import { - Events, - Notice, - Plugin, - TFile, - WorkspaceLeaf, - debounce, -} from "obsidian"; -import { Codeblocks } from "src/codeblocks"; +import { Events, Notice, Plugin, TFile, WorkspaceLeaf } from "obsidian"; import { DEFAULT_SETTINGS } from "src/const/settings"; import { VIEW_IDS } from "src/const/views"; import { rebuild_graph } from "src/graph/builders"; @@ -203,8 +195,7 @@ export default class BreadcrumbsPlugin extends Plugin { this.app.vault.on("create", (file) => { log.debug("on:create >", file.path); - // TODO: we should probably check for markdown files only - if (file instanceof TFile) { + if (file instanceof TFile && file.extension === "md") { const batch = new BatchGraphUpdate(); new AddNoteGraphUpdate( new GCNodeData(file.path, [], true, false, false), @@ -218,8 +209,7 @@ export default class BreadcrumbsPlugin extends Plugin { this.app.vault.on("rename", (file, old_path) => { log.debug("on:rename >", old_path, "->", file.path); - // TODO: we should probably check for markdown files only - if (file instanceof TFile) { + if (file instanceof TFile && file.extension === "md") { const batch = new BatchGraphUpdate(); new RenameNoteGraphUpdate( old_path, @@ -234,8 +224,7 @@ export default class BreadcrumbsPlugin extends Plugin { this.app.vault.on("delete", (file) => { log.debug("on:delete >", file.path); - // TODO: we should probably check for markdown files only - if (file instanceof TFile) { + if (file instanceof TFile && file.extension === "md") { const batch = new BatchGraphUpdate(); new RemoveNoteGraphUpdate(file.path).add_to_batch( batch, diff --git a/tests/commands/list_index.test.ts b/tests/commands/list_index.test.ts index f9e631d1..f37d1605 100644 --- a/tests/commands/list_index.test.ts +++ b/tests/commands/list_index.test.ts @@ -1,105 +1,105 @@ -import { ListIndex } from "src/commands/list_index"; -import { BCGraph } from "src/graph/MyMultiGraph"; -import { _mock_edge } from "tests/__mocks__/graph"; -import { describe, expect, test } from "vitest"; +// import { ListIndex } from "src/commands/list_index"; +// import { BCGraph } from "src/graph/MyMultiGraph"; +// import { _mock_edge } from "tests/__mocks__/graph"; +// import { describe, expect, test } from "vitest"; -const edges = [ - _mock_edge("index.md", "1.md", {}), - _mock_edge("1.md", "1.1.md", {}), - _mock_edge("1.1.md", "1.1.1.md", {}), - _mock_edge("1.1.md", "1.1.2.md", {}), - _mock_edge("1.md", "1.2.md", {}), - _mock_edge("1.2.md", "1.2.1.md", {}), - _mock_edge("1.2.md", "1.2.2.md", {}), - _mock_edge("index.md", "2.md", {}), - _mock_edge("2.md", "2.1.md", {}), - _mock_edge("2.1.md", "2.1.1.md", {}), - _mock_edge("2.1.md", "2.1.2.md", {}), - _mock_edge("2.md", "2.2.md", {}), - _mock_edge("2.2.md", "2.2.1.md", {}), - _mock_edge("2.2.md", "2.2.2.md", {}), -]; +// const edges = [ +// _mock_edge("index.md", "1.md", {}), +// _mock_edge("1.md", "1.1.md", {}), +// _mock_edge("1.1.md", "1.1.1.md", {}), +// _mock_edge("1.1.md", "1.1.2.md", {}), +// _mock_edge("1.md", "1.2.md", {}), +// _mock_edge("1.2.md", "1.2.1.md", {}), +// _mock_edge("1.2.md", "1.2.2.md", {}), +// _mock_edge("index.md", "2.md", {}), +// _mock_edge("2.md", "2.1.md", {}), +// _mock_edge("2.1.md", "2.1.1.md", {}), +// _mock_edge("2.1.md", "2.1.2.md", {}), +// _mock_edge("2.md", "2.2.md", {}), +// _mock_edge("2.2.md", "2.2.1.md", {}), +// _mock_edge("2.2.md", "2.2.2.md", {}), +// ]; -describe("build", () => { - test("binary-tree > defaults", () => { - const graph = new BCGraph({ edges }); +// describe("build", () => { +// test("binary-tree > defaults", () => { +// const graph = new BCGraph({ edges }); - const list_index = ListIndex.build(graph, "index.md", { - indent: " ", - fields: ["down"], - show_attributes: [], - field_group_labels: [], - link_kind: "none", - edge_sort_id: { - order: 1, - field: "basename", - }, - show_node_options: { - ext: false, - alias: false, - folder: false, - }, - }); +// const list_index = ListIndex.build(graph, "index.md", { +// indent: " ", +// fields: ["down"], +// show_attributes: [], +// field_group_labels: [], +// link_kind: "none", +// edge_sort_id: { +// order: 1, +// field: "basename", +// }, +// show_node_options: { +// ext: false, +// alias: false, +// folder: false, +// }, +// }); - expect(list_index).toBe( - [ - "- 1", - " - 1.1", - " - 1.1.1", - " - 1.1.2", - " - 1.2", - " - 1.2.1", - " - 1.2.2", - "- 2", - " - 2.1", - " - 2.1.1", - " - 2.1.2", - " - 2.2", - " - 2.2.1", - " - 2.2.2", - "", - ].join("\n"), - ); - }); +// expect(list_index).toBe( +// [ +// "- 1", +// " - 1.1", +// " - 1.1.1", +// " - 1.1.2", +// " - 1.2", +// " - 1.2.1", +// " - 1.2.2", +// "- 2", +// " - 2.1", +// " - 2.1.1", +// " - 2.1.2", +// " - 2.2", +// " - 2.2.1", +// " - 2.2.2", +// "", +// ].join("\n"), +// ); +// }); - test("binary-tree > indent + show-attributes + link_kind + edge_sort_id", () => { - const graph = new BCGraph({ edges }); +// test("binary-tree > indent + show-attributes + link_kind + edge_sort_id", () => { +// const graph = new BCGraph({ edges }); - const list_index = ListIndex.build(graph, "index.md", { - indent: ".", - fields: ["down"], - link_kind: "wiki", - field_group_labels: [], - show_attributes: ["explicit", "field"], - edge_sort_id: { - order: -1, - field: "basename", - }, - show_node_options: { - ext: true, - alias: false, - folder: false, - }, - }); +// const list_index = ListIndex.build(graph, "index.md", { +// indent: ".", +// fields: ["down"], +// link_kind: "wiki", +// field_group_labels: [], +// show_attributes: ["explicit", "field"], +// edge_sort_id: { +// order: -1, +// field: "basename", +// }, +// show_node_options: { +// ext: true, +// alias: false, +// folder: false, +// }, +// }); - expect(list_index).toBe( - [ - "- [[2]] (field=down explicit=true)", - ".- [[2.2]] (field=down explicit=true)", - "..- [[2.2.2]] (field=down explicit=true)", - "..- [[2.2.1]] (field=down explicit=true)", - ".- [[2.1]] (field=down explicit=true)", - "..- [[2.1.2]] (field=down explicit=true)", - "..- [[2.1.1]] (field=down explicit=true)", - "- [[1]] (field=down explicit=true)", - ".- [[1.2]] (field=down explicit=true)", - "..- [[1.2.2]] (field=down explicit=true)", - "..- [[1.2.1]] (field=down explicit=true)", - ".- [[1.1]] (field=down explicit=true)", - "..- [[1.1.2]] (field=down explicit=true)", - "..- [[1.1.1]] (field=down explicit=true)", - "", - ].join("\n"), - ); - }); -}); +// expect(list_index).toBe( +// [ +// "- [[2]] (field=down explicit=true)", +// ".- [[2.2]] (field=down explicit=true)", +// "..- [[2.2.2]] (field=down explicit=true)", +// "..- [[2.2.1]] (field=down explicit=true)", +// ".- [[2.1]] (field=down explicit=true)", +// "..- [[2.1.2]] (field=down explicit=true)", +// "..- [[2.1.1]] (field=down explicit=true)", +// "- [[1]] (field=down explicit=true)", +// ".- [[1.2]] (field=down explicit=true)", +// "..- [[1.2.2]] (field=down explicit=true)", +// "..- [[1.2.1]] (field=down explicit=true)", +// ".- [[1.1]] (field=down explicit=true)", +// "..- [[1.1.2]] (field=down explicit=true)", +// "..- [[1.1.1]] (field=down explicit=true)", +// "", +// ].join("\n"), +// ); +// }); +// }); diff --git a/tests/commands/stats.test.ts b/tests/commands/stats.test.ts index 81895726..f576ea64 100644 --- a/tests/commands/stats.test.ts +++ b/tests/commands/stats.test.ts @@ -1,61 +1,61 @@ -import { get_graph_stats } from "src/commands/stats"; -import { BCGraph } from "src/graph/MyMultiGraph"; -import { _mock_edge } from "tests/__mocks__/graph"; -import { describe, expect, test } from "vitest"; +// import { get_graph_stats } from "src/commands/stats"; +// import { BCGraph } from "src/graph/MyMultiGraph"; +// import { _mock_edge } from "tests/__mocks__/graph"; +// import { describe, expect, test } from "vitest"; -describe("get_graph_stats", () => { - test("straight-line", () => { - const graph = new BCGraph({ - edges: [ - _mock_edge("a", "b", {}), - _mock_edge("b", "c", { - explicit: true, - field: "parent", - source: "dataview_note", - }), - _mock_edge("c", "d", { - explicit: false, - field: "right", - implied_kind: "transitive:cousin_is_sibling", - round: 1, - }), - ], - }); +// describe("get_graph_stats", () => { +// test("straight-line", () => { +// const graph = new BCGraph({ +// edges: [ +// _mock_edge("a", "b", {}), +// _mock_edge("b", "c", { +// explicit: true, +// field: "parent", +// source: "dataview_note", +// }), +// _mock_edge("c", "d", { +// explicit: false, +// field: "right", +// implied_kind: "transitive:cousin_is_sibling", +// round: 1, +// }), +// ], +// }); - const stats = get_graph_stats(graph, { - groups: [ - { label: "ups", fields: ["parent"] }, - { label: "rights", fields: ["right"] }, - ], - }); +// const stats = get_graph_stats(graph, { +// groups: [ +// { label: "ups", fields: ["parent"] }, +// { label: "rights", fields: ["right"] }, +// ], +// }); - expect(stats).toStrictEqual({ - nodes: { resolved: { true: 4 } }, - edges: { - field: { - down: 1, - parent: 1, - right: 1, - }, - group: { - ups: 1, - rights: 1, - }, - source: { - typed_link: 1, - dataview_note: 1, - }, - explicit: { - true: 2, - false: 1, - }, - implied_kind: { - "transitive:cousin_is_sibling": 1, - }, - round: { - "1": 1, - }, - }, - }); - }); -}); +// expect(stats).toStrictEqual({ +// nodes: { resolved: { true: 4 } }, +// edges: { +// field: { +// down: 1, +// parent: 1, +// right: 1, +// }, +// group: { +// ups: 1, +// rights: 1, +// }, +// source: { +// typed_link: 1, +// dataview_note: 1, +// }, +// explicit: { +// true: 2, +// false: 1, +// }, +// implied_kind: { +// "transitive:cousin_is_sibling": 1, +// }, +// round: { +// "1": 1, +// }, +// }, +// }); +// }); +// }); diff --git a/tests/utils/mermaid.test.ts b/tests/utils/mermaid.test.ts index 8d4f8ac5..ed319efd 100644 --- a/tests/utils/mermaid.test.ts +++ b/tests/utils/mermaid.test.ts @@ -1,80 +1,80 @@ -import { Mermaid } from "src/utils/mermaid"; -import { _mock_edge } from "tests/__mocks__/graph"; -import { describe, test } from "vitest"; +// import { Mermaid } from "src/utils/mermaid"; +// import { _mock_edge } from "tests/__mocks__/graph"; +// import { describe, test } from "vitest"; -describe("from_edges", () => { - const edges = [ - _mock_edge("a.md", "b.md", { explicit: true, field: "up" }), - _mock_edge("b.md", "c.md", { explicit: false, field: "down" }), - ]; +// describe("from_edges", () => { +// const edges = [ +// _mock_edge("a.md", "b.md", { explicit: true, field: "up" }), +// _mock_edge("b.md", "c.md", { explicit: false, field: "down" }), +// ]; - test("!config", (t) => { - t.expect(Mermaid.from_edges(edges).trim()).toBe( - ` -%%{ init: { "flowchart": {} } }%% -flowchart LR -\t0("a.md") -\t1("b.md") -\t2("c.md") +// test("!config", (t) => { +// t.expect(Mermaid.from_edges(edges).trim()).toBe( +// ` +// %%{ init: { "flowchart": {} } }%% +// flowchart LR +// \t0("a.md") +// \t1("b.md") +// \t2("c.md") -\t0 --> 1 -\t1 -.-> 2`.trimStart(), - ); - }); +// \t0 --> 1 +// \t1 -.-> 2`.trimStart(), +// ); +// }); - test("config.kind,direction,renderer", (t) => { - t.expect( - Mermaid.from_edges(edges, { - kind: "graph", - direction: "TB", - renderer: "elk", - }).trim(), - ).toBe( - ` -%%{ init: { "flowchart": {"defaultRenderer":"elk"} } }%% -graph TB -\t0("a.md") -\t1("b.md") -\t2("c.md") +// test("config.kind,direction,renderer", (t) => { +// t.expect( +// Mermaid.from_edges(edges, { +// kind: "graph", +// direction: "TB", +// renderer: "elk", +// }).trim(), +// ).toBe( +// ` +// %%{ init: { "flowchart": {"defaultRenderer":"elk"} } }%% +// graph TB +// \t0("a.md") +// \t1("b.md") +// \t2("c.md") -\t0 --> 1 -\t1 -.-> 2`.trimStart(), - ); - }); +// \t0 --> 1 +// \t1 -.-> 2`.trimStart(), +// ); +// }); - test("config.show_attributes", (t) => { - t.expect( - Mermaid.from_edges(edges, { show_attributes: ["field"] }).trim(), - ).toBe( - ` -%%{ init: { "flowchart": {} } }%% -flowchart LR -\t0("a.md") -\t1("b.md") -\t2("c.md") +// test("config.show_attributes", (t) => { +// t.expect( +// Mermaid.from_edges(edges, { show_attributes: ["field"] }).trim(), +// ).toBe( +// ` +// %%{ init: { "flowchart": {} } }%% +// flowchart LR +// \t0("a.md") +// \t1("b.md") +// \t2("c.md") -\t0 -->|"up"| 1 -\t1 -.->|"down"| 2`.trimStart(), - ); - }); +// \t0 -->|"up"| 1 +// \t1 -.->|"down"| 2`.trimStart(), +// ); +// }); - test("config.click.class", (t) => { - t.expect( - Mermaid.from_edges(edges, { click: { method: "class" } }).trim(), - ).toBe( - ` -%%{ init: { "flowchart": {} } }%% -flowchart LR -\t0("a.md") -\t1("b.md") -\t2("c.md") +// test("config.click.class", (t) => { +// t.expect( +// Mermaid.from_edges(edges, { click: { method: "class" } }).trim(), +// ).toBe( +// ` +// %%{ init: { "flowchart": {} } }%% +// flowchart LR +// \t0("a.md") +// \t1("b.md") +// \t2("c.md") -\t0 --> 1 -\t1 -.-> 2 +// \t0 --> 1 +// \t1 -.-> 2 -\tclass 0,1,2 internal-link`.trimStart(), - ); - }); -}); +// \tclass 0,1,2 internal-link`.trimStart(), +// ); +// }); +// }); -// TODO: I need to test more cases here +// // TODO: I need to test more cases here diff --git a/wasm/src/data/edge.rs b/wasm/src/data/edge.rs index fe806749..9a559ef2 100644 --- a/wasm/src/data/edge.rs +++ b/wasm/src/data/edge.rs @@ -3,7 +3,7 @@ use std::rc::Rc; use itertools::Itertools; use wasm_bindgen::prelude::wasm_bindgen; -use crate::graph::edge_matches_edge_filter_string; +use crate::graph::{edge_matches_edge_filter, edge_matches_edge_filter_string}; #[wasm_bindgen] #[derive(Clone, Debug, PartialEq)] @@ -48,7 +48,11 @@ impl EdgeData { edge_matches_edge_filter_string(self, edge_types) } - pub fn get_attribute_label(&self, attributes: &Vec) -> String { + pub fn matches_edge_filter(&self, edge_types: Option<&Vec>>) -> bool { + edge_matches_edge_filter(self, edge_types) + } + + pub fn attribute_label(&self, attributes: &Vec) -> String { let mut result = vec![]; // the mapping that exist on the JS side are as follows diff --git a/wasm/src/data/edge_struct.rs b/wasm/src/data/edge_struct.rs index 2be857fc..4bada3fa 100644 --- a/wasm/src/data/edge_struct.rs +++ b/wasm/src/data/edge_struct.rs @@ -94,7 +94,7 @@ impl EdgeStruct { graph: &NoteGraph, attributes: Vec, ) -> utils::Result { - Ok(self.edge_data_ref(graph)?.get_attribute_label(&attributes)) + Ok(self.edge_data_ref(graph)?.attribute_label(&attributes)) } pub fn matches_edge_filter( diff --git a/wasm/src/data/node.rs b/wasm/src/data/node.rs index e7372c7d..36a358f1 100644 --- a/wasm/src/data/node.rs +++ b/wasm/src/data/node.rs @@ -40,16 +40,6 @@ impl NodeData { } impl NodeData { - pub fn from_construction_data(data: GCNodeData) -> NodeData { - NodeData { - path: data.path, - aliases: data.aliases, - resolved: data.resolved, - ignore_in_edges: data.ignore_in_edges, - ignore_out_edges: data.ignore_out_edges, - } - } - pub fn new_unresolved(path: String) -> NodeData { NodeData { path, @@ -71,3 +61,15 @@ impl NodeData { self.ignore_out_edges = data.ignore_out_edges; } } + +impl From for NodeData { + fn from(data: GCNodeData) -> NodeData { + NodeData { + path: data.path, + aliases: data.aliases, + resolved: data.resolved, + ignore_in_edges: data.ignore_in_edges, + ignore_out_edges: data.ignore_out_edges, + } + } +} diff --git a/wasm/src/data/rules.rs b/wasm/src/data/rules.rs index 74b5051b..5ea0b18f 100644 --- a/wasm/src/data/rules.rs +++ b/wasm/src/data/rules.rs @@ -53,6 +53,44 @@ impl TransitiveGraphRule { } } + pub fn create_example_graph(&self) -> Result { + let mut graph = NoteGraph::new(); + + let mut node_data = vec![]; + let mut edge_data = vec![]; + + let mut counter = 1; + for element in self.path.iter() { + node_data.push(GCNodeData::new( + counter.to_string(), + vec![], + true, + false, + false, + )); + edge_data.push(GCEdgeData::new( + counter.to_string(), + (counter + 1).to_string(), + element.to_string(), + "explicit".to_string(), + )); + + counter += 1; + } + + node_data.push(GCNodeData::new( + counter.to_string(), + vec![], + true, + false, + false, + )); + + graph.build_graph(node_data, edge_data, vec![self.clone()])?; + + Ok(graph) + } + #[wasm_bindgen(js_name = toString)] pub fn to_fancy_string(&self) -> String { format!("{:#?}", self) @@ -84,42 +122,3 @@ impl TransitiveGraphRule { self.close_reversed } } - -#[wasm_bindgen] -pub fn create_graph_from_rule(rule: TransitiveGraphRule) -> Result { - let mut graph = NoteGraph::new(); - - let mut node_data = vec![]; - let mut edge_data = vec![]; - - let mut counter = 1; - for element in rule.path.iter() { - node_data.push(GCNodeData::new( - counter.to_string(), - vec![], - true, - false, - false, - )); - edge_data.push(GCEdgeData::new( - counter.to_string(), - (counter + 1).to_string(), - element.to_string(), - "explicit".to_string(), - )); - - counter += 1; - } - - node_data.push(GCNodeData::new( - counter.to_string(), - vec![], - true, - false, - false, - )); - - graph.build_graph(node_data, edge_data, vec![rule])?; - - Ok(graph) -} diff --git a/wasm/src/data/traversal.rs b/wasm/src/data/traversal.rs index 09a44185..0ffd269d 100644 --- a/wasm/src/data/traversal.rs +++ b/wasm/src/data/traversal.rs @@ -6,7 +6,7 @@ use crate::{ edge_sorting::EdgeSorter, graph::NoteGraph, traversal::path::{Path, PathList}, - utils::{self, LOGGER}, + utils, }; #[wasm_bindgen] @@ -22,6 +22,8 @@ pub struct TraversalData { /// the children of the node #[wasm_bindgen(getter_with_clone)] pub children: Vec, + /// whether the node has a cut of children due to being at the depth limit of a traversal, or similar + pub has_cut_of_children: bool, } #[wasm_bindgen] @@ -32,12 +34,14 @@ impl TraversalData { depth: u32, number_of_children: u32, children: Vec, + has_cut_of_children: bool, ) -> TraversalData { TraversalData { edge, depth, number_of_children, children, + has_cut_of_children, } } @@ -88,22 +92,23 @@ pub struct TraversalResult { pub data: Vec, pub node_count: u32, pub max_depth: u32, + pub hit_depth_limit: bool, pub traversal_time: u64, } #[wasm_bindgen] impl TraversalResult { #[wasm_bindgen(constructor)] - pub fn new( - data: Vec, - node_count: u32, - max_depth: u32, - traversal_time: u64, - ) -> TraversalResult { + pub fn new(mut data: Vec, traversal_time: u64) -> TraversalResult { + let node_count = rec_count_children(&mut data); + let max_depth = rec_find_max_depth(&data); + let hit_depth_limit = data.iter().any(|datum| datum.has_cut_of_children); + TraversalResult { data, node_count, max_depth, + hit_depth_limit, traversal_time, } } @@ -136,12 +141,13 @@ impl TraversalResult { } impl TraversalResult { - /// Flattens the traversal data by removing the tree structure and deduplicating the edges by their target_path - pub fn flatten(&mut self, graph: &NoteGraph) -> utils::Result<()> { + /// Squashes the traversal data by removing the tree structure and deduplicating the edges by their target_path + /// Essentially, this will result in some kind of reachability set. + pub fn squash(&mut self, graph: &NoteGraph) -> utils::Result<()> { let mut data = Vec::new(); for datum in self.data.drain(..) { - rec_flatten_traversal_data(datum, &mut data); + rec_squash_traversal_data(datum, &mut data); } for datum in &data { @@ -164,11 +170,36 @@ impl TraversalResult { sorter.sort_traversal_data(graph, &mut self.data) } + + pub fn flatten(self) -> FlatTraversalResult { + FlatTraversalResult::from_traversal_result(self) + } } -fn rec_flatten_traversal_data(mut data: TraversalData, result: &mut Vec) { +/// Recursively counts the number of children of the given traversal data. +/// This also updates the number_of_children field of each traversal data. +fn rec_count_children(data: &mut [TraversalData]) -> u32 { + let mut total_children = 0; + + for datum in data.iter_mut() { + datum.number_of_children = rec_count_children(&mut datum.children); + total_children += 1 + datum.number_of_children; + } + + total_children +} + +/// Recursively finds the maximum depth of the given traversal data. +fn rec_find_max_depth(data: &[TraversalData]) -> u32 { + data.iter() + .map(|datum| u32::max(rec_find_max_depth(&datum.children), datum.depth)) + .max() + .unwrap_or(0) +} + +fn rec_squash_traversal_data(mut data: TraversalData, result: &mut Vec) { for child in data.children.drain(..) { - rec_flatten_traversal_data(child, result); + rec_squash_traversal_data(child, result); } result.push(data); @@ -187,6 +218,7 @@ pub struct FlatTraversalData { /// the children of the node #[wasm_bindgen(getter_with_clone)] pub children: Vec, + pub has_cut_of_children: bool, } impl FlatTraversalData { @@ -195,12 +227,14 @@ impl FlatTraversalData { depth: u32, number_of_children: u32, children: Vec, + has_cut_of_children: bool, ) -> FlatTraversalData { FlatTraversalData { edge, depth, number_of_children, children, + has_cut_of_children, } } } @@ -223,6 +257,7 @@ pub struct FlatTraversalResult { pub data: Vec, pub node_count: u32, pub max_depth: u32, + pub hit_depth_limit: bool, pub traversal_time: u64, #[wasm_bindgen(getter_with_clone)] pub entry_nodes: Vec, @@ -233,6 +268,7 @@ impl FlatTraversalResult { data: Vec, node_count: u32, max_depth: u32, + hit_depth_limit: bool, traversal_time: u64, entry_nodes: Vec, ) -> FlatTraversalResult { @@ -240,23 +276,25 @@ impl FlatTraversalResult { data, node_count, max_depth, + hit_depth_limit, traversal_time, entry_nodes, } } - pub fn from_rec_traversal_result(result: TraversalResult) -> FlatTraversalResult { + pub fn from_traversal_result(result: TraversalResult) -> FlatTraversalResult { let mut flat_data = Vec::new(); let mut entry_nodes = Vec::new(); for datum in result.data { - entry_nodes.push(rec_flatten_traversal_data_to_flat(datum, &mut flat_data)); + entry_nodes.push(rec_flatten_traversal_data(datum, &mut flat_data)); } FlatTraversalResult::new( flat_data, result.node_count, result.max_depth, + result.hit_depth_limit, result.traversal_time, entry_nodes, ) @@ -278,9 +316,9 @@ impl FlatTraversalResult { self.data.get(index).cloned() } + /// Sorts the flat traversal data with a given edge sorter. + /// This is not as efficient as sorting the traversal data before flattening it, but it's still a lot better than sorting then re-flatten. pub fn sort(&mut self, graph: &NoteGraph, sorter: &EdgeSorter) -> utils::Result<()> { - LOGGER.warn(&format!("Entry nodes: {:?}", self.entry_nodes)); - let cloned_edges = self .data .iter() @@ -290,23 +328,20 @@ impl FlatTraversalResult { for datum in &mut self.data { sorter.sort_flat_traversal_data(graph, &cloned_edges, &mut datum.children)?; } - sorter.sort_flat_traversal_data(graph, &cloned_edges, &mut self.entry_nodes)?; - LOGGER.warn(&format!("Entry nodes: {:?}", self.entry_nodes)); - Ok(()) } } -fn rec_flatten_traversal_data_to_flat( +fn rec_flatten_traversal_data( mut data: TraversalData, result: &mut Vec, ) -> usize { let children = data .children .drain(..) - .map(|datum| rec_flatten_traversal_data_to_flat(datum, result)) + .map(|datum| rec_flatten_traversal_data(datum, result)) .collect(); result.push(FlatTraversalData::new( @@ -314,6 +349,7 @@ fn rec_flatten_traversal_data_to_flat( data.depth, data.number_of_children, children, + data.has_cut_of_children, )); result.len() - 1 } diff --git a/wasm/src/mermaid.rs b/wasm/src/mermaid.rs index 9a07f4b7..ab8c37ba 100644 --- a/wasm/src/mermaid.rs +++ b/wasm/src/mermaid.rs @@ -265,7 +265,7 @@ impl NoteGraph { label.push_str( forward .iter() - .map(|edge| edge.get_attribute_label(&diagram_options.edge_label_attributes)) + .map(|edge| edge.attribute_label(&diagram_options.edge_label_attributes)) .collect::>() .join(", ") .as_str(), @@ -276,7 +276,7 @@ impl NoteGraph { label.push_str( backward .iter() - .map(|edge| edge.get_attribute_label(&diagram_options.edge_label_attributes)) + .map(|edge| edge.attribute_label(&diagram_options.edge_label_attributes)) .collect::>() .join(", ") .as_str(), diff --git a/wasm/src/traversal/mod.rs b/wasm/src/traversal/mod.rs index 2500a693..d869b33a 100644 --- a/wasm/src/traversal/mod.rs +++ b/wasm/src/traversal/mod.rs @@ -88,15 +88,10 @@ impl NoteGraph { } } - let node_count = NoteGraph::int_rec_traversal_data_count_children(&mut result); - let max_depth = NoteGraph::int_rec_traversal_data_max_depth(&result); - let total_elapsed = now.elapsed(); Ok(TraversalResult::new( result, - node_count, - max_depth, total_elapsed.as_millis() as u64, )) } @@ -111,14 +106,14 @@ impl NoteGraph { let mut result = self.rec_traverse(options)?; if postprocess_options.flatten { - result.flatten(self)?; + result.squash(self)?; } if let Some(sorter) = &postprocess_options.sorter { result.sort(self, sorter)?; } - Ok(FlatTraversalResult::from_rec_traversal_result(result)) + Ok(result.flatten()) } } @@ -136,8 +131,9 @@ impl NoteGraph { traversal_count: &mut u32, ) -> Result { let mut new_children = Vec::new(); + let at_depth_limit = depth >= max_depth; - if depth < max_depth { + if !at_depth_limit { for outgoing_edge in self.graph.edges(node) { let edge_data = outgoing_edge.weight(); @@ -172,31 +168,13 @@ impl NoteGraph { } } - Ok(TraversalData::new(edge, depth, 0, new_children)) - } - - fn int_rec_traversal_data_count_children(data: &mut [TraversalData]) -> u32 { - let mut total_children = 0; - - for datum in data.iter_mut() { - datum.number_of_children = - NoteGraph::int_rec_traversal_data_count_children(&mut datum.children); - total_children += 1 + datum.number_of_children; - } - - total_children - } - - fn int_rec_traversal_data_max_depth(data: &[TraversalData]) -> u32 { - data.iter() - .map(|datum| { - u32::max( - NoteGraph::int_rec_traversal_data_max_depth(&datum.children), - datum.depth, - ) - }) - .max() - .unwrap_or(0) + Ok(TraversalData::new( + edge, + depth, + 0, + new_children, + at_depth_limit, + )) } pub fn int_traverse_basic( @@ -255,7 +233,7 @@ impl NoteGraph { /// These lists are ordered by the order in which the nodes and edges were visited. /// Each node and edge is only visited once. /// At the depth limit, edges are only visited if they point to already visited nodes. - fn int_traverse_depth_first<'a, N, E>( + pub fn int_traverse_depth_first<'a, N, E>( &'a self, entry_nodes: Vec, edge_types: Option<&Vec>>, @@ -281,7 +259,7 @@ impl NoteGraph { /// These lists are ordered by the order in which the nodes and edges were visited. /// Each node and edge is only visited once. /// At the depth limit, edges are only visited if they point to already visited nodes. - fn int_traverse_breadth_first<'a, N, E>( + pub fn int_traverse_breadth_first<'a, N, E>( &'a self, entry_nodes: Vec, edge_types: Option<&Vec>>, diff --git a/wasm/src/traversal/path.rs b/wasm/src/traversal/path.rs index 37c90fcf..8ea6348f 100644 --- a/wasm/src/traversal/path.rs +++ b/wasm/src/traversal/path.rs @@ -88,6 +88,7 @@ impl PathList { .unwrap_or(0) } + /// Cuts off all paths at a given depth, then sorts and deduplicates them. pub fn process(&self, graph: &NoteGraph, depth: usize) -> utils::Result> { let paths = self .paths diff --git a/wasm/src/update/graph.rs b/wasm/src/update/graph.rs index f50648a3..e6e8fa39 100644 --- a/wasm/src/update/graph.rs +++ b/wasm/src/update/graph.rs @@ -39,7 +39,7 @@ impl UpdateableGraph for NoteGraph { } None => { let node_path = data.path.clone(); - let node_index = self.graph.add_node(NodeData::from_construction_data(data)); + let node_index = self.graph.add_node(data.into()); self.node_hash.insert(node_path, node_index); } } From 51b94d5155ed655e5a02b86a1cc61a870ac550fc Mon Sep 17 00:00:00 2001 From: Moritz Jung Date: Fri, 10 Jan 2025 00:29:32 +0100 Subject: [PATCH 56/65] some code comments --- package-lock.json | 2 +- wasm/rust-toolchain | 1 + wasm/src/data/rules.rs | 8 ++++ wasm/src/graph.rs | 84 +++++++++++++++++++++++----------------- wasm/src/update/graph.rs | 6 +++ 5 files changed, 64 insertions(+), 37 deletions(-) create mode 100644 wasm/rust-toolchain diff --git a/package-lock.json b/package-lock.json index 854b2b20..efa2d1f1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -37,7 +37,7 @@ "svelte-preprocess": "^6.0.3", "tailwindcss": "^3.4.1", "tslib": "2.6.2", - "typescript": "^5.3.3", + "typescript": "^5.5.0", "vitest": "^1.3.1" } }, diff --git a/wasm/rust-toolchain b/wasm/rust-toolchain new file mode 100644 index 00000000..bf867e0a --- /dev/null +++ b/wasm/rust-toolchain @@ -0,0 +1 @@ +nightly diff --git a/wasm/src/data/rules.rs b/wasm/src/data/rules.rs index 5ea0b18f..f2afb3cd 100644 --- a/wasm/src/data/rules.rs +++ b/wasm/src/data/rules.rs @@ -102,10 +102,18 @@ impl TransitiveGraphRule { Rc::clone(&self.name) } + pub fn name_ref(&self) -> &str { + &self.name + } + pub fn edge_type(&self) -> Rc { Rc::clone(&self.edge_type) } + pub fn edge_type_ref(&self) -> &str { + &self.edge_type + } + pub fn iter_path(&self) -> impl Iterator> { self.path.iter() } diff --git a/wasm/src/graph.rs b/wasm/src/graph.rs index 8b4ba648..2de8813a 100644 --- a/wasm/src/graph.rs +++ b/wasm/src/graph.rs @@ -13,7 +13,7 @@ use crate::{ data::{ construction::{GCEdgeData, GCNodeData}, edge::EdgeData, - edge_list::GroupedEdgeList, + edge_list::{EdgeList, GroupedEdgeList}, edge_struct::EdgeStruct, node::NodeData, rules::TransitiveGraphRule, @@ -38,6 +38,10 @@ pub fn edge_matches_edge_filter_string(edge: &EdgeData, edge_types: Option<&Vec< } } +/// A graph that stores notes and their relationships. +/// +/// INVARIANT: The edge type tracker should contain exactly the edge types that are present in the graph. +/// INVARIANT: The node hash should contain exactly the node paths that are present in the graph. #[wasm_bindgen] #[derive(Clone)] pub struct NoteGraph { @@ -49,6 +53,7 @@ pub struct NoteGraph { pub edge_types: VecSet<[Rc; 16]>, #[wasm_bindgen(skip)] pub node_hash: HashMap, + /// A JS function that is called after every update to the graph, notifying the JS side that there were changes in the graph, but not which changes. update_callback: Option, /// A revision number that is incremented after every update. /// This can be used to check if the graph has changed. @@ -74,7 +79,7 @@ impl NoteGraph { self.update_callback = Some(callback); } - /// Call the update callback. + /// Notify the JS side that the graph has been updated. pub fn notify_update(&self) { if let Some(callback) = &self.update_callback { match callback.call0(&JsValue::NULL) { @@ -93,8 +98,8 @@ impl NoteGraph { } } - /// Builds the graph from a list of nodes and edges. - /// Will delete all existing data. + /// Builds the graph from a list of nodes, edges, and transitive rules. + /// All existing data in the graph is removed. pub fn build_graph( &mut self, nodes: Vec, @@ -120,12 +125,14 @@ impl NoteGraph { } /// Applies a batch update to the graph. - /// Throws an error if the update fails. + /// Throws an error if the update fails, and leave the graph in an inconsistent state. + /// + /// TODO: some security against errors leaving the graph in an inconsistent state. Maybe safely clear the entire graph. pub fn apply_update(&mut self, update: BatchGraphUpdate) -> Result<()> { let mut perf_logger = PerfLogger::new("Applying Update".to_owned()); perf_logger.start_split("Removing implied edges".to_owned()); - self.int_remove_implied_edges_unchecked(); + self.int_remove_implied_edges(); perf_logger.start_split("Applying updates".to_owned()); @@ -148,7 +155,7 @@ impl NoteGraph { Ok(()) } - /// Iterate all nodes in the graph and call the provided function with the [NodeData] of each node. + /// Iterate all nodes in the graph and call the provided function with each [NodeData]. pub fn iterate_nodes(&self, f: &js_sys::Function) { let this = JsValue::NULL; @@ -161,7 +168,7 @@ impl NoteGraph { }); } - /// Iterate all edges in the graph and call the provided function with the [EdgeData] of each edge. + /// Iterate all edges in the graph and call the provided function with each [EdgeData]. pub fn iterate_edges(&self, f: &js_sys::Function) { let this = JsValue::NULL; @@ -175,16 +182,16 @@ impl NoteGraph { } /// Get all outgoing edges from a node. - pub fn get_outgoing_edges(&self, node: String) -> Vec { + pub fn get_outgoing_edges(&self, node: String) -> EdgeList { let node_index = self.int_get_node_index(&node); - match node_index { + EdgeList::from_vec(match node_index { Some(node_index) => self .int_iter_outgoing_edges(node_index) .map(|edge| EdgeStruct::from_edge_ref(edge, self)) .collect(), None => Vec::new(), - } + }) } /// Get all outgoing edges from a node, filtered and grouped by edge type. @@ -208,16 +215,16 @@ impl NoteGraph { } /// Get all incoming edges to a node. - pub fn get_incoming_edges(&self, node: String) -> Vec { + pub fn get_incoming_edges(&self, node: String) -> EdgeList { let node_index = self.int_get_node_index(&node); - match node_index { + EdgeList::from_vec(match node_index { Some(node_index) => self .int_iter_incoming_edges(node_index) .map(|edge| EdgeStruct::from_edge_ref(edge, self)) .collect(), None => Vec::new(), - } + }) } /// Checks if a node exists in the graph. @@ -257,8 +264,8 @@ impl Default for NoteGraph { } /// Internal methods, not exposed to the wasm interface. -/// All of these methods are prefixed with `int_`. impl NoteGraph { + /// Get the current revision number of the graph, useful to check if [EdgeStruct]s have changed. pub fn get_revision(&self) -> u32 { self.revision } @@ -284,9 +291,9 @@ impl NoteGraph { // a rule like [A, B] -> (C with back edge D) would do nothing if applied multiple times, since the edges on the left side were not modified let mut edge_type_tracker = self.edge_types.clone(); - let mut edges_to_add: Vec<(NGNodeIndex, NGNodeIndex, EdgeData)> = Vec::new(); + let mut edges_to_add: Vec<(NGNodeIndex, NGNodeIndex, &TransitiveGraphRule)> = Vec::new(); - for i in 1..(max_rounds + 1) { + for i in 1..=max_rounds { let round_perf_split = perf_split.start_split(format!("Round {}", i)); if edge_type_tracker.is_empty() { @@ -312,10 +319,14 @@ impl NoteGraph { continue; } + // For every rule (outer loop) we iterate over all nodes in the graph (this loop) + // and check for all possible applications of that rule for that node. for start_node in self.graph.node_indices() { - // let path = rule.path.clone(); + // We start with the start node. let mut current_nodes = vec![start_node]; + // Now we iterate the path of the rule and each step, for all current nodes, + // we check for outgoing edges that match the edge type of the current element of the rule path. for edge_type in rule.iter_path() { let mut next_nodes = Vec::new(); @@ -330,22 +341,17 @@ impl NoteGraph { current_nodes = next_nodes; } + // Now we are left with end nodes. For each end node, there exists a path from the start node to the end node that matches the rule. for end_node in current_nodes { - // if the rule can't loop and the start and end node are the same, we skip the edge + // If the rule can't loop, that means the start and end nodes can't be the same. if !rule.can_loop() && start_node == end_node { continue; } - // if self.int_has_edge(start_node, end_node, &rule.edge_type()) { - // continue; - // } - - let edge_data = EdgeData::new(rule.edge_type(), rule.name(), false, i); - if rule.close_reversed() { - edges_to_add.push((end_node, start_node, edge_data)); + edges_to_add.push((end_node, start_node, rule)); } else { - edges_to_add.push((start_node, end_node, edge_data)); + edges_to_add.push((start_node, end_node, rule)); } } } @@ -355,15 +361,19 @@ impl NoteGraph { round_perf_split.start_split(format!("Adding {} Edges", edges_to_add.len())); - for (from, to, edge_data) in edges_to_add.drain(..) { - if self.int_has_edge(from, to, &edge_data.edge_type) { + for (from, to, rule) in edges_to_add.drain(..) { + if self.int_has_edge(from, to, rule.edge_type_ref()) { continue; } - self.edge_types.insert(Rc::clone(&edge_data.edge_type)); - edge_type_tracker.insert(Rc::clone(&edge_data.edge_type)); + self.edge_types.insert(rule.edge_type()); + edge_type_tracker.insert(rule.edge_type()); - self.graph.add_edge(from, to, edge_data); + self.graph.add_edge( + from, + to, + EdgeData::new(rule.edge_type(), rule.name(), false, i), + ); } round_perf_split.stop(); @@ -381,8 +391,9 @@ impl NoteGraph { } /// Removes all implied edges from the graph. - /// UNCHECKED: This does not update the edge type tracker. - pub fn int_remove_implied_edges_unchecked(&mut self) { + /// + /// INVARIANTS: This does not update the edge type tracker. + pub fn int_remove_implied_edges(&mut self) { let edge_count = self.graph.edge_count(); self.graph.retain_edges(|frozen_graph, edge| { @@ -413,12 +424,13 @@ impl NoteGraph { self.graph.node_count() } - /// Returns the node index for a specific node weight. + /// Get the node index for a given node path. + /// Returns `None` if there is no node with that path. pub fn int_get_node_index(&self, node: &str) -> Option { self.node_hash.get(node).copied() } - /// Returns the node weight for a specific node index. + /// Returns the [NodeData] for a specific node index. /// /// Will return an error if the node is not found. pub fn int_get_node_weight(&self, node: NGNodeIndex) -> Result<&NodeData> { diff --git a/wasm/src/update/graph.rs b/wasm/src/update/graph.rs index e6e8fa39..4b8d8cde 100644 --- a/wasm/src/update/graph.rs +++ b/wasm/src/update/graph.rs @@ -124,7 +124,11 @@ impl UpdateableGraph for NoteGraph { } } +/// Helper methods for the impl above. impl NoteGraph { + /// Given an edge index, removes the edge from the graph. + /// If the target node is unresolved and has no incoming edges, it will be removed as well. + /// /// INVARIANT: This function does not update the edge type tracker. fn remove_edge_by_index(&mut self, edge_index: NGEdgeIndex) -> utils::Result<()> { let (_, target) = self @@ -145,6 +149,8 @@ impl NoteGraph { Ok(()) } + /// Given a node index and the name of a node, removes it from the graph and the node hash. + /// /// INVARIANT: This function does not update the edge type tracker. fn remove_node_by_index_and_name(&mut self, node_index: NGNodeIndex, name: &str) { self.node_hash.remove(name); From a8ad005ee8f26d25c045394dca0c41f78ad02f60 Mon Sep 17 00:00:00 2001 From: Moritz Jung Date: Fri, 10 Jan 2025 00:52:33 +0100 Subject: [PATCH 57/65] fix build errors; more comments --- src/commands/freeze_edges/index.ts | 1 + src/commands/jump/index.ts | 1 + src/commands/thread/index.ts | 1 + src/components/page_views/PrevNextView.svelte | 1 + src/utils/mermaid.ts | 225 +----------------- wasm/rustfmt.toml | 3 +- wasm/src/data/edge.rs | 3 +- wasm/src/data/traversal.rs | 11 +- wasm/src/graph.rs | 64 +++-- wasm/src/mermaid.rs | 3 +- wasm/src/traversal/mod.rs | 45 ++-- wasm/src/traversal/options.rs | 5 +- wasm/src/traversal/path.rs | 3 +- wasm/src/update/graph.rs | 9 +- 14 files changed, 102 insertions(+), 273 deletions(-) diff --git a/src/commands/freeze_edges/index.ts b/src/commands/freeze_edges/index.ts index 1e73c01e..53032bb9 100644 --- a/src/commands/freeze_edges/index.ts +++ b/src/commands/freeze_edges/index.ts @@ -12,6 +12,7 @@ export const freeze_implied_edges_to_note = async ( ) => { const implied_edges = plugin.graph .get_outgoing_edges(source_file.path) + .get_edges() .filter( // Don't freeze a note to itself (self_is_sibling) (e) => !e.is_self_loop() && !e.explicit, diff --git a/src/commands/jump/index.ts b/src/commands/jump/index.ts index ad3fba8b..de554183 100644 --- a/src/commands/jump/index.ts +++ b/src/commands/jump/index.ts @@ -12,6 +12,7 @@ export const jump_to_neighbour = async ( const matches = plugin.graph .get_outgoing_edges(active_file.path) + .get_edges() .filter( (e) => e.matches_edge_filter(plugin.graph, options.fields) && diff --git a/src/commands/thread/index.ts b/src/commands/thread/index.ts index 5f17031a..24102f7e 100644 --- a/src/commands/thread/index.ts +++ b/src/commands/thread/index.ts @@ -71,6 +71,7 @@ export const thread = async ( const edge = plugin.graph .get_outgoing_edges(source_file.path) + .get_edges() .find( (e) => e.edge_type === field && diff --git a/src/components/page_views/PrevNextView.svelte b/src/components/page_views/PrevNextView.svelte index a273b70f..11d7a760 100644 --- a/src/components/page_views/PrevNextView.svelte +++ b/src/components/page_views/PrevNextView.svelte @@ -40,6 +40,7 @@ ? group_by( plugin.graph .get_outgoing_edges(file_path) + .get_edges() .filter((e) => e.matches_edge_filter( plugin.graph, diff --git a/src/utils/mermaid.ts b/src/utils/mermaid.ts index af8cf7d1..bbae1a22 100644 --- a/src/utils/mermaid.ts +++ b/src/utils/mermaid.ts @@ -1,20 +1,9 @@ -// import type { -// BCEdge, -// BCEdgeAttributes, -// BCNodeAttributes, -// EdgeAttribute, -// } from "src/graph/MyMultiGraph"; -// import { remove_duplicates_by } from "./arrays"; -// import { remove_nullish_keys, untyped_pick } from "./objects"; -// import { url_search_params } from "./url"; - import type { BreadcrumbsSettings } from "src/interfaces/settings"; import { MermaidGraphOptions, NodeData, TransitiveGraphRule, TraversalOptions, - create_graph_from_rule, } from "wasm/pkg/breadcrumbs_graph_wasm"; const MERMAID_DIRECTIONS = ["LR", "RL", "TB", "BT"] as const; @@ -39,218 +28,6 @@ const MERMAID_CURVE_STYLES = [ ] as const; type MermaidCurveStyle = (typeof MERMAID_CURVE_STYLES)[number]; -// type MermaidEdge = { -// source_i: number; -// target_i: number; -// arrow: string; -// attr: BCEdgeAttributes; -// collapsed_attr: Record>; -// }; - -// const build_arrow = (e: { attr: Pick }) => -// e.attr.explicit ? "-->" : "-.->"; - -// const build_attrs = ( -// attr: Record, -// show_attributes?: EdgeAttribute[], -// ) => { -// const params = show_attributes?.length -// ? url_search_params(untyped_pick(attr, show_attributes), { -// trim_lone_param: true, -// }) -// : null; - -// // Only add the attributes if there are any, otherwise Mermaid will throw an error -// return params?.length ? `|"${params}"|` : ""; -// }; - -// const from_edges = ( -// edges: Omit[], -// config?: { -// active_node_id?: string; -// renderer?: MermaidRenderer; -// kind?: "flowchart" | "graph"; -// direction?: MermaidDirection; -// curve_style?: MermaidCurveStyle; -// show_attributes?: EdgeAttribute[]; -// collapse_opposing_edges?: false; -// get_node_label?: (id: string, attr: BCNodeAttributes) => string; -// click?: -// | { method: "class" } -// | { method: "callback"; callback_name: string } -// | { -// method: "href"; -// getter: (id: string, attr: BCNodeAttributes) => string; -// }; -// }, -// ) => { -// const resolved = Object.assign( -// { direction: "LR", kind: "flowchart" }, -// remove_nullish_keys( -// config ?? ({} as unknown as NonNullable), -// ), -// ); - -// const flowchart_init = remove_nullish_keys({ -// curve: resolved.curve_style, -// defaultRenderer: resolved.renderer, -// }); - -// const lines = [ -// // NOTE: Regardless of kind, the below field should always be flowchart -// `%%{ init: { "flowchart": ${JSON.stringify(flowchart_init)} } }%%`, -// `${resolved.kind} ${resolved.direction}`, -// ]; - -// const node_map = remove_duplicates_by( -// // NOTE: This is _pretty_ inefficient, but necessary. -// // If we just take all unique target_ids, we miss source nodes that don't have any incoming edges. -// edges.flatMap((e) => [ -// { path: e.source_id, attr: e.source_attr }, -// { path: e.target_id, attr: e.target_attr }, -// ]), -// (n) => n.path, -// ).reduce( -// (map, node, i) => -// map.set(node.path, { -// i, -// attr: node.attr, -// label: -// resolved.get_node_label?.(node.path, node.attr) ?? -// node.path, -// }), -// new Map(), -// ); - -// // Declare the labeled nodes -// node_map.forEach((node) => { -// lines.push(`\t${node.i}("${node.label}")`); -// }); - -// lines.push(""); - -// // Collapse opposing edges to and from the same nodes -// // e.g. A -->|same| B -->|same| A becomes A <-->|same| B -// const mermaid_edges: MermaidEdge[] = []; - -// for (const edge of edges) { -// const [source_i, target_i] = [ -// node_map.get(edge.source_id)!.i, -// node_map.get(edge.target_id)!.i, -// ]; - -// const opposing_edge_i = -// resolved.collapse_opposing_edges !== false -// ? mermaid_edges.findIndex( -// (existing) => -// // NOTE: This is pretty intense, all opposing edges will collapse, because now there's no direction semantics -// target_i === existing.source_i && -// source_i === existing.target_i, -// ) -// : -1; - -// if (opposing_edge_i === -1) { -// // If there is no opposing edge, add the original edge -// mermaid_edges.push({ -// source_i, -// target_i, -// arrow: build_arrow(edge), -// attr: edge.attr, -// collapsed_attr: Object.fromEntries( -// resolved.show_attributes?.map((attr) => [ -// attr, -// new Set([ -// // @ts-ignore: If the property is not in the object, it will be undefined -// edge.attr[attr] ?? "_", -// ]), -// ]) ?? [], -// ), -// }); -// } else { -// // If there is an opposing edge, collapse them into a single edge -// const existing = mermaid_edges[opposing_edge_i]; - -// existing.arrow = -// edge.attr.explicit || existing.attr.explicit ? "---" : "-.-"; - -// resolved.show_attributes?.forEach((attr) => { -// existing.collapsed_attr[attr].add( -// // @ts-ignore: If the property is not in the object, it will be undefined -// edge.attr[attr] ?? "_", -// ); -// }); -// } -// } - -// // Add the edges -// mermaid_edges.forEach(({ arrow, collapsed_attr, source_i, target_i }) => { -// const attrs = build_attrs( -// Object.fromEntries( -// Object.entries(collapsed_attr).map(([key, set]) => [ -// key, -// [...set.values()].join("|"), -// ]), -// ), -// resolved.show_attributes, -// ); - -// lines.push(`\t${source_i} ${arrow}${attrs} ${target_i}`); -// }); - -// lines.push(""); - -// const active_note_i = resolved.active_node_id -// ? node_map.get(resolved.active_node_id)?.i -// : undefined; - -// if (active_note_i !== undefined) { -// lines.push(`\tclass ${active_note_i} BC-active-node`); -// } - -// switch (resolved.click?.method) { -// case "class": { -// const nodes = [...node_map.values()]; - -// if (nodes.length) { -// lines.push( -// `\tclass ${nodes.filter((n) => n.i !== active_note_i).map((n) => n.i)} internal-link`, -// ); -// } - -// const unresolved_nodes = nodes.filter((n) => !n.attr.resolved); -// if (unresolved_nodes.length) { -// lines.push( -// `\tclass ${unresolved_nodes.map((n) => n.i)} is-unresolved`, -// ); -// } - -// break; -// } - -// case "href": { -// node_map.forEach((node, path) => { -// lines.push( -// `\tclick ${node.i} "${(>resolved.click)?.getter(path, node.attr)}"`, -// ); -// }); - -// break; -// } - -// case "callback": { -// node_map.forEach((node) => { -// lines.push( -// `\tclick ${node.i} call ${(>resolved.click)?.callback_name}()`, -// ); -// }); - -// break; -// } -// } - -// return lines.join("\n"); -// }; - const _encode = (code: string) => { const bytes = new TextEncoder().encode(code); return btoa(String.fromCharCode(...bytes)); @@ -302,7 +79,7 @@ const from_transitive_rule = ( rule.close_reversed, ); - const graph = create_graph_from_rule(wasm_rule); + const graph = wasm_rule.create_example_graph(); return graph.generate_mermaid_graph( new TraversalOptions(["1"], undefined, 100, false), diff --git a/wasm/rustfmt.toml b/wasm/rustfmt.toml index cb7b6072..32846ff7 100644 --- a/wasm/rustfmt.toml +++ b/wasm/rustfmt.toml @@ -1,2 +1,3 @@ imports_granularity = "Crate" -group_imports = "StdExternalCrate" \ No newline at end of file +group_imports = "StdExternalCrate" +wrap_comments = true \ No newline at end of file diff --git a/wasm/src/data/edge.rs b/wasm/src/data/edge.rs index 9a559ef2..0304f5ef 100644 --- a/wasm/src/data/edge.rs +++ b/wasm/src/data/edge.rs @@ -58,7 +58,8 @@ impl EdgeData { // the mapping that exist on the JS side are as follows // "field" | "explicit" | "source" | "implied_kind" | "round" - // TODO(JS): maybe change the attribute options so that the JS side better matches the data + // TODO(JS): maybe change the attribute options so that the JS side better + // matches the data for attribute in attributes { let data = match attribute.as_str() { "field" => Some(("field", self.edge_type.to_string())), diff --git a/wasm/src/data/traversal.rs b/wasm/src/data/traversal.rs index 0ffd269d..83f588cd 100644 --- a/wasm/src/data/traversal.rs +++ b/wasm/src/data/traversal.rs @@ -22,7 +22,8 @@ pub struct TraversalData { /// the children of the node #[wasm_bindgen(getter_with_clone)] pub children: Vec, - /// whether the node has a cut of children due to being at the depth limit of a traversal, or similar + /// whether the node has a cut of children due to being at the depth limit + /// of a traversal, or similar pub has_cut_of_children: bool, } @@ -141,8 +142,9 @@ impl TraversalResult { } impl TraversalResult { - /// Squashes the traversal data by removing the tree structure and deduplicating the edges by their target_path - /// Essentially, this will result in some kind of reachability set. + /// Squashes the traversal data by removing the tree structure and + /// deduplicating the edges by their target_path Essentially, this will + /// result in some kind of reachability set. pub fn squash(&mut self, graph: &NoteGraph) -> utils::Result<()> { let mut data = Vec::new(); @@ -317,7 +319,8 @@ impl FlatTraversalResult { } /// Sorts the flat traversal data with a given edge sorter. - /// This is not as efficient as sorting the traversal data before flattening it, but it's still a lot better than sorting then re-flatten. + /// This is not as efficient as sorting the traversal data before flattening + /// it, but it's still a lot better than sorting then re-flatten. pub fn sort(&mut self, graph: &NoteGraph, sorter: &EdgeSorter) -> utils::Result<()> { let cloned_edges = self .data diff --git a/wasm/src/graph.rs b/wasm/src/graph.rs index 2de8813a..8f6f8101 100644 --- a/wasm/src/graph.rs +++ b/wasm/src/graph.rs @@ -40,8 +40,11 @@ pub fn edge_matches_edge_filter_string(edge: &EdgeData, edge_types: Option<&Vec< /// A graph that stores notes and their relationships. /// -/// INVARIANT: The edge type tracker should contain exactly the edge types that are present in the graph. -/// INVARIANT: The node hash should contain exactly the node paths that are present in the graph. +/// INVARIANT: The edge type tracker should contain exactly the edge types that +/// are present in the graph. +/// +/// INVARIANT: The node hash should contain exactly the node paths that are +/// present in the graph. #[wasm_bindgen] #[derive(Clone)] pub struct NoteGraph { @@ -53,7 +56,8 @@ pub struct NoteGraph { pub edge_types: VecSet<[Rc; 16]>, #[wasm_bindgen(skip)] pub node_hash: HashMap, - /// A JS function that is called after every update to the graph, notifying the JS side that there were changes in the graph, but not which changes. + /// A JS function that is called after every update to the graph, notifying + /// the JS side that there were changes in the graph, but not which changes. update_callback: Option, /// A revision number that is incremented after every update. /// This can be used to check if the graph has changed. @@ -125,9 +129,11 @@ impl NoteGraph { } /// Applies a batch update to the graph. - /// Throws an error if the update fails, and leave the graph in an inconsistent state. + /// Throws an error if the update fails, and leave the graph in an + /// inconsistent state. /// - /// TODO: some security against errors leaving the graph in an inconsistent state. Maybe safely clear the entire graph. + /// TODO: some security against errors leaving the graph in an inconsistent + /// state. Maybe safely clear the entire graph. pub fn apply_update(&mut self, update: BatchGraphUpdate) -> Result<()> { let mut perf_logger = PerfLogger::new("Applying Update".to_owned()); perf_logger.start_split("Removing implied edges".to_owned()); @@ -155,7 +161,8 @@ impl NoteGraph { Ok(()) } - /// Iterate all nodes in the graph and call the provided function with each [NodeData]. + /// Iterate all nodes in the graph and call the provided function with each + /// [NodeData]. pub fn iterate_nodes(&self, f: &js_sys::Function) { let this = JsValue::NULL; @@ -163,12 +170,14 @@ impl NoteGraph { match f.call1(&this, &node.weight().clone().into()) { Ok(_) => {} Err(e) => LOGGER.warn(&format!("Error calling node iteration callback: {:?}", e)), - // LOGGER.with(|l| l.warn(&format!("Error calling node iteration callback: {:?}", e))), + // LOGGER.with(|l| l.warn(&format!("Error calling node iteration callback: {:?}", + // e))), } }); } - /// Iterate all edges in the graph and call the provided function with each [EdgeData]. + /// Iterate all edges in the graph and call the provided function with each + /// [EdgeData]. pub fn iterate_edges(&self, f: &js_sys::Function) { let this = JsValue::NULL; @@ -176,7 +185,8 @@ impl NoteGraph { match f.call1(&this, &edge.weight().clone().into()) { Ok(_) => {} Err(e) => LOGGER.warn(&format!("Error calling edge iteration callback: {:?}", e)), - // LOGGER.with(|l| l.warn(&format!("Error calling edge iteration callback: {:?}", e))), + // LOGGER.with(|l| l.warn(&format!("Error calling edge iteration callback: {:?}", + // e))), } }); } @@ -265,7 +275,8 @@ impl Default for NoteGraph { /// Internal methods, not exposed to the wasm interface. impl NoteGraph { - /// Get the current revision number of the graph, useful to check if [EdgeStruct]s have changed. + /// Get the current revision number of the graph, useful to check if + /// [EdgeStruct]s have changed. pub fn get_revision(&self) -> u32 { self.revision } @@ -286,9 +297,10 @@ impl NoteGraph { // rules look like // [A, B, C] -> D - // We can keep track of edge types that were added in the last round and only check a rule that has any of those edge types on the left side - // we would need to also check back edges though - // a rule like [A, B] -> (C with back edge D) would do nothing if applied multiple times, since the edges on the left side were not modified + // We can keep track of edge types that were added in the last round and only + // check a rule that has any of those edge types on the left side. + // A rule like [A, B] -> C would do nothing if applied + // multiple times, since the edges on the left side were not modified. let mut edge_type_tracker = self.edge_types.clone(); let mut edges_to_add: Vec<(NGNodeIndex, NGNodeIndex, &TransitiveGraphRule)> = Vec::new(); @@ -296,6 +308,8 @@ impl NoteGraph { for i in 1..=max_rounds { let round_perf_split = perf_split.start_split(format!("Round {}", i)); + // if the edge type tracker is empty, we didn't add any edges last round, so we + // can stop if edge_type_tracker.is_empty() { break; } @@ -310,7 +324,8 @@ impl NoteGraph { continue; } - // if all edge types of a rule didn't see any changes in the last round, we can skip the rule + // if all edge types of a rule didn't see any changes in the last round, we can + // skip the rule if rule .iter_path() .all(|edge_type| !edge_type_tracker.contains(edge_type)) @@ -319,15 +334,19 @@ impl NoteGraph { continue; } - // For every rule (outer loop) we iterate over all nodes in the graph (this loop) - // and check for all possible applications of that rule for that node. + // For every rule (outer loop) we iterate over all nodes in the graph (this + // loop) and check for all possible applications of that rule + // for that node. for start_node in self.graph.node_indices() { // We start with the start node. let mut current_nodes = vec![start_node]; // Now we iterate the path of the rule and each step, for all current nodes, - // we check for outgoing edges that match the edge type of the current element of the rule path. + // we check for outgoing edges that match the edge type of the current element + // of the rule path. for edge_type in rule.iter_path() { + // TODO: We are creating a new vec here in a hot loop, maybe we can only + // create it once and reuse it. let mut next_nodes = Vec::new(); for current_node in current_nodes { @@ -341,9 +360,11 @@ impl NoteGraph { current_nodes = next_nodes; } - // Now we are left with end nodes. For each end node, there exists a path from the start node to the end node that matches the rule. + // Now we are left with end nodes. For each end node, there exists a path from + // the start node to the end node that matches the rule. for end_node in current_nodes { - // If the rule can't loop, that means the start and end nodes can't be the same. + // If the rule can't loop, that means the start and end nodes can't be the + // same. if !rule.can_loop() && start_node == end_node { continue; } @@ -357,6 +378,11 @@ impl NoteGraph { } } + // if there are no edges to add, we can stop + if edges_to_add.is_empty() { + break; + } + edge_type_tracker.retain(|_| false); round_perf_split.start_split(format!("Adding {} Edges", edges_to_add.len())); diff --git a/wasm/src/mermaid.rs b/wasm/src/mermaid.rs index ab8c37ba..81dbead4 100644 --- a/wasm/src/mermaid.rs +++ b/wasm/src/mermaid.rs @@ -302,7 +302,8 @@ impl NoteGraph { ) -> Result> { let mut accumulated_edges = AccumulatedEdgeHashMap::default(); - // sorting the two node indices in the edge tuple could be a speedup, since then only one lookup is needed + // sorting the two node indices in the edge tuple could be a speedup, since then + // only one lookup is needed for edge_struct in edges { edge_struct.check_revision(graph)?; diff --git a/wasm/src/traversal/mod.rs b/wasm/src/traversal/mod.rs index d869b33a..84cab504 100644 --- a/wasm/src/traversal/mod.rs +++ b/wasm/src/traversal/mod.rs @@ -97,7 +97,8 @@ impl NoteGraph { } /// Runs a recursive traversal of the graph and post-processes the result. - /// The post-processed result is more efficient to work with from JavaScript. + /// The post-processed result is more efficient to work with from + /// JavaScript. pub fn rec_traverse_and_process( &self, options: TraversalOptions, @@ -120,7 +121,8 @@ impl NoteGraph { impl NoteGraph { /// Recursively traverses the graph using DFS and builds a tree structure. /// - /// Will return an error if the node weight for any node along the traversal is not found. + /// Will return an error if the node weight for any node along the traversal + /// is not found. fn int_rec_traverse( &self, node: NGNodeIndex, @@ -227,12 +229,15 @@ impl NoteGraph { } } - /// Traverses the tree in a depth first manner and calls the provided callbacks for each node and edge. - /// The depth metric **might not** accurately represent the intuitive understanding of depth on a graph. - /// A list of tuples of node indices and the result of the node callback and a list of tuples of edge indices and the result of the edge callback are returned. - /// These lists are ordered by the order in which the nodes and edges were visited. - /// Each node and edge is only visited once. - /// At the depth limit, edges are only visited if they point to already visited nodes. + /// Traverses the tree in a depth first manner and calls the provided + /// callbacks for each node and edge. The depth metric **might not** + /// accurately represent the intuitive understanding of depth on a graph. + /// A list of tuples of node indices and the result of the node callback and + /// a list of tuples of edge indices and the result of the edge callback are + /// returned. These lists are ordered by the order in which the nodes + /// and edges were visited. Each node and edge is only visited once. + /// At the depth limit, edges are only visited if they point to already + /// visited nodes. pub fn int_traverse_depth_first<'a, N, E>( &'a self, entry_nodes: Vec, @@ -253,12 +258,15 @@ impl NoteGraph { ) } - /// Traverses the tree in a breadth first manner and calls the provided callbacks for each node and edge. - /// The depth metric accurately represent the intuitive understanding of depth on a graph. - /// A list of tuples of node indices and the result of the node callback and a list of tuples of edge indices and the result of the edge callback are returned. - /// These lists are ordered by the order in which the nodes and edges were visited. - /// Each node and edge is only visited once. - /// At the depth limit, edges are only visited if they point to already visited nodes. + /// Traverses the tree in a breadth first manner and calls the provided + /// callbacks for each node and edge. The depth metric accurately + /// represent the intuitive understanding of depth on a graph. A list of + /// tuples of node indices and the result of the node callback and a list of + /// tuples of edge indices and the result of the edge callback are returned. + /// These lists are ordered by the order in which the nodes and edges were + /// visited. Each node and edge is only visited once. + /// At the depth limit, edges are only visited if they point to already + /// visited nodes. pub fn int_traverse_breadth_first<'a, N, E>( &'a self, entry_nodes: Vec, @@ -309,13 +317,16 @@ impl NoteGraph { if edge_matches_edge_filter(edge_data, edge_types) { let already_visited = visited_nodes.contains(&target); - // we only add the edge if we are not at the depth limit or if we are at the depth limit and the target node is already in the depth map - // this captures all the outgoing edges from the nodes at the depth limit to nodes already present in the depth map + // we only add the edge if we are not at the depth limit or if we are at the + // depth limit and the target node is already in the depth map + // this captures all the outgoing edges from the nodes at the depth limit to + // nodes already present in the depth map if !at_depth_limit || already_visited { edge_list.push((edge.id(), edge_callback(edge))); } - // we only insert the new node when we are not at the depth limit and the node is not already in the depth map + // we only insert the new node when we are not at the depth limit and the node + // is not already in the depth map if !at_depth_limit && !already_visited { node_list.push((target, node_callback(target, current_depth + 1))); visited_nodes.insert(target); diff --git a/wasm/src/traversal/options.rs b/wasm/src/traversal/options.rs index d82a79f6..1e2713a9 100644 --- a/wasm/src/traversal/options.rs +++ b/wasm/src/traversal/options.rs @@ -11,8 +11,9 @@ pub struct TraversalOptions { #[wasm_bindgen(getter_with_clone)] pub edge_types: Option>, pub max_depth: u32, - /// if true, multiple traversals - one for each edge type - will be performed and the results will be combined - /// if false, one traversal over all edge types will be performed + /// if true, multiple traversals - one for each edge type - will be + /// performed and the results will be combined if false, one traversal + /// over all edge types will be performed pub separate_edges: bool, } diff --git a/wasm/src/traversal/path.rs b/wasm/src/traversal/path.rs index 8ea6348f..fc5ae3cb 100644 --- a/wasm/src/traversal/path.rs +++ b/wasm/src/traversal/path.rs @@ -120,7 +120,8 @@ impl PathList { } impl PathList { - /// creates new path list, assumes that the paths are already sorted by length + /// creates new path list, assumes that the paths are already sorted by + /// length pub fn new(paths: Vec) -> PathList { PathList { paths } } diff --git a/wasm/src/update/graph.rs b/wasm/src/update/graph.rs index 4b8d8cde..3ebb56e2 100644 --- a/wasm/src/update/graph.rs +++ b/wasm/src/update/graph.rs @@ -21,7 +21,8 @@ pub(super) trait UpdateableGraph { fn upd_remove_edge(&mut self, from: &str, to: &str, edge_type: &str) -> utils::Result<()>; } -/// INVARIANT: These update methods should keep the node_hash intact, but the edge type tracker can be made inconsistent. +/// INVARIANT: These update methods should keep the node_hash intact, but the +/// edge type tracker can be made inconsistent. impl UpdateableGraph for NoteGraph { fn upd_add_node(&mut self, data: GCNodeData) -> utils::Result<()> { // we check if the node already exists in the graph @@ -127,7 +128,8 @@ impl UpdateableGraph for NoteGraph { /// Helper methods for the impl above. impl NoteGraph { /// Given an edge index, removes the edge from the graph. - /// If the target node is unresolved and has no incoming edges, it will be removed as well. + /// If the target node is unresolved and has no incoming edges, it will be + /// removed as well. /// /// INVARIANT: This function does not update the edge type tracker. fn remove_edge_by_index(&mut self, edge_index: NGEdgeIndex) -> utils::Result<()> { @@ -149,7 +151,8 @@ impl NoteGraph { Ok(()) } - /// Given a node index and the name of a node, removes it from the graph and the node hash. + /// Given a node index and the name of a node, removes it from the graph and + /// the node hash. /// /// INVARIANT: This function does not update the edge type tracker. fn remove_node_by_index_and_name(&mut self, node_index: NGNodeIndex, name: &str) { From d349365fe30889ea780cf14328caafac6867f661 Mon Sep 17 00:00:00 2001 From: Moritz Jung Date: Fri, 10 Jan 2025 22:16:33 +0100 Subject: [PATCH 58/65] markmind codeblock --- src/codeblocks/MDRC.ts | 20 +- src/commands/jump/index.ts | 6 +- src/commands/list_index/index.ts | 59 ++++- .../codeblocks/CodeblockMarkmap.svelte | 230 +++++++++++++----- .../codeblocks/CodeblockMermaid.svelte | 2 +- .../codeblocks/CodeblockTree.svelte | 6 +- .../obsidian/RenderExternalCodeblock.svelte | 4 +- src/components/page_views/PrevNextView.svelte | 10 +- wasm/src/data/edge_list.rs | 16 ++ wasm/src/data/mod.rs | 1 + wasm/src/graph.rs | 17 +- 11 files changed, 259 insertions(+), 112 deletions(-) diff --git a/src/codeblocks/MDRC.ts b/src/codeblocks/MDRC.ts index 9704744a..743085c9 100644 --- a/src/codeblocks/MDRC.ts +++ b/src/codeblocks/MDRC.ts @@ -16,6 +16,7 @@ export class CodeblockMDRC extends MarkdownRenderChild { component: | ReturnType | ReturnType + | ReturnType | undefined; file_path: string; id: string; @@ -110,16 +111,15 @@ export class CodeblockMDRC extends MarkdownRenderChild { }, }); } else if (options.type === "markmap") { - // TODO - // this.component = mount(CodeblockMarkmap, { - // target: this.containerEl, - // props: { - // errors, - // options, - // file_path, - // plugin: this.plugin, - // }, - // }); + this.component = mount(CodeblockMarkmap, { + target: this.containerEl, + props: { + errors, + options, + file_path, + plugin: this.plugin, + }, + }); } else { log.error("CodeblockMDRC unknown type", options.type); } diff --git a/src/commands/jump/index.ts b/src/commands/jump/index.ts index de554183..deaa6115 100644 --- a/src/commands/jump/index.ts +++ b/src/commands/jump/index.ts @@ -11,12 +11,10 @@ export const jump_to_neighbour = async ( if (!active_file) return; const matches = plugin.graph - .get_outgoing_edges(active_file.path) + .get_filtered_outgoing_edges(active_file.path, options.fields) .get_edges() .filter( - (e) => - e.matches_edge_filter(plugin.graph, options.fields) && - e.target_path(plugin.graph) !== active_file.path, + (e) => e.target_path(plugin.graph) !== active_file.path, ); if (!matches.length) { diff --git a/src/commands/list_index/index.ts b/src/commands/list_index/index.ts index 62850119..e7de79b4 100644 --- a/src/commands/list_index/index.ts +++ b/src/commands/list_index/index.ts @@ -14,6 +14,7 @@ import { export namespace ListIndex { export type Options = { + show_entry_nodes: boolean; // TODO: merge_fields: boolean; indent: string; fields: string[]; @@ -27,6 +28,7 @@ export namespace ListIndex { }; export const DEFAULT_OPTIONS: Options = { + show_entry_nodes: false, fields: [], indent: "\\t", link_kind: "wiki", @@ -49,19 +51,50 @@ export namespace ListIndex { tree: FlatTraversalResult, options: Pick< Options, - "link_kind" | "indent" | "show_node_options" | "show_attributes" + "link_kind" | "indent" | "show_node_options" | "show_attributes" | "show_entry_nodes" >, ) => { const all_traversal_data = tree.data; - const current_nodes = Array.from(tree.entry_nodes).map( - (node_index) => all_traversal_data[node_index], - ); - return edge_tree_to_list_index_inner( - plugin, - all_traversal_data, - current_nodes, - options, - ); + + if (options.show_entry_nodes) { + return Array.from(tree.entry_nodes).map( + (node_index) => { + const datum = all_traversal_data[node_index]; + const edge = datum.edge; + + const display = edge.stringify_source( + plugin.graph, + toNodeStringifyOptions(plugin, options.show_node_options), + ); + + const link = Links.ify(edge.source_path(plugin.graph), display, { + link_kind: options.link_kind, + }); + + const attr = edge.get_attribute_label( + plugin.graph, + options.show_attributes, + ); + + return `- ${link} ${attr}\n` + edge_tree_to_list_index_inner( + plugin, + all_traversal_data, + [datum], + options, + ); + }, + ).join("\n"); + } else { + const current_nodes = Array.from(tree.entry_nodes).map( + (node_index) => all_traversal_data[node_index], + ); + return edge_tree_to_list_index_inner( + plugin, + all_traversal_data, + current_nodes, + options, + ); + } }; export const edge_tree_to_list_index_inner = ( @@ -76,7 +109,9 @@ export namespace ListIndex { let index = ""; const real_indent = options.indent.replace(/\\t/g, "\t"); - current_nodes.forEach(({ children, depth, edge }) => { + current_nodes.forEach((datum) => { + const { edge, children, depth } = datum; + const display = edge.stringify_target( plugin.graph, toNodeStringifyOptions(plugin, options.show_node_options), @@ -91,7 +126,7 @@ export namespace ListIndex { options.show_attributes, ); - index += real_indent.repeat(depth) + `- ${link}${attr}\n`; + index += real_indent.repeat(depth - 1) + `- ${link} ${attr}\n`; const new_children = Array.from(children).map( (child_id) => all_traversal_data[child_id], diff --git a/src/components/codeblocks/CodeblockMarkmap.svelte b/src/components/codeblocks/CodeblockMarkmap.svelte index 02249fee..47d64571 100644 --- a/src/components/codeblocks/CodeblockMarkmap.svelte +++ b/src/components/codeblocks/CodeblockMarkmap.svelte @@ -1,10 +1,8 @@ - +
diff --git a/src/components/codeblocks/CodeblockMermaid.svelte b/src/components/codeblocks/CodeblockMermaid.svelte index 2f76f670..5b03f3c8 100644 --- a/src/components/codeblocks/CodeblockMermaid.svelte +++ b/src/components/codeblocks/CodeblockMermaid.svelte @@ -38,7 +38,7 @@ let active_file = $derived($active_file_store); - export const update = () => { + export function update() { const max_depth = options.depth[1] === Infinity ? DEFAULT_MAX_DEPTH diff --git a/src/components/codeblocks/CodeblockTree.svelte b/src/components/codeblocks/CodeblockTree.svelte index 1a66863a..aa9d187f 100644 --- a/src/components/codeblocks/CodeblockTree.svelte +++ b/src/components/codeblocks/CodeblockTree.svelte @@ -33,14 +33,14 @@ ); const { show_node_options } = plugin.settings.views.codeblocks; - const DEFAULT_MAX_DEPTH = 10; + const DEFAULT_MAX_DEPTH = 5; let data: FlatTraversalResult | undefined = $state(undefined); let error: string | undefined = $state(undefined); let active_file = $derived($active_file_store); - export const update = () => { + export function update() { const max_depth = options.depth[1] === Infinity ? DEFAULT_MAX_DEPTH @@ -85,8 +85,6 @@ "An error occurred while updating the codeblock tree. Check the console for more information (Ctrl + Shift + I)."; } } - - data = data; }; onMount(() => { diff --git a/src/components/obsidian/RenderExternalCodeblock.svelte b/src/components/obsidian/RenderExternalCodeblock.svelte index a11b4129..2d67174c 100644 --- a/src/components/obsidian/RenderExternalCodeblock.svelte +++ b/src/components/obsidian/RenderExternalCodeblock.svelte @@ -14,13 +14,13 @@ let { code, type, - plugin = $bindable(), + plugin, source_path = undefined, }: Props = $props(); diff --git a/src/components/page_views/PrevNextView.svelte b/src/components/page_views/PrevNextView.svelte index 11d7a760..6f7514a6 100644 --- a/src/components/page_views/PrevNextView.svelte +++ b/src/components/page_views/PrevNextView.svelte @@ -39,14 +39,8 @@ const grouped_out_edges = plugin.graph.has_node(file_path) ? group_by( plugin.graph - .get_outgoing_edges(file_path) - .get_edges() - .filter((e) => - e.matches_edge_filter( - plugin.graph, - merged_field_labels, - ), - ), + .get_filtered_outgoing_edges(file_path, merged_field_labels) + .get_edges(), (e) => edge_field_labels.prev.includes(e.edge_type) ? ("prev" as const) diff --git a/wasm/src/data/edge_list.rs b/wasm/src/data/edge_list.rs index deb96fd2..1cde87fd 100644 --- a/wasm/src/data/edge_list.rs +++ b/wasm/src/data/edge_list.rs @@ -13,10 +13,18 @@ pub struct EdgeList { #[wasm_bindgen] impl EdgeList { + /// Returns a clone of the edges. pub fn get_edges(&self) -> Vec { self.edges.clone() } + /// Consumes the [EdgeList] and returns the edges as a Vec (or array for + /// JS). + pub fn to_array(self) -> Vec { + self.edges + } + + /// Returns a sorted clone of the edges. pub fn get_sorted_edges( &self, graph: &NoteGraph, @@ -38,6 +46,14 @@ impl EdgeList { grouped_edges } + pub fn first(&self) -> Option { + self.edges.first().cloned() + } + + pub fn last(&self) -> Option { + self.edges.last().cloned() + } + #[wasm_bindgen(js_name = toString)] pub fn to_fancy_string(&self) -> String { format!("{:#?}", self) diff --git a/wasm/src/data/mod.rs b/wasm/src/data/mod.rs index df57a410..6c2f47d3 100644 --- a/wasm/src/data/mod.rs +++ b/wasm/src/data/mod.rs @@ -47,6 +47,7 @@ impl NodeStringifyOptions { } } +#[wasm_bindgen] impl NodeStringifyOptions { pub fn stringify_node(&self, node: &NodeData) -> String { if self.alias && !node.aliases.is_empty() { diff --git a/wasm/src/graph.rs b/wasm/src/graph.rs index 8f6f8101..f2c4f2df 100644 --- a/wasm/src/graph.rs +++ b/wasm/src/graph.rs @@ -204,15 +204,15 @@ impl NoteGraph { }) } - /// Get all outgoing edges from a node, filtered and grouped by edge type. - pub fn get_filtered_grouped_outgoing_edges( + /// Get all outgoing edges from a node, filtered by edge type. + pub fn get_filtered_outgoing_edges( &self, node: String, edge_types: Option>, - ) -> GroupedEdgeList { + ) -> EdgeList { let node_index = self.int_get_node_index(&node); - GroupedEdgeList::from_vec(match node_index { + EdgeList::from_vec(match node_index { Some(node_index) => self .int_iter_outgoing_edges(node_index) .filter(|edge_ref| { @@ -224,6 +224,15 @@ impl NoteGraph { }) } + /// Get all outgoing edges from a node, filtered and grouped by edge type. + pub fn get_filtered_grouped_outgoing_edges( + &self, + node: String, + edge_types: Option>, + ) -> GroupedEdgeList { + GroupedEdgeList::from_edge_list(self.get_filtered_outgoing_edges(node, edge_types)) + } + /// Get all incoming edges to a node. pub fn get_incoming_edges(&self, node: String) -> EdgeList { let node_index = self.int_get_node_index(&node); From ba37ec8f2c05a38d0b4c79d82294587128cc9891 Mon Sep 17 00:00:00 2001 From: Moritz Jung Date: Sat, 11 Jan 2025 12:09:53 +0100 Subject: [PATCH 59/65] improve tree rendering performance --- src/commands/list_index/index.ts | 6 +- src/components/NestedEdgeList.svelte | 117 ++++++++++-------- .../button/CopyToClipboardButton.svelte | 4 +- .../codeblocks/CodeblockTree.svelte | 4 +- src/components/side_views/TreeView.svelte | 2 +- src/utils/wasm_types.d.ts | 9 ++ wasm/Cargo.toml | 2 +- wasm/src/data/traversal.rs | 85 +++++++++++-- 8 files changed, 156 insertions(+), 73 deletions(-) create mode 100644 src/utils/wasm_types.d.ts diff --git a/src/commands/list_index/index.ts b/src/commands/list_index/index.ts index e7de79b4..9792cd13 100644 --- a/src/commands/list_index/index.ts +++ b/src/commands/list_index/index.ts @@ -48,12 +48,16 @@ export namespace ListIndex { // TODO(Rust): This should probably be moved to the Rust side export const edge_tree_to_list_index = ( plugin: BreadcrumbsPlugin, - tree: FlatTraversalResult, + tree: FlatTraversalResult | undefined, options: Pick< Options, "link_kind" | "indent" | "show_node_options" | "show_attributes" | "show_entry_nodes" >, ) => { + if (!tree) { + return ""; + } + const all_traversal_data = tree.data; if (options.show_entry_nodes) { diff --git a/src/components/NestedEdgeList.svelte b/src/components/NestedEdgeList.svelte index 2051391b..0b5c80ae 100644 --- a/src/components/NestedEdgeList.svelte +++ b/src/components/NestedEdgeList.svelte @@ -1,22 +1,20 @@ {#each items as item, i} - {@const datum = data[item]} - {@const children = datum.children} -
- - {#if children.length || datum.has_cut_of_children} -
- + {@const children = data.children_at_index(item)} + {@const render_data = data.rendering_obj_at_index(item, plugin.graph, node_stringify_options, show_attributes ?? []) as EdgeRenderingData} + {#if children && render_data} +
+ + {#if children.length || render_data.has_cut_of_children} +
+ +
+ {/if} + +
+ + +
- {/if} -
- -
+ {#if show_attributes?.length} + + {/if} +
- {#if show_attributes?.length} - + {#if children.length} +
+ +
{/if} -
- {#if children.length} -
- -
- {/if} - - {#if datum.has_cut_of_children} -
-
- -
- Depth limit reached... -
-
-
-
- {/if} -
+ {#if render_data.has_cut_of_children} +
+
+ +
+ Depth limit reached... +
+
+
+
+ {/if} +
+ {/if} {/each} diff --git a/src/components/button/CopyToClipboardButton.svelte b/src/components/button/CopyToClipboardButton.svelte index fb67fec2..ba866f17 100644 --- a/src/components/button/CopyToClipboardButton.svelte +++ b/src/components/button/CopyToClipboardButton.svelte @@ -5,7 +5,7 @@ interface Props { cls?: string; - text: string; + text: string | (() => string); aria_label?: string; options?: { notify?: boolean; log?: boolean }; } @@ -26,7 +26,7 @@ onclick={() => { copied = true; - copy_to_clipboard(text, options); + copy_to_clipboard(typeof text === 'string' ? text : text(), options); setTimeout(() => (copied = false), 2_500); }} diff --git a/src/components/codeblocks/CodeblockTree.svelte b/src/components/codeblocks/CodeblockTree.svelte index aa9d187f..2851e53a 100644 --- a/src/components/codeblocks/CodeblockTree.svelte +++ b/src/components/codeblocks/CodeblockTree.svelte @@ -110,7 +110,7 @@
ListIndex.edge_tree_to_list_index(plugin, data, { ...plugin.settings.commands.list_index.default_options, show_attributes: options["show-attributes"] ?? [], })} @@ -122,7 +122,7 @@ diff --git a/src/utils/wasm_types.d.ts b/src/utils/wasm_types.d.ts new file mode 100644 index 00000000..d722b2a0 --- /dev/null +++ b/src/utils/wasm_types.d.ts @@ -0,0 +1,9 @@ +interface EdgeRenderingData { + link_display: string; + link_path: string; + target_resolved: boolean; + explicit: boolean; + edge_source: string; + attribute_label: string; + has_cut_of_children: boolean; +} \ No newline at end of file diff --git a/wasm/Cargo.toml b/wasm/Cargo.toml index d78b65ad..09957141 100644 --- a/wasm/Cargo.toml +++ b/wasm/Cargo.toml @@ -35,6 +35,6 @@ wasm-bindgen-test = "0.3.34" [profile.release] opt-level = 3 # Highest level of optimization. -lto = "fat" # Enable link-time optimization. +lto = "thin" # Enable link-time optimization. overflow-checks = false # Disable integer overflow checks. incremental = true # Enable incremental compilation for faster builds. diff --git a/wasm/src/data/traversal.rs b/wasm/src/data/traversal.rs index 83f588cd..030abe47 100644 --- a/wasm/src/data/traversal.rs +++ b/wasm/src/data/traversal.rs @@ -1,12 +1,13 @@ use itertools::Itertools; -use wasm_bindgen::prelude::wasm_bindgen; +use wasm_bindgen::{prelude::wasm_bindgen, JsValue}; +use super::NodeStringifyOptions; use crate::{ data::edge_struct::EdgeStruct, edge_sorting::EdgeSorter, graph::NoteGraph, traversal::path::{Path, PathList}, - utils, + utils::Result, }; #[wasm_bindgen] @@ -46,11 +47,7 @@ impl TraversalData { } } - pub fn rec_sort_children( - &mut self, - graph: &NoteGraph, - sorter: &EdgeSorter, - ) -> utils::Result<()> { + pub fn rec_sort_children(&mut self, graph: &NoteGraph, sorter: &EdgeSorter) -> Result<()> { for child in &mut self.children { child.rec_sort_children(graph, sorter)?; } @@ -145,7 +142,7 @@ impl TraversalResult { /// Squashes the traversal data by removing the tree structure and /// deduplicating the edges by their target_path Essentially, this will /// result in some kind of reachability set. - pub fn squash(&mut self, graph: &NoteGraph) -> utils::Result<()> { + pub fn squash(&mut self, graph: &NoteGraph) -> Result<()> { let mut data = Vec::new(); for datum in self.data.drain(..) { @@ -165,7 +162,7 @@ impl TraversalResult { Ok(()) } - pub fn sort(&mut self, graph: &NoteGraph, sorter: &EdgeSorter) -> utils::Result<()> { + pub fn sort(&mut self, graph: &NoteGraph, sorter: &EdgeSorter) -> Result<()> { for datum in &mut self.data { datum.rec_sort_children(graph, sorter)?; } @@ -247,9 +244,58 @@ impl FlatTraversalData { &self, graph: &NoteGraph, attributes: Vec, - ) -> utils::Result { + ) -> Result { self.edge.get_attribute_label(graph, attributes) } + + pub fn to_js_rendering_obj( + &self, + graph: &NoteGraph, + str_opt: &NodeStringifyOptions, + attributes: Vec, + ) -> Result { + let target_data = self.edge.target_data_ref(graph)?; + let edge_data = self.edge.edge_data_ref(graph)?; + + let obj = js_sys::Object::new(); + let _ = js_sys::Reflect::set( + &obj, + &JsValue::from_str("link_display"), + &JsValue::from_str(&str_opt.stringify_node(target_data)), + ); + let _ = js_sys::Reflect::set( + &obj, + &JsValue::from_str("link_path"), + &JsValue::from_str(&target_data.path), + ); + let _ = js_sys::Reflect::set( + &obj, + &JsValue::from_str("target_resolved"), + &JsValue::from_bool(target_data.resolved), + ); + let _ = js_sys::Reflect::set( + &obj, + &JsValue::from_str("explicit"), + &JsValue::from_bool(edge_data.explicit), + ); + let _ = js_sys::Reflect::set( + &obj, + &JsValue::from_str("edge_source"), + &JsValue::from_str(edge_data.edge_source.as_ref()), + ); + let _ = js_sys::Reflect::set( + &obj, + &JsValue::from_str("attribute_label"), + &JsValue::from_str(&self.get_attribute_label(graph, attributes)?), + ); + let _ = js_sys::Reflect::set( + &obj, + &JsValue::from_str("has_cut_of_children"), + &JsValue::from_bool(self.has_cut_of_children), + ); + + Ok(obj.into()) + } } #[wasm_bindgen] @@ -318,10 +364,27 @@ impl FlatTraversalResult { self.data.get(index).cloned() } + pub fn children_at_index(&self, index: usize) -> Option> { + self.data.get(index).map(|datum| datum.children.clone()) + } + + pub fn rendering_obj_at_index( + &self, + index: usize, + graph: &NoteGraph, + str_opt: &NodeStringifyOptions, + attributes: Vec, + ) -> Result { + self.data + .get(index) + .map(|datum| datum.to_js_rendering_obj(graph, str_opt, attributes)) + .unwrap_or(Ok(JsValue::UNDEFINED)) + } + /// Sorts the flat traversal data with a given edge sorter. /// This is not as efficient as sorting the traversal data before flattening /// it, but it's still a lot better than sorting then re-flatten. - pub fn sort(&mut self, graph: &NoteGraph, sorter: &EdgeSorter) -> utils::Result<()> { + pub fn sort(&mut self, graph: &NoteGraph, sorter: &EdgeSorter) -> Result<()> { let cloned_edges = self .data .iter() From 2c741da7f0029ce3b41bd8d3161dfde3d139bb38 Mon Sep 17 00:00:00 2001 From: Moritz Jung Date: Sun, 12 Jan 2025 16:18:29 +0100 Subject: [PATCH 60/65] more tests and bug fixes --- package.json | 2 +- wasm/src/data/rules.rs | 1 - wasm/src/edge_sorting.rs | 4 +- wasm/src/graph.rs | 34 ++++- wasm/src/update/graph.rs | 40 +----- wasm/tests/graph.rs | 114 +---------------- wasm/tests/update.rs | 268 +++++++++++++++++++++++++++++++++++++++ 7 files changed, 308 insertions(+), 155 deletions(-) create mode 100644 wasm/tests/update.rs diff --git a/package.json b/package.json index e747fdd3..5e43cac7 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,7 @@ "wasm:profile": "cd wasm && wasm-pack build --profiling --target web", "wasm:fmt": "cd wasm && cargo fmt", "wasm:lint": "cd wasm && cargo clippy", - "wasm:test": "cd wasm && wasm-pack test --node --features test --release" + "wasm:test": "cd wasm && wasm-pack test --node --features test" }, "keywords": [], "author": "SkepticMystic", diff --git a/wasm/src/data/rules.rs b/wasm/src/data/rules.rs index f2afb3cd..c5b399d1 100644 --- a/wasm/src/data/rules.rs +++ b/wasm/src/data/rules.rs @@ -16,7 +16,6 @@ pub struct TransitiveGraphRule { path: Vec>, // the edge type to add edge_type: Rc, - // the edge type to add rounds: u8, can_loop: bool, close_reversed: bool, diff --git a/wasm/src/edge_sorting.rs b/wasm/src/edge_sorting.rs index eb72e13a..ac2001fa 100644 --- a/wasm/src/edge_sorting.rs +++ b/wasm/src/edge_sorting.rs @@ -182,8 +182,8 @@ impl EdgeComparer for BasenameComparer { fn compare(&self, graph: &NoteGraph, a: &EdgeStruct, b: &EdgeStruct) -> std::cmp::Ordering { let a_target = a.target_path_ref(graph).unwrap(); let b_target = b.target_path_ref(graph).unwrap(); - let a_basename = a_target.split('/').last().unwrap(); - let b_basename = b_target.split('/').last().unwrap(); + let a_basename = a_target.split('/').next_back().unwrap(); + let b_basename = b_target.split('/').next_back().unwrap(); a_basename.cmp(b_basename) } diff --git a/wasm/src/graph.rs b/wasm/src/graph.rs index f2c4f2df..47806339 100644 --- a/wasm/src/graph.rs +++ b/wasm/src/graph.rs @@ -148,6 +148,7 @@ impl NoteGraph { perf_logger.start_split("Rebuilding edge type tracker".to_owned()); + self.int_remove_orphan_unresolved_nodes(); self.int_rebuild_edge_type_tracker(); self.int_build_implied_edges(&mut perf_logger); self.revision += 1; @@ -451,6 +452,31 @@ impl NoteGraph { // }); } + /// Removes all unresolved notes with no incoming or outgoing edges. + /// + /// INVARIANT: This updates the node hash. + /// INVARIANT: This keeps the edge type tracker up to date, as only nodes + /// with no connecting edges are removed. + pub fn int_remove_orphan_unresolved_nodes(&mut self) { + let mut nodes_to_remove: Vec<(NGNodeIndex, String)> = Vec::new(); + + for node in self.graph.node_indices() { + let node_weight = self.graph.node_weight(node).unwrap(); + + if !node_weight.resolved + && !self.int_has_incoming_edges(node) + && !self.int_has_outgoing_edges(node) + { + nodes_to_remove.push((node, node_weight.path.clone())); + } + } + + for (node_index, name) in nodes_to_remove { + self.node_hash.remove(&name); + self.graph.remove_node(node_index); + } + } + // --------------------- // Node Methods // --------------------- @@ -483,15 +509,15 @@ impl NoteGraph { pub fn int_has_incoming_edges(&self, node: NGNodeIndex) -> bool { self.graph .edges_directed(node, petgraph::Direction::Incoming) - .count() - > 0 + .next() + .is_some() } pub fn int_has_outgoing_edges(&self, node: NGNodeIndex) -> bool { self.graph .edges_directed(node, petgraph::Direction::Outgoing) - .count() - > 0 + .next() + .is_some() } pub fn int_iter_incoming_edges(&self, node: NGNodeIndex) -> Edges<'_, EdgeData, Directed, u32> { diff --git a/wasm/src/update/graph.rs b/wasm/src/update/graph.rs index 3ebb56e2..991e3ac6 100644 --- a/wasm/src/update/graph.rs +++ b/wasm/src/update/graph.rs @@ -68,7 +68,7 @@ impl UpdateableGraph for NoteGraph { .collect(); for edge in edges_to_remove { - self.remove_edge_by_index(edge)?; + self.graph.remove_edge(edge); } } None => { @@ -119,7 +119,10 @@ impl UpdateableGraph for NoteGraph { ))?; match self.int_get_edge(from, to, edge_type) { - Some(edge) => self.remove_edge_by_index(edge.id()), + Some(edge) => { + self.graph.remove_edge(edge.id()); + Ok(()) + } None => Err(NoteGraphError::new("failed to delete edge, edge not found")), } } @@ -127,39 +130,6 @@ impl UpdateableGraph for NoteGraph { /// Helper methods for the impl above. impl NoteGraph { - /// Given an edge index, removes the edge from the graph. - /// If the target node is unresolved and has no incoming edges, it will be - /// removed as well. - /// - /// INVARIANT: This function does not update the edge type tracker. - fn remove_edge_by_index(&mut self, edge_index: NGEdgeIndex) -> utils::Result<()> { - let (_, target) = self - .graph - .edge_endpoints(edge_index) - .ok_or(NoteGraphError::new("Edge not found"))?; - let target_data = self.int_get_node_weight(target)?; - let target_name = target_data.path.clone(); - let target_unresolved = !target_data.resolved; - - self.graph.remove_edge(edge_index); - - if target_unresolved && !self.int_has_incoming_edges(target) { - // INVARIANT: target node is unresolved and has no incoming edges - self.remove_node_by_index_and_name(target, &target_name); - } - - Ok(()) - } - - /// Given a node index and the name of a node, removes it from the graph and - /// the node hash. - /// - /// INVARIANT: This function does not update the edge type tracker. - fn remove_node_by_index_and_name(&mut self, node_index: NGNodeIndex, name: &str) { - self.node_hash.remove(name); - self.graph.remove_node(node_index); - } - /// Gets the node index for a specific node. /// If the node does not exist, a new unresolved node will be created and /// the index of the new node returned. diff --git a/wasm/tests/graph.rs b/wasm/tests/graph.rs index ad7947ca..fe456507 100644 --- a/wasm/tests/graph.rs +++ b/wasm/tests/graph.rs @@ -1,23 +1,12 @@ // #![cfg(target_arch = "wasm32")] extern crate wasm_bindgen_test; -use breadcrumbs_graph_wasm::{ - data::{ - construction::{GCEdgeData, GCNodeData}, - rules::TransitiveGraphRule, - }, - graph::NoteGraph, - update::{ - batch::BatchGraphUpdate, AddEdgeGraphUpdate, AddNoteGraphUpdate, RemoveNoteGraphUpdate, - }, - utils::graph_eq, -}; +use breadcrumbs_graph_wasm::{data::rules::TransitiveGraphRule, graph::NoteGraph}; use wasm_bindgen_test::*; +// wasm_bindgen_test_configure!(run_in_browser); mod common; -// wasm_bindgen_test_configure!(run_in_browser); - #[wasm_bindgen_test] fn test_graph_builds() { let data = common::tdata_generate_tree(3, 2); @@ -188,102 +177,3 @@ fn test_implied_edge_rules_sibling_can_loop() { assert_eq!(same_edge_4.weight().explicit, false); graph.assert_correct_trackers(); } - -#[wasm_bindgen_test] -fn test_empty_update_does_nothing() { - let data = common::tdata_generate_tree(2, 2); - let mut graph_1 = common::tdata_to_graph(data.clone()); - let graph_2 = common::tdata_to_graph(data); - - let batch = BatchGraphUpdate::new(); - graph_1.apply_update(batch).unwrap(); - - assert!(graph_eq(&graph_1.graph, &graph_2.graph)); - graph_1.assert_correct_trackers(); -} - -#[wasm_bindgen_test] -fn test_remove_update() { - let data = common::tdata_generate_tree(2, 2); - let mut graph = common::tdata_to_graph(data); - - let mut batch = BatchGraphUpdate::new(); - RemoveNoteGraphUpdate::new("0".to_string()).add_to_batch(&mut batch); - graph.apply_update(batch).unwrap(); - - let node_0 = graph.int_get_node_index(&"0".to_string()).unwrap(); - let node_0_weight = graph.int_get_node_weight(node_0).unwrap(); - - assert_eq!(node_0_weight.resolved, false); - assert_eq!(graph.int_has_outgoing_edges(node_0), false); - assert!(graph.int_has_edge_by_name(&"root".to_string(), &"0".to_string(), &"down".to_string())); - - graph.assert_correct_trackers(); -} - -#[wasm_bindgen_test] -fn test_remove_add_update() { - let data = common::tdata_generate_tree(2, 2); - let mut graph_1 = common::tdata_to_graph(data.clone()); - let graph_2 = common::tdata_to_graph(data); - - let mut batch = BatchGraphUpdate::new(); - RemoveNoteGraphUpdate::new("0".to_string()).add_to_batch(&mut batch); - AddNoteGraphUpdate::new(GCNodeData::new("0".to_string(), vec![], true, false, false)) - .add_to_batch(&mut batch); - AddEdgeGraphUpdate::new(GCEdgeData::new( - "0".to_string(), - "00".to_string(), - "down".to_string(), - "typed-link".to_string(), - )) - .add_to_batch(&mut batch); - AddEdgeGraphUpdate::new(GCEdgeData::new( - "0".to_string(), - "01".to_string(), - "down".to_string(), - "typed-link".to_string(), - )) - .add_to_batch(&mut batch); - - graph_1.apply_update(batch).unwrap(); - - assert!(graph_eq(&graph_1.graph, &graph_2.graph)); - - graph_1.assert_correct_trackers(); -} - -#[wasm_bindgen_test] -fn test_remove_add_separate_updates() { - let data = common::tdata_generate_tree(2, 2); - let mut graph_1 = common::tdata_to_graph(data.clone()); - let graph_2 = common::tdata_to_graph(data); - - let mut batch_1 = BatchGraphUpdate::new(); - RemoveNoteGraphUpdate::new("0".to_string()).add_to_batch(&mut batch_1); - - graph_1.apply_update(batch_1).unwrap(); - - let mut batch_2 = BatchGraphUpdate::new(); - AddNoteGraphUpdate::new(GCNodeData::new("0".to_string(), vec![], true, false, false)) - .add_to_batch(&mut batch_2); - AddEdgeGraphUpdate::new(GCEdgeData::new( - "0".to_string(), - "00".to_string(), - "down".to_string(), - "typed-link".to_string(), - )) - .add_to_batch(&mut batch_2); - AddEdgeGraphUpdate::new(GCEdgeData::new( - "0".to_string(), - "01".to_string(), - "down".to_string(), - "typed-link".to_string(), - )) - .add_to_batch(&mut batch_2); - graph_1.apply_update(batch_2).unwrap(); - - assert!(graph_eq(&graph_1.graph, &graph_2.graph)); - - graph_1.assert_correct_trackers(); -} diff --git a/wasm/tests/update.rs b/wasm/tests/update.rs new file mode 100644 index 00000000..df933bec --- /dev/null +++ b/wasm/tests/update.rs @@ -0,0 +1,268 @@ +extern crate wasm_bindgen_test; +use breadcrumbs_graph_wasm::{ + data::{ + construction::{GCEdgeData, GCNodeData}, + rules::TransitiveGraphRule, + }, + graph::NoteGraph, + update::{ + batch::BatchGraphUpdate, AddEdgeGraphUpdate, AddNoteGraphUpdate, RemoveNoteGraphUpdate, + }, + utils::graph_eq, +}; +use wasm_bindgen_test::*; + +mod common; + +#[wasm_bindgen_test] +fn test_empty_update_does_nothing() { + let data = common::tdata_generate_tree(2, 2); + let mut graph_1 = common::tdata_to_graph(data.clone()); + let graph_2 = common::tdata_to_graph(data); + + let batch = BatchGraphUpdate::new(); + graph_1.apply_update(batch).unwrap(); + + assert!(graph_eq(&graph_1.graph, &graph_2.graph)); + graph_1.assert_correct_trackers(); +} + +#[wasm_bindgen_test] +fn test_remove_update() { + let data = common::tdata_generate_tree(2, 2); + let mut graph = common::tdata_to_graph(data); + + let mut batch = BatchGraphUpdate::new(); + RemoveNoteGraphUpdate::new("0".to_string()).add_to_batch(&mut batch); + graph.apply_update(batch).unwrap(); + + let node_0 = graph.int_get_node_index(&"0".to_string()).unwrap(); + let node_0_weight = graph.int_get_node_weight(node_0).unwrap(); + + assert_eq!(node_0_weight.resolved, false); + assert_eq!(graph.int_has_outgoing_edges(node_0), false); + assert!(graph.int_has_edge_by_name(&"root".to_string(), &"0".to_string(), &"down".to_string())); + + graph.assert_correct_trackers(); +} + +#[wasm_bindgen_test] +fn test_remove_add_as_one_update() { + let data = common::tdata_generate_tree(2, 2); + let mut graph_1 = common::tdata_to_graph(data.clone()); + let graph_2 = common::tdata_to_graph(data); + + let mut batch = BatchGraphUpdate::new(); + RemoveNoteGraphUpdate::new("0".to_string()).add_to_batch(&mut batch); + AddNoteGraphUpdate::new(GCNodeData::new("0".to_string(), vec![], true, false, false)) + .add_to_batch(&mut batch); + AddEdgeGraphUpdate::new(GCEdgeData::new( + "0".to_string(), + "00".to_string(), + "down".to_string(), + "typed-link".to_string(), + )) + .add_to_batch(&mut batch); + AddEdgeGraphUpdate::new(GCEdgeData::new( + "0".to_string(), + "01".to_string(), + "down".to_string(), + "typed-link".to_string(), + )) + .add_to_batch(&mut batch); + + graph_1.apply_update(batch).unwrap(); + + assert!(graph_eq(&graph_1.graph, &graph_2.graph)); + + graph_1.assert_correct_trackers(); +} + +#[wasm_bindgen_test] +fn test_remove_add_as_separate_updates() { + let data = common::tdata_generate_tree(2, 2); + let mut graph_1 = common::tdata_to_graph(data.clone()); + let graph_2 = common::tdata_to_graph(data); + + let mut batch_1 = BatchGraphUpdate::new(); + RemoveNoteGraphUpdate::new("0".to_string()).add_to_batch(&mut batch_1); + + graph_1.apply_update(batch_1).unwrap(); + + let mut batch_2 = BatchGraphUpdate::new(); + AddNoteGraphUpdate::new(GCNodeData::new("0".to_string(), vec![], true, false, false)) + .add_to_batch(&mut batch_2); + AddEdgeGraphUpdate::new(GCEdgeData::new( + "0".to_string(), + "00".to_string(), + "down".to_string(), + "typed-link".to_string(), + )) + .add_to_batch(&mut batch_2); + AddEdgeGraphUpdate::new(GCEdgeData::new( + "0".to_string(), + "01".to_string(), + "down".to_string(), + "typed-link".to_string(), + )) + .add_to_batch(&mut batch_2); + graph_1.apply_update(batch_2).unwrap(); + + assert!(graph_eq(&graph_1.graph, &graph_2.graph)); + + graph_1.assert_correct_trackers(); +} + +#[wasm_bindgen_test] +fn test_add_edge_to_unresolved() { + let data = common::tdata_generate_tree(2, 2); + let mut graph = common::tdata_to_graph(data.clone()); + + let mut batch = BatchGraphUpdate::new(); + AddEdgeGraphUpdate::new(GCEdgeData::new( + "00".to_string(), + "000".to_string(), + "down".to_string(), + "typed-link".to_string(), + )) + .add_to_batch(&mut batch); + + graph.apply_update(batch).unwrap(); + + let node_000 = graph.int_get_node_index(&"000".to_string()).unwrap(); + + assert_eq!(graph.int_has_incoming_edges(node_000), true); + assert_eq!(graph.int_has_outgoing_edges(node_000), false); + assert_eq!(graph.get_node("000".to_string()).unwrap().resolved, false); +} + +#[wasm_bindgen_test] +fn test_unresolved_node_can_have_outgoing_edges() { + let nodes = vec![ + GCNodeData::new("a".to_string(), vec![], true, false, false), + GCNodeData::new("b".to_string(), vec![], true, false, false), + ]; + let edges = vec![ + GCEdgeData::new( + "a".to_string(), + "c".to_string(), + "1".to_string(), + "typed-link".to_string(), + ), + GCEdgeData::new( + "a".to_string(), + "b".to_string(), + "2".to_string(), + "typed-link".to_string(), + ), + GCEdgeData::new( + "b".to_string(), + "d".to_string(), + "3".to_string(), + "typed-link".to_string(), + ), + ]; + + // This is how the graph looks like. The uppercase letters are the resolved + // nodes. + // + // A ---2--> B + // | | + // | | + // 1 3 + // | | + // v v + // c d + + let rules = vec![ + TransitiveGraphRule::new( + "r1".to_string(), + vec!["1".to_string()], + "inv1".to_string(), + 5, + false, + true, + ), + TransitiveGraphRule::new( + "r2".to_string(), + vec!["inv1".to_string(), "2".to_string(), "3".to_string()], + "4".to_string(), + 5, + false, + false, + ), + TransitiveGraphRule::new( + "r3".to_string(), + vec!["4".to_string()], + "inv4".to_string(), + 5, + false, + true, + ), + ]; + + // Rule1 inverts edge 1 between a and c + // Rule2 creates an new edge 4 between c and d + // Rule3 inverts edge 4 between c and d + // + // A ---2--> B + // ^ | + // | | + // 1 3 + // | | + // v v + // c <--4--> d + + let mut graph = NoteGraph::new(); + graph.build_graph(nodes, edges, rules).unwrap(); + + let node_c = graph.int_get_node_index(&"c".to_string()).unwrap(); + assert_eq!(graph.int_has_incoming_edges(node_c), true); + assert_eq!(graph.int_has_outgoing_edges(node_c), true); + + let node_d = graph.int_get_node_index(&"d".to_string()).unwrap(); + assert_eq!(graph.int_has_incoming_edges(node_d), true); + assert_eq!(graph.int_has_outgoing_edges(node_d), true); + + assert_eq!(graph.graph.node_count(), 4); + + let _ = graph + .int_get_edge_by_name(&"c".to_string(), &"d".to_string(), &"4".to_string()) + .unwrap(); + let _ = graph + .int_get_edge_by_name(&"d".to_string(), &"c".to_string(), &"inv4".to_string()) + .unwrap(); + + let mut batch = BatchGraphUpdate::new(); + RemoveNoteGraphUpdate::new("b".to_string()).add_to_batch(&mut batch); + graph.apply_update(batch).unwrap(); + + // The graph should now look like this: + // + // A ---2--> b + // ^ + // | + // 1 + // | + // v + // c + + assert_eq!(graph.get_node("b".to_string()).unwrap().resolved, false); + + let node_c = graph.int_get_node_index(&"c".to_string()).unwrap(); + assert_eq!(graph.int_has_incoming_edges(node_c), true); + assert_eq!(graph.int_has_outgoing_edges(node_c), true); + + assert_eq!(graph.int_get_node_index(&"d".to_string()), None); + + assert_eq!(graph.graph.node_count(), 3); + + let mut batch = BatchGraphUpdate::new(); + RemoveNoteGraphUpdate::new("a".to_string()).add_to_batch(&mut batch); + graph.apply_update(batch).unwrap(); + + // The graph should now be empty, otherwise we leaked unresolved nodes. + + assert_eq!(graph.graph.node_count(), 0); + assert_eq!(graph.graph.edge_count(), 0); +} From 0b5c55974739354658266f0cd139ede719b9dad0 Mon Sep 17 00:00:00 2001 From: Moritz Jung Date: Sun, 12 Jan 2025 19:48:21 +0100 Subject: [PATCH 61/65] make js tests run --- esbuild.config.mjs | 31 +- package-lock.json | 951 +++++++++++---------------- package.json | 11 +- src/api/index.ts | 3 +- src/commands/list_index/index.ts | 83 +-- src/components/NestedEdgeList.svelte | 2 +- src/graph/utils.ts | 6 +- src/main.ts | 2 +- tests/commands/list_index.test.ts | 227 ++++--- tests/commands/stats.test.ts | 10 +- tests/utils/mermaid.test.ts | 8 +- vite.config.mjs | 10 + wasmPlugin.mjs | 27 + 13 files changed, 608 insertions(+), 763 deletions(-) create mode 100644 vite.config.mjs create mode 100644 wasmPlugin.mjs diff --git a/esbuild.config.mjs b/esbuild.config.mjs index ba06e6a4..0f4994e6 100644 --- a/esbuild.config.mjs +++ b/esbuild.config.mjs @@ -3,8 +3,7 @@ import esbuild from "esbuild"; import esbuildSvelte from "esbuild-svelte"; import process from "process"; import { sveltePreprocess } from "svelte-preprocess"; -import path from 'node:path'; -import fs from 'node:fs'; +import { wasmPluginEsbuild } from "./wasmPlugin"; const banner = `/* THIS IS A GENERATED/BUNDLED FILE BY ESBUILD @@ -12,32 +11,6 @@ if you want to view the source, please visit the github repository of this plugi */ `; -const wasmPlugin = { - name: 'wasm', - setup(build) { - // Resolve ".wasm" files to a path with a namespace - build.onResolve({ filter: /\.wasm$/ }, args => { - if (args.resolveDir === '') { - return; // Ignore unresolvable paths - } - return { - path: path.isAbsolute(args.path) ? args.path : path.join(args.resolveDir, args.path), - namespace: 'wasm-binary', - }; - }); - - // Virtual modules in the "wasm-binary" namespace contain the - // actual bytes of the WebAssembly file. This uses esbuild's - // built-in "binary" loader instead of manually embedding the - // binary data inside JavaScript code ourselves. - build.onLoad({ filter: /.*/, namespace: 'wasm-binary' }, async args => ({ - contents: await fs.promises.readFile(args.path), - loader: 'binary', - })); - }, -}; - - const prod = process.argv[2] === "production"; const context = await esbuild.context({ @@ -76,7 +49,7 @@ const context = await esbuild.context({ compilerOptions: { css: 'injected', dev: !prod }, preprocess: sveltePreprocess(), }), - wasmPlugin, + wasmPluginEsbuild, ], }); diff --git a/package-lock.json b/package-lock.json index efa2d1f1..2246edac 100644 --- a/package-lock.json +++ b/package-lock.json @@ -22,8 +22,8 @@ "@types/obsidian-typings": "npm:obsidian-typings@^1.0.6", "@typescript-eslint/eslint-plugin": "6.18.1", "@typescript-eslint/parser": "6.18.1", - "@vitest/coverage-v8": "^1.3.1", - "@vitest/ui": "^1.3.1", + "@vitest/coverage-v8": "^2.1.8", + "@vitest/ui": "^2.1.8", "builtin-modules": "3.3.0", "esbuild": "0.24.2", "esbuild-svelte": "^0.9.0", @@ -38,7 +38,8 @@ "tailwindcss": "^3.4.1", "tslib": "2.6.2", "typescript": "^5.5.0", - "vitest": "^1.3.1" + "vite-plugin-wasm": "^3.4.1", + "vitest": "^2.1.8" } }, "node_modules/@aidenlx/folder-note-core": { @@ -94,12 +95,12 @@ } }, "node_modules/@babel/parser": { - "version": "7.26.3", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.3.tgz", - "integrity": "sha512-WJ/CvmY8Mea8iDXo6a7RK2wbmJITT5fN3BEkRuFlxVyNx8jOKIIhmC4fSkTcPcf8JyavbBwIe6OpiCOBXt/IcA==", + "version": "7.26.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.5.tgz", + "integrity": "sha512-SRJ4jYmXRqV1/Xc+TIVG84WjHBXKlxO9sHQnA2Pf12QQEAp1LOh6kDzNHXcUnbH1QI0FDoPPVOt+vyUDucxpaw==", "dev": true, "dependencies": { - "@babel/types": "^7.26.3" + "@babel/types": "^7.26.5" }, "bin": { "parser": "bin/babel-parser.js" @@ -109,9 +110,9 @@ } }, "node_modules/@babel/types": { - "version": "7.26.3", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.3.tgz", - "integrity": "sha512-vN5p+1kl59GVKMvTHt55NzzmYVxprfJD+ql7U9NFIfKCBkYE55LYtS+WtPlaYOyzydrKI8Nezd+aZextrd+FMA==", + "version": "7.26.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.5.tgz", + "integrity": "sha512-L6mZmwFDK6Cjh1nRCLXpa6no13ZIioJDz7mdkzHv399pThrTa/k0nUlNaenOeh2kWu/iaOQYElEpKPUswUa9Vg==", "dev": true, "dependencies": { "@babel/helper-string-parser": "^7.25.9", @@ -128,8 +129,8 @@ "dev": true }, "node_modules/@codemirror/language": { - "version": "6.10.3", - "resolved": "git+ssh://git@github.com/lishid/cm-language.git#be84c19d93fa87ac4e43c1a5874b1d228b5b2a89", + "version": "6.10.8", + "resolved": "git+ssh://git@github.com/lishid/cm-language.git#6c1c5f5b677f6f6503d1ca2ec47f62f6406cda67", "dev": true, "license": "MIT", "dependencies": { @@ -142,18 +143,18 @@ } }, "node_modules/@codemirror/state": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/@codemirror/state/-/state-6.5.0.tgz", - "integrity": "sha512-MwBHVK60IiIHDcoMet78lxt6iw5gJOGSbNbOIVBHWVXIH4/Nq1+GQgLLGgI1KlnN86WDXsPudVaqYHKBIx7Eyw==", + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/@codemirror/state/-/state-6.5.1.tgz", + "integrity": "sha512-3rA9lcwciEB47ZevqvD8qgbzhM9qMb8vCcQCNmDfVRPQG4JT9mSb0Jg8H7YjKGGQcFnLN323fj9jdnG59Kx6bg==", "dev": true, "dependencies": { "@marijn/find-cluster-break": "^1.0.0" } }, "node_modules/@codemirror/view": { - "version": "6.36.1", - "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.36.1.tgz", - "integrity": "sha512-miD1nyT4m4uopZaDdO2uXU/LLHliKNYL9kB1C1wJHrunHLm/rpkb5QVSokqgw9hFqEZakrdlb/VGWX8aYZTslQ==", + "version": "6.36.2", + "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.36.2.tgz", + "integrity": "sha512-DZ6ONbs8qdJK0fdN7AB82CgI6tYXf4HWk1wSVa0+9bhVznCuuvhQtX8bFBoy3dv8rZSQqUd8GvhVAcielcidrA==", "dev": true, "dependencies": { "@codemirror/state": "^6.5.0", @@ -761,18 +762,6 @@ "node": ">=8" } }, - "node_modules/@jest/schemas": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", - "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", - "dev": true, - "dependencies": { - "@sinclair/typebox": "^0.27.8" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, "node_modules/@jridgewell/gen-mapping": { "version": "0.3.8", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz", @@ -898,9 +887,9 @@ "dev": true }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.30.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.30.0.tgz", - "integrity": "sha512-qFcFto9figFLz2g25DxJ1WWL9+c91fTxnGuwhToCl8BaqDsDYMl/kOnBXAyAqkkzAWimYMSWNPWEjt+ADAHuoQ==", + "version": "4.30.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.30.1.tgz", + "integrity": "sha512-pSWY+EVt3rJ9fQ3IqlrEUtXh3cGqGtPDH1FQlNZehO2yYxCHEX1SPsz1M//NXwYfbTlcKr9WObLnJX9FsS9K1Q==", "cpu": [ "arm" ], @@ -911,9 +900,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.30.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.30.0.tgz", - "integrity": "sha512-vqrQdusvVl7dthqNjWCL043qelBK+gv9v3ZiqdxgaJvmZyIAAXMjeGVSqZynKq69T7062T5VrVTuikKSAAVP6A==", + "version": "4.30.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.30.1.tgz", + "integrity": "sha512-/NA2qXxE3D/BRjOJM8wQblmArQq1YoBVJjrjoTSBS09jgUisq7bqxNHJ8kjCHeV21W/9WDGwJEWSN0KQ2mtD/w==", "cpu": [ "arm64" ], @@ -924,9 +913,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.30.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.30.0.tgz", - "integrity": "sha512-617pd92LhdA9+wpixnzsyhVft3szYiN16aNUMzVkf2N+yAk8UXY226Bfp36LvxYTUt7MO/ycqGFjQgJ0wlMaWQ==", + "version": "4.30.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.30.1.tgz", + "integrity": "sha512-r7FQIXD7gB0WJ5mokTUgUWPl0eYIH0wnxqeSAhuIwvnnpjdVB8cRRClyKLQr7lgzjctkbp5KmswWszlwYln03Q==", "cpu": [ "arm64" ], @@ -937,9 +926,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.30.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.30.0.tgz", - "integrity": "sha512-Y3b4oDoaEhCypg8ajPqigKDcpi5ZZovemQl9Edpem0uNv6UUjXv7iySBpGIUTSs2ovWOzYpfw9EbFJXF/fJHWw==", + "version": "4.30.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.30.1.tgz", + "integrity": "sha512-x78BavIwSH6sqfP2xeI1hd1GpHL8J4W2BXcVM/5KYKoAD3nNsfitQhvWSw+TFtQTLZ9OmlF+FEInEHyubut2OA==", "cpu": [ "x64" ], @@ -950,9 +939,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.30.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.30.0.tgz", - "integrity": "sha512-3REQJ4f90sFIBfa0BUokiCdrV/E4uIjhkWe1bMgCkhFXbf4D8YN6C4zwJL881GM818qVYE9BO3dGwjKhpo2ABA==", + "version": "4.30.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.30.1.tgz", + "integrity": "sha512-HYTlUAjbO1z8ywxsDFWADfTRfTIIy/oUlfIDmlHYmjUP2QRDTzBuWXc9O4CXM+bo9qfiCclmHk1x4ogBjOUpUQ==", "cpu": [ "arm64" ], @@ -963,9 +952,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.30.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.30.0.tgz", - "integrity": "sha512-ZtY3Y8icbe3Cc+uQicsXG5L+CRGUfLZjW6j2gn5ikpltt3Whqjfo5mkyZ86UiuHF9Q3ZsaQeW7YswlHnN+lAcg==", + "version": "4.30.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.30.1.tgz", + "integrity": "sha512-1MEdGqogQLccphhX5myCJqeGNYTNcmTyaic9S7CG3JhwuIByJ7J05vGbZxsizQthP1xpVx7kd3o31eOogfEirw==", "cpu": [ "x64" ], @@ -976,9 +965,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.30.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.30.0.tgz", - "integrity": "sha512-bsPGGzfiHXMhQGuFGpmo2PyTwcrh2otL6ycSZAFTESviUoBOuxF7iBbAL5IJXc/69peXl5rAtbewBFeASZ9O0g==", + "version": "4.30.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.30.1.tgz", + "integrity": "sha512-PaMRNBSqCx7K3Wc9QZkFx5+CX27WFpAMxJNiYGAXfmMIKC7jstlr32UhTgK6T07OtqR+wYlWm9IxzennjnvdJg==", "cpu": [ "arm" ], @@ -989,9 +978,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.30.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.30.0.tgz", - "integrity": "sha512-kvyIECEhs2DrrdfQf++maCWJIQ974EI4txlz1nNSBaCdtf7i5Xf1AQCEJWOC5rEBisdaMFFnOWNLYt7KpFqy5A==", + "version": "4.30.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.30.1.tgz", + "integrity": "sha512-B8Rcyj9AV7ZlEFqvB5BubG5iO6ANDsRKlhIxySXcF1axXYUyqwBok+XZPgIYGBgs7LDXfWfifxhw0Ik57T0Yug==", "cpu": [ "arm" ], @@ -1002,9 +991,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.30.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.30.0.tgz", - "integrity": "sha512-CFE7zDNrokaotXu+shwIrmWrFxllg79vciH4E/zeK7NitVuWEaXRzS0mFfFvyhZfn8WfVOG/1E9u8/DFEgK7WQ==", + "version": "4.30.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.30.1.tgz", + "integrity": "sha512-hqVyueGxAj3cBKrAI4aFHLV+h0Lv5VgWZs9CUGqr1z0fZtlADVV1YPOij6AhcK5An33EXaxnDLmJdQikcn5NEw==", "cpu": [ "arm64" ], @@ -1015,9 +1004,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.30.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.30.0.tgz", - "integrity": "sha512-MctNTBlvMcIBP0t8lV/NXiUwFg9oK5F79CxLU+a3xgrdJjfBLVIEHSAjQ9+ipofN2GKaMLnFFXLltg1HEEPaGQ==", + "version": "4.30.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.30.1.tgz", + "integrity": "sha512-i4Ab2vnvS1AE1PyOIGp2kXni69gU2DAUVt6FSXeIqUCPIR3ZlheMW3oP2JkukDfu3PsexYRbOiJrY+yVNSk9oA==", "cpu": [ "arm64" ], @@ -1028,9 +1017,9 @@ ] }, "node_modules/@rollup/rollup-linux-loongarch64-gnu": { - "version": "4.30.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.30.0.tgz", - "integrity": "sha512-fBpoYwLEPivL3q368+gwn4qnYnr7GVwM6NnMo8rJ4wb0p/Y5lg88vQRRP077gf+tc25akuqd+1Sxbn9meODhwA==", + "version": "4.30.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.30.1.tgz", + "integrity": "sha512-fARcF5g296snX0oLGkVxPmysetwUk2zmHcca+e9ObOovBR++9ZPOhqFUM61UUZ2EYpXVPN1redgqVoBB34nTpQ==", "cpu": [ "loong64" ], @@ -1041,9 +1030,9 @@ ] }, "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.30.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.30.0.tgz", - "integrity": "sha512-1hiHPV6dUaqIMXrIjN+vgJqtfkLpqHS1Xsg0oUfUVD98xGp1wX89PIXgDF2DWra1nxAd8dfE0Dk59MyeKaBVAw==", + "version": "4.30.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.30.1.tgz", + "integrity": "sha512-GLrZraoO3wVT4uFXh67ElpwQY0DIygxdv0BNW9Hkm3X34wu+BkqrDrkcsIapAY+N2ATEbvak0XQ9gxZtCIA5Rw==", "cpu": [ "ppc64" ], @@ -1054,9 +1043,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.30.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.30.0.tgz", - "integrity": "sha512-U0xcC80SMpEbvvLw92emHrNjlS3OXjAM0aVzlWfar6PR0ODWCTQtKeeB+tlAPGfZQXicv1SpWwRz9Hyzq3Jx3g==", + "version": "4.30.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.30.1.tgz", + "integrity": "sha512-0WKLaAUUHKBtll0wvOmh6yh3S0wSU9+yas923JIChfxOaaBarmb/lBKPF0w/+jTVozFnOXJeRGZ8NvOxvk/jcw==", "cpu": [ "riscv64" ], @@ -1067,9 +1056,9 @@ ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.30.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.30.0.tgz", - "integrity": "sha512-VU/P/IODrNPasgZDLIFJmMiLGez+BN11DQWfTVlViJVabyF3JaeaJkP6teI8760f18BMGCQOW9gOmuzFaI1pUw==", + "version": "4.30.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.30.1.tgz", + "integrity": "sha512-GWFs97Ruxo5Bt+cvVTQkOJ6TIx0xJDD/bMAOXWJg8TCSTEK8RnFeOeiFTxKniTc4vMIaWvCplMAFBt9miGxgkA==", "cpu": [ "s390x" ], @@ -1080,9 +1069,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.30.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.30.0.tgz", - "integrity": "sha512-laQVRvdbKmjXuFA3ZiZj7+U24FcmoPlXEi2OyLfbpY2MW1oxLt9Au8q9eHd0x6Pw/Kw4oe9gwVXWwIf2PVqblg==", + "version": "4.30.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.30.1.tgz", + "integrity": "sha512-UtgGb7QGgXDIO+tqqJ5oZRGHsDLO8SlpE4MhqpY9Llpzi5rJMvrK6ZGhsRCST2abZdBqIBeXW6WPD5fGK5SDwg==", "cpu": [ "x64" ], @@ -1093,9 +1082,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.30.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.30.0.tgz", - "integrity": "sha512-3wzKzduS7jzxqcOvy/ocU/gMR3/QrHEFLge5CD7Si9fyHuoXcidyYZ6jyx8OPYmCcGm3uKTUl+9jUSAY74Ln5A==", + "version": "4.30.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.30.1.tgz", + "integrity": "sha512-V9U8Ey2UqmQsBT+xTOeMzPzwDzyXmnAoO4edZhL7INkwQcaW1Ckv3WJX3qrrp/VHaDkEWIBWhRwP47r8cdrOow==", "cpu": [ "x64" ], @@ -1106,9 +1095,9 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.30.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.30.0.tgz", - "integrity": "sha512-jROwnI1+wPyuv696rAFHp5+6RFhXGGwgmgSfzE8e4xfit6oLRg7GyMArVUoM3ChS045OwWr9aTnU+2c1UdBMyw==", + "version": "4.30.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.30.1.tgz", + "integrity": "sha512-WabtHWiPaFF47W3PkHnjbmWawnX/aE57K47ZDT1BXTS5GgrBUEpvOzq0FI0V/UYzQJgdb8XlhVNH8/fwV8xDjw==", "cpu": [ "arm64" ], @@ -1119,9 +1108,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.30.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.30.0.tgz", - "integrity": "sha512-duzweyup5WELhcXx5H1jokpr13i3BV9b48FMiikYAwk/MT1LrMYYk2TzenBd0jj4ivQIt58JWSxc19y4SvLP4g==", + "version": "4.30.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.30.1.tgz", + "integrity": "sha512-pxHAU+Zv39hLUTdQQHUVHf4P+0C47y/ZloorHpzs2SXMRqeAWmGghzAhfOlzFHHwjvgokdFAhC4V+6kC1lRRfw==", "cpu": [ "ia32" ], @@ -1132,9 +1121,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.30.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.30.0.tgz", - "integrity": "sha512-DYvxS0M07PvgvavMIybCOBYheyrqlui6ZQBHJs6GqduVzHSZ06TPPvlfvnYstjODHQ8UUXFwt5YE+h0jFI8kwg==", + "version": "4.30.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.30.1.tgz", + "integrity": "sha512-D6qjsXGcvhTjv0kI4fU8tUuBDF/Ueee4SVX79VfNDXZa64TfCW1Slkb6Z7O1p7vflqZjcmOVdZlqf8gvJxc6og==", "cpu": [ "x64" ], @@ -1144,12 +1133,6 @@ "win32" ] }, - "node_modules/@sinclair/typebox": { - "version": "0.27.8", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", - "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", - "dev": true - }, "node_modules/@tsconfig/svelte": { "version": "5.0.4", "resolved": "https://registry.npmjs.org/@tsconfig/svelte/-/svelte-5.0.4.tgz", @@ -1424,144 +1407,159 @@ "peer": true }, "node_modules/@vitest/coverage-v8": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-1.6.0.tgz", - "integrity": "sha512-KvapcbMY/8GYIG0rlwwOKCVNRc0OL20rrhFkg/CHNzncV03TE2XWvO5w9uZYoxNiMEBacAJt3unSOiZ7svePew==", + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-2.1.8.tgz", + "integrity": "sha512-2Y7BPlKH18mAZYAW1tYByudlCYrQyl5RGvnnDYJKW5tCiO5qg3KSAy3XAxcxKz900a0ZXxWtKrMuZLe3lKBpJw==", "dev": true, "dependencies": { - "@ampproject/remapping": "^2.2.1", + "@ampproject/remapping": "^2.3.0", "@bcoe/v8-coverage": "^0.2.3", - "debug": "^4.3.4", + "debug": "^4.3.7", "istanbul-lib-coverage": "^3.2.2", "istanbul-lib-report": "^3.0.1", - "istanbul-lib-source-maps": "^5.0.4", - "istanbul-reports": "^3.1.6", - "magic-string": "^0.30.5", - "magicast": "^0.3.3", - "picocolors": "^1.0.0", - "std-env": "^3.5.0", - "strip-literal": "^2.0.0", - "test-exclude": "^6.0.0" + "istanbul-lib-source-maps": "^5.0.6", + "istanbul-reports": "^3.1.7", + "magic-string": "^0.30.12", + "magicast": "^0.3.5", + "std-env": "^3.8.0", + "test-exclude": "^7.0.1", + "tinyrainbow": "^1.2.0" }, "funding": { "url": "https://opencollective.com/vitest" }, "peerDependencies": { - "vitest": "1.6.0" + "@vitest/browser": "2.1.8", + "vitest": "2.1.8" + }, + "peerDependenciesMeta": { + "@vitest/browser": { + "optional": true + } } }, "node_modules/@vitest/expect": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-1.6.0.tgz", - "integrity": "sha512-ixEvFVQjycy/oNgHjqsL6AZCDduC+tflRluaHIzKIsdbzkLn2U/iBnVeJwB6HsIjQBdfMR8Z0tRxKUsvFJEeWQ==", + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-2.1.8.tgz", + "integrity": "sha512-8ytZ/fFHq2g4PJVAtDX57mayemKgDR6X3Oa2Foro+EygiOJHUXhCqBAAKQYYajZpFoIfvBCF1j6R6IYRSIUFuw==", "dev": true, "dependencies": { - "@vitest/spy": "1.6.0", - "@vitest/utils": "1.6.0", - "chai": "^4.3.10" + "@vitest/spy": "2.1.8", + "@vitest/utils": "2.1.8", + "chai": "^5.1.2", + "tinyrainbow": "^1.2.0" }, "funding": { "url": "https://opencollective.com/vitest" } }, - "node_modules/@vitest/runner": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-1.6.0.tgz", - "integrity": "sha512-P4xgwPjwesuBiHisAVz/LSSZtDjOTPYZVmNAnpHHSR6ONrf8eCJOFRvUwdHn30F5M1fxhqtl7QZQUk2dprIXAg==", + "node_modules/@vitest/mocker": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-2.1.8.tgz", + "integrity": "sha512-7guJ/47I6uqfttp33mgo6ga5Gr1VnL58rcqYKyShoRK9ebu8T5Rs6HN3s1NABiBeVTdWNrwUMcHH54uXZBN4zA==", "dev": true, "dependencies": { - "@vitest/utils": "1.6.0", - "p-limit": "^5.0.0", - "pathe": "^1.1.1" + "@vitest/spy": "2.1.8", + "estree-walker": "^3.0.3", + "magic-string": "^0.30.12" }, "funding": { "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "msw": "^2.4.9", + "vite": "^5.0.0" + }, + "peerDependenciesMeta": { + "msw": { + "optional": true + }, + "vite": { + "optional": true + } } }, - "node_modules/@vitest/runner/node_modules/p-limit": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-5.0.0.tgz", - "integrity": "sha512-/Eaoq+QyLSiXQ4lyYV23f14mZRQcXnxfHrN0vCai+ak9G0pp9iEQukIIZq5NccEvwRB8PUnZT0KsOoDCINS1qQ==", + "node_modules/@vitest/pretty-format": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-2.1.8.tgz", + "integrity": "sha512-9HiSZ9zpqNLKlbIDRWOnAWqgcA7xu+8YxXSekhr0Ykab7PAYFkhkwoqVArPOtJhPmYeE2YHgKZlj3CP36z2AJQ==", "dev": true, "dependencies": { - "yocto-queue": "^1.0.0" - }, - "engines": { - "node": ">=18" + "tinyrainbow": "^1.2.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://opencollective.com/vitest" } }, - "node_modules/@vitest/runner/node_modules/yocto-queue": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.1.1.tgz", - "integrity": "sha512-b4JR1PFR10y1mKjhHY9LaGo6tmrgjit7hxVIeAmyMw3jegXR4dhYqLaQF5zMXZxY7tLpMyJeLjr1C4rLmkVe8g==", + "node_modules/@vitest/runner": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-2.1.8.tgz", + "integrity": "sha512-17ub8vQstRnRlIU5k50bG+QOMLHRhYPAna5tw8tYbj+jzjcspnwnwtPtiOlkuKC4+ixDPTuLZiqiWWQ2PSXHVg==", "dev": true, - "engines": { - "node": ">=12.20" + "dependencies": { + "@vitest/utils": "2.1.8", + "pathe": "^1.1.2" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://opencollective.com/vitest" } }, "node_modules/@vitest/snapshot": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-1.6.0.tgz", - "integrity": "sha512-+Hx43f8Chus+DCmygqqfetcAZrDJwvTj0ymqjQq4CvmpKFSTVteEOBzCusu1x2tt4OJcvBflyHUE0DZSLgEMtQ==", + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-2.1.8.tgz", + "integrity": "sha512-20T7xRFbmnkfcmgVEz+z3AU/3b0cEzZOt/zmnvZEctg64/QZbSDJEVm9fLnnlSi74KibmRsO9/Qabi+t0vCRPg==", "dev": true, "dependencies": { - "magic-string": "^0.30.5", - "pathe": "^1.1.1", - "pretty-format": "^29.7.0" + "@vitest/pretty-format": "2.1.8", + "magic-string": "^0.30.12", + "pathe": "^1.1.2" }, "funding": { "url": "https://opencollective.com/vitest" } }, "node_modules/@vitest/spy": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-1.6.0.tgz", - "integrity": "sha512-leUTap6B/cqi/bQkXUu6bQV5TZPx7pmMBKBQiI0rJA8c3pB56ZsaTbREnF7CJfmvAS4V2cXIBAh/3rVwrrCYgw==", + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-2.1.8.tgz", + "integrity": "sha512-5swjf2q95gXeYPevtW0BLk6H8+bPlMb4Vw/9Em4hFxDcaOxS+e0LOX4yqNxoHzMR2akEB2xfpnWUzkZokmgWDg==", "dev": true, "dependencies": { - "tinyspy": "^2.2.0" + "tinyspy": "^3.0.2" }, "funding": { "url": "https://opencollective.com/vitest" } }, "node_modules/@vitest/ui": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@vitest/ui/-/ui-1.6.0.tgz", - "integrity": "sha512-k3Lyo+ONLOgylctiGovRKy7V4+dIN2yxstX3eY5cWFXH6WP+ooVX79YSyi0GagdTQzLmT43BF27T0s6dOIPBXA==", + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/@vitest/ui/-/ui-2.1.8.tgz", + "integrity": "sha512-5zPJ1fs0ixSVSs5+5V2XJjXLmNzjugHRyV11RqxYVR+oMcogZ9qTuSfKW+OcTV0JeFNznI83BNylzH6SSNJ1+w==", "dev": true, "dependencies": { - "@vitest/utils": "1.6.0", - "fast-glob": "^3.3.2", - "fflate": "^0.8.1", - "flatted": "^3.2.9", - "pathe": "^1.1.1", - "picocolors": "^1.0.0", - "sirv": "^2.0.4" + "@vitest/utils": "2.1.8", + "fflate": "^0.8.2", + "flatted": "^3.3.1", + "pathe": "^1.1.2", + "sirv": "^3.0.0", + "tinyglobby": "^0.2.10", + "tinyrainbow": "^1.2.0" }, "funding": { "url": "https://opencollective.com/vitest" }, "peerDependencies": { - "vitest": "1.6.0" + "vitest": "2.1.8" } }, "node_modules/@vitest/utils": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-1.6.0.tgz", - "integrity": "sha512-21cPiuGMoMZwiOHa2i4LXkMkMkCGzA+MVFV70jRwHo95dL4x/ts5GZhML1QWuy7yfp3WzK3lRvZi3JnXTYqrBw==", + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-2.1.8.tgz", + "integrity": "sha512-dwSoui6djdwbfFmIgbIjX2ZhIoG7Ex/+xpxyiEgIGzjliY8xGkcpITKTlp6B4MgtGkF2ilvm97cPM96XZaAgcA==", "dev": true, "dependencies": { - "diff-sequences": "^29.6.3", - "estree-walker": "^3.0.3", - "loupe": "^2.3.7", - "pretty-format": "^29.7.0" + "@vitest/pretty-format": "2.1.8", + "loupe": "^3.1.2", + "tinyrainbow": "^1.2.0" }, "funding": { "url": "https://opencollective.com/vitest" @@ -1596,18 +1594,6 @@ "acorn": ">=8.9.0" } }, - "node_modules/acorn-walk": { - "version": "8.3.4", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz", - "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==", - "dev": true, - "dependencies": { - "acorn": "^8.11.0" - }, - "engines": { - "node": ">=0.4.0" - } - }, "node_modules/ajv": { "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", @@ -1736,12 +1722,12 @@ } }, "node_modules/assertion-error": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", - "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", + "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", "dev": true, "engines": { - "node": "*" + "node": ">=12" } }, "node_modules/available-typed-arrays": { @@ -1894,21 +1880,19 @@ } }, "node_modules/chai": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/chai/-/chai-4.5.0.tgz", - "integrity": "sha512-RITGBfijLkBddZvnn8jdqoTypxvqbOLYQkGGxXzeFjVHvudaPw0HNFD9x928/eUwYWd2dPCugVqspGALTZZQKw==", + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/chai/-/chai-5.1.2.tgz", + "integrity": "sha512-aGtmf24DW6MLHHG5gCx4zaI3uBq3KRtxeVs0DjFH6Z0rDNbsvTxFASFvdj79pxjxZ8/5u3PIiN3IwEIQkiiuPw==", "dev": true, "dependencies": { - "assertion-error": "^1.1.0", - "check-error": "^1.0.3", - "deep-eql": "^4.1.3", - "get-func-name": "^2.0.2", - "loupe": "^2.3.6", - "pathval": "^1.1.1", - "type-detect": "^4.1.0" + "assertion-error": "^2.0.1", + "check-error": "^2.1.1", + "deep-eql": "^5.0.1", + "loupe": "^3.1.0", + "pathval": "^2.0.0" }, "engines": { - "node": ">=4" + "node": ">=12" } }, "node_modules/chalk": { @@ -1929,15 +1913,12 @@ } }, "node_modules/check-error": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.3.tgz", - "integrity": "sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-2.1.1.tgz", + "integrity": "sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==", "dev": true, - "dependencies": { - "get-func-name": "^2.0.2" - }, "engines": { - "node": "*" + "node": ">= 16" } }, "node_modules/chokidar": { @@ -2017,12 +1998,6 @@ "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", "dev": true }, - "node_modules/confbox": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.1.8.tgz", - "integrity": "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==", - "dev": true - }, "node_modules/cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", @@ -2118,13 +2093,10 @@ } }, "node_modules/deep-eql": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.4.tgz", - "integrity": "sha512-SUwdGfqdKOwxCPeVYjwSyRpJ7Z+fhpwIAtmCUdZIWZ/YP5R9WAsyuSgpLVDi9bjWoN2LXHNss/dk3urXtdQxGg==", + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-5.0.2.tgz", + "integrity": "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==", "dev": true, - "dependencies": { - "type-detect": "^4.0.0" - }, "engines": { "node": ">=6" } @@ -2176,15 +2148,6 @@ "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==", "dev": true }, - "node_modules/diff-sequences": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", - "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", - "dev": true, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, "node_modules/dir-glob": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", @@ -2334,6 +2297,12 @@ "node": ">= 0.4" } }, + "node_modules/es-module-lexer": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.6.0.tgz", + "integrity": "sha512-qqnD1yMU6tk/jnaMosogGySTZP8YtUgAffA9nMN+E/rjxcfRQ6IEk7IiozUjgxKoFHBGjTLnrHB/YC45r/59EQ==", + "dev": true + }, "node_modules/es-object-atoms": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.0.0.tgz", @@ -2558,9 +2527,9 @@ } }, "node_modules/esm-env": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/esm-env/-/esm-env-1.2.1.tgz", - "integrity": "sha512-U9JedYYjCnadUlXk7e1Kr+aENQhtUaoaV9+gZm1T8LC/YBAPJx3NSPIAurFOC0U5vrdSevnUJS2/wUVxGwPhng==" + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/esm-env/-/esm-env-1.2.2.tgz", + "integrity": "sha512-Epxrv+Nr/CaL4ZcFGPJIYLWFom+YeV1DqMLHJoEd9SYRxNbaFruBwfEX/kkHUJf55j2+TUbmDcmuilbP1TmXHA==" }, "node_modules/espree": { "version": "9.6.1", @@ -2594,9 +2563,9 @@ } }, "node_modules/esrap": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/esrap/-/esrap-1.3.2.tgz", - "integrity": "sha512-C4PXusxYhFT98GjLSmb20k9PREuUdporer50dhzGuJu9IJXktbMddVCMLAERl5dAHyAi73GWWCE4FVHGP1794g==", + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/esrap/-/esrap-1.4.2.tgz", + "integrity": "sha512-FhVlJzvTw7ZLxYZ7RyHwQCFE64dkkpzGNNnphaGCLwjqGk1SQcqzbgdx9FowPCktx6NOSHkzvcZ3vsvdH54YXA==", "dependencies": { "@jridgewell/sourcemap-codec": "^1.4.15" } @@ -2651,27 +2620,13 @@ "node": ">=0.8.x" } }, - "node_modules/execa": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz", - "integrity": "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==", + "node_modules/expect-type": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.1.0.tgz", + "integrity": "sha512-bFi65yM+xZgk+u/KRIpekdSYkTB5W1pEf0Lt8Q8Msh7b+eQ7LXVtIB1Bkm4fvclDEL1b2CZkMhv2mOeF8tMdkA==", "dev": true, - "dependencies": { - "cross-spawn": "^7.0.3", - "get-stream": "^8.0.1", - "human-signals": "^5.0.0", - "is-stream": "^3.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^5.1.0", - "onetime": "^6.0.0", - "signal-exit": "^4.1.0", - "strip-final-newline": "^3.0.0" - }, "engines": { - "node": ">=16.17" - }, - "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" + "node": ">=12.0.0" } }, "node_modules/fast-deep-equal": { @@ -2830,7 +2785,8 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true + "dev": true, + "peer": true }, "node_modules/fsevents": { "version": "2.3.3", @@ -2884,15 +2840,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/get-func-name": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.2.tgz", - "integrity": "sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==", - "dev": true, - "engines": { - "node": "*" - } - }, "node_modules/get-intrinsic": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.7.tgz", @@ -2930,18 +2877,6 @@ "node": ">= 0.4" } }, - "node_modules/get-stream": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz", - "integrity": "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==", - "dev": true, - "engines": { - "node": ">=16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/get-symbol-description": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.1.0.tgz", @@ -2965,6 +2900,7 @@ "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", "deprecated": "Glob versions prior to v9 are no longer supported", "dev": true, + "peer": true, "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -2997,6 +2933,7 @@ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, + "peer": true, "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -3007,6 +2944,7 @@ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, + "peer": true, "dependencies": { "brace-expansion": "^1.1.7" }, @@ -3207,15 +3145,6 @@ "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", "dev": true }, - "node_modules/human-signals": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz", - "integrity": "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==", - "dev": true, - "engines": { - "node": ">=16.17.0" - } - }, "node_modules/ignore": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", @@ -3264,6 +3193,7 @@ "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", "dev": true, + "peer": true, "dependencies": { "once": "^1.3.0", "wrappy": "1" @@ -3273,7 +3203,8 @@ "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true + "dev": true, + "peer": true }, "node_modules/internal-slot": { "version": "1.1.0", @@ -3596,18 +3527,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-stream": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", - "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", - "dev": true, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/is-string": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.1.1.tgz", @@ -3785,12 +3704,6 @@ "jiti": "bin/jiti.js" } }, - "node_modules/js-tokens": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-9.0.1.tgz", - "integrity": "sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==", - "dev": true - }, "node_modules/js-yaml": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", @@ -3897,22 +3810,6 @@ "node": ">=4" } }, - "node_modules/local-pkg": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/local-pkg/-/local-pkg-0.5.1.tgz", - "integrity": "sha512-9rrA30MRRP3gBD3HTGnC6cDFpaE1kVDWxWgqWJUN0RvDNAo+Nz/9GxB+nHOH0ifbVFy0hSA1V6vFDvnx54lTEQ==", - "dev": true, - "dependencies": { - "mlly": "^1.7.3", - "pkg-types": "^1.2.1" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/antfu" - } - }, "node_modules/localforage": { "version": "1.10.0", "resolved": "https://registry.npmjs.org/localforage/-/localforage-1.10.0.tgz", @@ -3964,13 +3861,10 @@ } }, "node_modules/loupe": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.7.tgz", - "integrity": "sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==", - "dev": true, - "dependencies": { - "get-func-name": "^2.0.1" - } + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.1.2.tgz", + "integrity": "sha512-23I4pFZHmAemUnz8WZXbYRSKYj801VDaNv9ETuMh7IrMc7VuVVSo+Z9iLE3ni30+U48iDWfi30d3twAXBYmnCg==", + "dev": true }, "node_modules/lru-cache": { "version": "10.4.3", @@ -4046,12 +3940,6 @@ "node": ">= 0.10.0" } }, - "node_modules/merge-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", - "dev": true - }, "node_modules/merge2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", @@ -4074,18 +3962,6 @@ "node": ">=8.6" } }, - "node_modules/mimic-fn": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", - "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/minimatch": { "version": "9.0.3", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", @@ -4110,18 +3986,6 @@ "node": ">=16 || 14 >=14.17" } }, - "node_modules/mlly": { - "version": "1.7.3", - "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.7.3.tgz", - "integrity": "sha512-xUsx5n/mN0uQf4V548PKQ+YShA4/IW0KI1dZhrNrPCLG+xizETbHTkOa1f8/xut9JRPp8kQuMnz0oqwkTiLo/A==", - "dev": true, - "dependencies": { - "acorn": "^8.14.0", - "pathe": "^1.1.2", - "pkg-types": "^1.2.1", - "ufo": "^1.5.4" - } - }, "node_modules/moment": { "version": "2.29.4", "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz", @@ -4402,33 +4266,6 @@ "which": "bin/which" } }, - "node_modules/npm-run-path": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.3.0.tgz", - "integrity": "sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==", - "dev": true, - "dependencies": { - "path-key": "^4.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/npm-run-path/node_modules/path-key": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", - "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -4589,25 +4426,11 @@ "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", "dev": true, + "peer": true, "dependencies": { "wrappy": "1" } }, - "node_modules/onetime": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", - "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", - "dev": true, - "dependencies": { - "mimic-fn": "^4.0.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/optionator": { "version": "0.9.4", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", @@ -4682,9 +4505,9 @@ "dev": true }, "node_modules/papaparse": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/papaparse/-/papaparse-5.4.1.tgz", - "integrity": "sha512-HipMsgJkZu8br23pW15uvo6sib6wne/4woLZPlFf3rpDyMe9ywEXUsuD7+6K9PRkJlVT51j/sCOYDKGGS3ZJrw==", + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/papaparse/-/papaparse-5.5.1.tgz", + "integrity": "sha512-EuEKUhyxrHVozD7g3/ztsJn6qaKse8RPfR6buNB2dMJvdtXNhcw8jccVi/LxNEY3HVrV6GO6Z4OoeCG9Iy9wpA==", "dev": true }, "node_modules/parent-module": { @@ -4734,6 +4557,7 @@ "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", "dev": true, + "peer": true, "engines": { "node": ">=0.10.0" } @@ -4785,12 +4609,12 @@ "dev": true }, "node_modules/pathval": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", - "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-2.0.0.tgz", + "integrity": "sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA==", "dev": true, "engines": { - "node": "*" + "node": ">= 14.16" } }, "node_modules/picocolors": { @@ -4841,17 +4665,6 @@ "node": ">= 6" } }, - "node_modules/pkg-types": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.3.0.tgz", - "integrity": "sha512-kS7yWjVFCkIw9hqdJBoMxDdzEngmkr5FXeWZZfQ6GoYacjVnsW6l2CcYW/0ThD0vF4LPJgVYnrg4d0uuhwYQbg==", - "dev": true, - "dependencies": { - "confbox": "^0.1.8", - "mlly": "^1.7.3", - "pathe": "^1.1.2" - } - }, "node_modules/possible-typed-array-names": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz", @@ -5123,32 +4936,6 @@ } } }, - "node_modules/pretty-format": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", - "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", - "dev": true, - "dependencies": { - "@jest/schemas": "^29.6.3", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/pretty-format/node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", @@ -5179,12 +4966,6 @@ } ] }, - "node_modules/react-is": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", - "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", - "dev": true - }, "node_modules/read-cache": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", @@ -5341,9 +5122,9 @@ } }, "node_modules/rollup": { - "version": "4.30.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.30.0.tgz", - "integrity": "sha512-sDnr1pcjTgUT69qBksNF1N1anwfbyYG6TBQ22b03bII8EdiUQ7J0TlozVaTMjT/eEJAO49e1ndV7t+UZfL1+vA==", + "version": "4.30.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.30.1.tgz", + "integrity": "sha512-mlJ4glW020fPuLi7DkM/lN97mYEZGWeqBnrljzN0gs7GLctqX3lNWxKQ7Gl712UAX+6fog/L3jh4gb7R6aVi3w==", "dev": true, "dependencies": { "@types/estree": "1.0.6" @@ -5356,25 +5137,25 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.30.0", - "@rollup/rollup-android-arm64": "4.30.0", - "@rollup/rollup-darwin-arm64": "4.30.0", - "@rollup/rollup-darwin-x64": "4.30.0", - "@rollup/rollup-freebsd-arm64": "4.30.0", - "@rollup/rollup-freebsd-x64": "4.30.0", - "@rollup/rollup-linux-arm-gnueabihf": "4.30.0", - "@rollup/rollup-linux-arm-musleabihf": "4.30.0", - "@rollup/rollup-linux-arm64-gnu": "4.30.0", - "@rollup/rollup-linux-arm64-musl": "4.30.0", - "@rollup/rollup-linux-loongarch64-gnu": "4.30.0", - "@rollup/rollup-linux-powerpc64le-gnu": "4.30.0", - "@rollup/rollup-linux-riscv64-gnu": "4.30.0", - "@rollup/rollup-linux-s390x-gnu": "4.30.0", - "@rollup/rollup-linux-x64-gnu": "4.30.0", - "@rollup/rollup-linux-x64-musl": "4.30.0", - "@rollup/rollup-win32-arm64-msvc": "4.30.0", - "@rollup/rollup-win32-ia32-msvc": "4.30.0", - "@rollup/rollup-win32-x64-msvc": "4.30.0", + "@rollup/rollup-android-arm-eabi": "4.30.1", + "@rollup/rollup-android-arm64": "4.30.1", + "@rollup/rollup-darwin-arm64": "4.30.1", + "@rollup/rollup-darwin-x64": "4.30.1", + "@rollup/rollup-freebsd-arm64": "4.30.1", + "@rollup/rollup-freebsd-x64": "4.30.1", + "@rollup/rollup-linux-arm-gnueabihf": "4.30.1", + "@rollup/rollup-linux-arm-musleabihf": "4.30.1", + "@rollup/rollup-linux-arm64-gnu": "4.30.1", + "@rollup/rollup-linux-arm64-musl": "4.30.1", + "@rollup/rollup-linux-loongarch64-gnu": "4.30.1", + "@rollup/rollup-linux-powerpc64le-gnu": "4.30.1", + "@rollup/rollup-linux-riscv64-gnu": "4.30.1", + "@rollup/rollup-linux-s390x-gnu": "4.30.1", + "@rollup/rollup-linux-x64-gnu": "4.30.1", + "@rollup/rollup-linux-x64-musl": "4.30.1", + "@rollup/rollup-win32-arm64-msvc": "4.30.1", + "@rollup/rollup-win32-ia32-msvc": "4.30.1", + "@rollup/rollup-win32-x64-msvc": "4.30.1", "fsevents": "~2.3.2" } }, @@ -5635,9 +5416,9 @@ } }, "node_modules/sirv": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/sirv/-/sirv-2.0.4.tgz", - "integrity": "sha512-94Bdh3cC2PKrbgSOUqTiGPWVZeSiXfKOVZNJniWoqrWrRkB1CJzBU3NEbiTsPcYy1lDsANA/THzS+9WBiy5nfQ==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/sirv/-/sirv-3.0.0.tgz", + "integrity": "sha512-BPwJGUeDaDCHihkORDchNyyTvWFhcusy1XMmhEVTQTwGeybFbp8YEmB+njbPnth1FibULBSBVwCQni25XlCUDg==", "dev": true, "dependencies": { "@polka/url": "^1.0.0-next.24", @@ -5645,7 +5426,7 @@ "totalist": "^3.0.0" }, "engines": { - "node": ">= 10" + "node": ">=18" } }, "node_modules/slash": { @@ -5889,18 +5670,6 @@ "node": ">=4" } }, - "node_modules/strip-final-newline": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", - "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/strip-json-comments": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", @@ -5914,18 +5683,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/strip-literal": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/strip-literal/-/strip-literal-2.1.1.tgz", - "integrity": "sha512-631UJ6O00eNGfMiWG78ck80dfBab8X6IVFB51jZK5Icd7XAs60Z5y7QdSd/wGIklnWvRbUNloVzhOKKmutxQ6Q==", - "dev": true, - "dependencies": { - "js-tokens": "^9.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/antfu" - } - }, "node_modules/style-mod": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/style-mod/-/style-mod-4.1.2.tgz", @@ -6014,9 +5771,9 @@ } }, "node_modules/svelte": { - "version": "5.16.4", - "resolved": "https://registry.npmjs.org/svelte/-/svelte-5.16.4.tgz", - "integrity": "sha512-bYQ4Ai0aryxE80/kRkC/zDnh5d8BgrSUM3/1FuGxknnijPq4aF+HEV7cTPVtrI+5AdT13dL4L9XWhUgARINS8Q==", + "version": "5.17.3", + "resolved": "https://registry.npmjs.org/svelte/-/svelte-5.17.3.tgz", + "integrity": "sha512-eLgtpR2JiTgeuNQRCDcLx35Z7Lu9Qe09GPOz+gvtR9nmIZu5xgFd6oFiLGQlxLD0/u7xVyF5AUkjDVyFHe6Bvw==", "dependencies": { "@ampproject/remapping": "^2.3.0", "@jridgewell/sourcemap-codec": "^1.5.0", @@ -6130,39 +5887,52 @@ } }, "node_modules/test-exclude": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", - "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-7.0.1.tgz", + "integrity": "sha512-pFYqmTw68LXVjeWJMST4+borgQP2AyMNbg1BpZh9LbyhUeNkeaPF9gzfPGUAnSMV3qPYdWUwDIjjCLiSDOl7vg==", "dev": true, "dependencies": { "@istanbuljs/schema": "^0.1.2", - "glob": "^7.1.4", - "minimatch": "^3.0.4" + "glob": "^10.4.1", + "minimatch": "^9.0.4" }, "engines": { - "node": ">=8" + "node": ">=18" } }, - "node_modules/test-exclude/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "node_modules/test-exclude/node_modules/glob": { + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", "dev": true, "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, "node_modules/test-exclude/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", "dev": true, "dependencies": { - "brace-expansion": "^1.1.7" + "brace-expansion": "^2.0.1" }, "engines": { - "node": "*" + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, "node_modules/text-table": { @@ -6199,19 +5969,73 @@ "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", "dev": true }, + "node_modules/tinyexec": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.2.tgz", + "integrity": "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==", + "dev": true + }, + "node_modules/tinyglobby": { + "version": "0.2.10", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.10.tgz", + "integrity": "sha512-Zc+8eJlFMvgatPZTl6A9L/yht8QqdmUNtURHaKZLmKBE12hNPSrqNkUp2cs3M/UKmNVVAMFQYSjYIVHDjW5zew==", + "dev": true, + "dependencies": { + "fdir": "^6.4.2", + "picomatch": "^4.0.2" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/tinyglobby/node_modules/fdir": { + "version": "6.4.2", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.2.tgz", + "integrity": "sha512-KnhMXsKSPZlAhp7+IjUkRZKPb4fUyccpDrdFXbi4QL1qkmFh9kVY09Yox+n4MaOb3lHZ1Tv829C3oaaXoMYPDQ==", + "dev": true, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/tinyglobby/node_modules/picomatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", + "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/tinypool": { - "version": "0.8.4", - "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-0.8.4.tgz", - "integrity": "sha512-i11VH5gS6IFeLY3gMBQ00/MmLncVP7JLXOw1vlgkytLmJK7QnEr7NXf0LBdxfmNPAeyetukOk0bOYrJrFGjYJQ==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.0.2.tgz", + "integrity": "sha512-al6n+QEANGFOMf/dmUMsuS5/r9B06uwlyNjZZql/zv8J7ybHCgoihBNORZCY2mzUuAnomQa2JdhyHKzZxPCrFA==", + "dev": true, + "engines": { + "node": "^18.0.0 || >=20.0.0" + } + }, + "node_modules/tinyrainbow": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-1.2.0.tgz", + "integrity": "sha512-weEDEq7Z5eTHPDh4xjX789+fHfF+P8boiFB+0vbWzpbnbsEr/GRaohi/uMKxg8RZMXnl1ItAi/IUHWMsjDV7kQ==", "dev": true, "engines": { "node": ">=14.0.0" } }, "node_modules/tinyspy": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-2.2.1.tgz", - "integrity": "sha512-KYad6Vy5VDWV4GH3fjpseMQ/XU2BhIYP7Vzd0LG44qRWm/Yt2WCOTicFdvmgo6gWaqooMQCawTtILVQJupKu7A==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-3.0.2.tgz", + "integrity": "sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==", "dev": true, "engines": { "node": ">=14.0.0" @@ -6275,15 +6099,6 @@ "node": ">= 0.8.0" } }, - "node_modules/type-detect": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.1.0.tgz", - "integrity": "sha512-Acylog8/luQ8L7il+geoSxhEkazvkslg7PSNKOX59mbB9cOveP5aq9h74Y7YU8yDpJwetzQQrfIwtf4Wp4LKcw==", - "dev": true, - "engines": { - "node": ">=4" - } - }, "node_modules/type-fest": { "version": "0.20.2", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", @@ -6372,9 +6187,9 @@ } }, "node_modules/typescript": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.2.tgz", - "integrity": "sha512-i5t66RHxDvVN40HfDd1PsEThGNnlMCMT3jMUuoh9/0TaqWevNontacunWyN02LA9/fIbEWlcHZcgTKb9QoaLfg==", + "version": "5.7.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.3.tgz", + "integrity": "sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw==", "dev": true, "bin": { "tsc": "bin/tsc", @@ -6384,12 +6199,6 @@ "node": ">=14.17" } }, - "node_modules/ufo": { - "version": "1.5.4", - "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.5.4.tgz", - "integrity": "sha512-UsUk3byDzKd04EyoZ7U4DOlxQaD14JUKQl6/P7wiX4FNvUfm3XL246n9W5AmqwW5RSFJ27NAuM0iLscAOYUiGQ==", - "dev": true - }, "node_modules/unbox-primitive": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz", @@ -6500,15 +6309,15 @@ } }, "node_modules/vite-node": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-1.6.0.tgz", - "integrity": "sha512-de6HJgzC+TFzOu0NTC4RAIsyf/DY/ibWDYQUcuEA84EMHhcefTUGkjFHKKEJhQN4A+6I0u++kr3l36ZF2d7XRw==", + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-2.1.8.tgz", + "integrity": "sha512-uPAwSr57kYjAUux+8E2j0q0Fxpn8M9VoyfGiRI8Kfktz9NcYMCenwY5RnZxnF1WTu3TGiYipirIzacLL3VVGFg==", "dev": true, "dependencies": { "cac": "^6.7.14", - "debug": "^4.3.4", - "pathe": "^1.1.1", - "picocolors": "^1.0.0", + "debug": "^4.3.7", + "es-module-lexer": "^1.5.4", + "pathe": "^1.1.2", "vite": "^5.0.0" }, "bin": { @@ -6521,6 +6330,15 @@ "url": "https://opencollective.com/vitest" } }, + "node_modules/vite-plugin-wasm": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/vite-plugin-wasm/-/vite-plugin-wasm-3.4.1.tgz", + "integrity": "sha512-ja3nSo2UCkVeitltJGkS3pfQHAanHv/DqGatdI39ja6McgABlpsZ5hVgl6wuR8Qx5etY3T5qgDQhOWzc5RReZA==", + "dev": true, + "peerDependencies": { + "vite": "^2 || ^3 || ^4 || ^5 || ^6" + } + }, "node_modules/vite/node_modules/@esbuild/aix-ppc64": { "version": "0.21.5", "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", @@ -6928,31 +6746,31 @@ } }, "node_modules/vitest": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-1.6.0.tgz", - "integrity": "sha512-H5r/dN06swuFnzNFhq/dnz37bPXnq8xB2xB5JOVk8K09rUtoeNN+LHWkoQ0A/i3hvbUKKcCei9KpbxqHMLhLLA==", - "dev": true, - "dependencies": { - "@vitest/expect": "1.6.0", - "@vitest/runner": "1.6.0", - "@vitest/snapshot": "1.6.0", - "@vitest/spy": "1.6.0", - "@vitest/utils": "1.6.0", - "acorn-walk": "^8.3.2", - "chai": "^4.3.10", - "debug": "^4.3.4", - "execa": "^8.0.1", - "local-pkg": "^0.5.0", - "magic-string": "^0.30.5", - "pathe": "^1.1.1", - "picocolors": "^1.0.0", - "std-env": "^3.5.0", - "strip-literal": "^2.0.0", - "tinybench": "^2.5.1", - "tinypool": "^0.8.3", + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-2.1.8.tgz", + "integrity": "sha512-1vBKTZskHw/aosXqQUlVWWlGUxSJR8YtiyZDJAFeW2kPAeX6S3Sool0mjspO+kXLuxVWlEDDowBAeqeAQefqLQ==", + "dev": true, + "dependencies": { + "@vitest/expect": "2.1.8", + "@vitest/mocker": "2.1.8", + "@vitest/pretty-format": "^2.1.8", + "@vitest/runner": "2.1.8", + "@vitest/snapshot": "2.1.8", + "@vitest/spy": "2.1.8", + "@vitest/utils": "2.1.8", + "chai": "^5.1.2", + "debug": "^4.3.7", + "expect-type": "^1.1.0", + "magic-string": "^0.30.12", + "pathe": "^1.1.2", + "std-env": "^3.8.0", + "tinybench": "^2.9.0", + "tinyexec": "^0.3.1", + "tinypool": "^1.0.1", + "tinyrainbow": "^1.2.0", "vite": "^5.0.0", - "vite-node": "1.6.0", - "why-is-node-running": "^2.2.2" + "vite-node": "2.1.8", + "why-is-node-running": "^2.3.0" }, "bin": { "vitest": "vitest.mjs" @@ -6966,8 +6784,8 @@ "peerDependencies": { "@edge-runtime/vm": "*", "@types/node": "^18.0.0 || >=20.0.0", - "@vitest/browser": "1.6.0", - "@vitest/ui": "1.6.0", + "@vitest/browser": "2.1.8", + "@vitest/ui": "2.1.8", "happy-dom": "*", "jsdom": "*" }, @@ -7221,7 +7039,8 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true + "dev": true, + "peer": true }, "node_modules/yaml": { "version": "2.7.0", diff --git a/package.json b/package.json index 5e43cac7..e5edf415 100644 --- a/package.json +++ b/package.json @@ -14,8 +14,8 @@ "version:prod": "node version-bump.mjs && git add manifest.json versions.json package.json", "version:beta": "node version-bump-beta.mjs && git add manifest-beta.json versions.json package.json", "release:beta": "npm run version:beta && git commit -m 'release:beta' && git push origin master:master && git tag -a $npm_package_version -m \"$npm_package_version\" && git push --tags", - "test": "vitest", - "coverage:ui": "vitest run --coverage --coverage.include 'src/**' --coverage.reporter html && npx vite preview --outDir ./coverage --open", + "test": "vitest --config vite.config.mjs", + "coverage:ui": "vitest run --config vite.config.mjs --coverage --coverage.include 'src/**' --coverage.reporter html && npx vite preview --outDir ./coverage --open", "wasm:build": "cd wasm && wasm-pack build --target web", "wasm:dev": "cd wasm && wasm-pack build --dev --target web", "wasm:profile": "cd wasm && wasm-pack build --profiling --target web", @@ -34,8 +34,8 @@ "@types/obsidian-typings": "npm:obsidian-typings@^1.0.6", "@typescript-eslint/eslint-plugin": "6.18.1", "@typescript-eslint/parser": "6.18.1", - "@vitest/coverage-v8": "^1.3.1", - "@vitest/ui": "^1.3.1", + "@vitest/coverage-v8": "^2.1.8", + "@vitest/ui": "^2.1.8", "builtin-modules": "3.3.0", "esbuild": "0.24.2", "esbuild-svelte": "^0.9.0", @@ -50,7 +50,8 @@ "tailwindcss": "^3.4.1", "tslib": "2.6.2", "typescript": "^5.5.0", - "vitest": "^1.3.1" + "vite-plugin-wasm": "^3.4.1", + "vitest": "^2.1.8" }, "dependencies": { "graphology": "^0.25.4", diff --git a/src/api/index.ts b/src/api/index.ts index 0f8edaa9..3c458456 100644 --- a/src/api/index.ts +++ b/src/api/index.ts @@ -41,8 +41,9 @@ export class BCAPI { if (!start_node) throw new Error("No active file"); return ListIndex.build( - this.plugin, + this.plugin.graph, start_node, + this.plugin.settings, Object.assign({ ...ListIndex.DEFAULT_OPTIONS }, options), ); } diff --git a/src/commands/list_index/index.ts b/src/commands/list_index/index.ts index 9792cd13..4d5d45ac 100644 --- a/src/commands/list_index/index.ts +++ b/src/commands/list_index/index.ts @@ -1,12 +1,12 @@ import type { EdgeSortId } from "src/const/graph"; import type { LinkKind } from "src/interfaces/links"; -import type { ShowNodeOptions } from "src/interfaces/settings"; -import type BreadcrumbsPlugin from "src/main"; +import type { BreadcrumbsSettings, ShowNodeOptions } from "src/interfaces/settings"; import { Links } from "src/utils/links"; import { toNodeStringifyOptions, type EdgeAttribute } from "src/graph/utils"; import { FlatTraversalData, FlatTraversalResult, + NoteGraph, TraversalOptions, TraversalPostprocessOptions, create_edge_sorter, @@ -14,7 +14,6 @@ import { export namespace ListIndex { export type Options = { - show_entry_nodes: boolean; // TODO: merge_fields: boolean; indent: string; fields: string[]; @@ -28,7 +27,6 @@ export namespace ListIndex { }; export const DEFAULT_OPTIONS: Options = { - show_entry_nodes: false, fields: [], indent: "\\t", link_kind: "wiki", @@ -47,11 +45,12 @@ export namespace ListIndex { // TODO(Rust): This should probably be moved to the Rust side export const edge_tree_to_list_index = ( - plugin: BreadcrumbsPlugin, + graph: NoteGraph, tree: FlatTraversalResult | undefined, + plugin_settings: BreadcrumbsSettings | undefined, options: Pick< Options, - "link_kind" | "indent" | "show_node_options" | "show_attributes" | "show_entry_nodes" + "link_kind" | "indent" | "show_node_options" | "show_attributes" >, ) => { if (!tree) { @@ -60,51 +59,23 @@ export namespace ListIndex { const all_traversal_data = tree.data; - if (options.show_entry_nodes) { - return Array.from(tree.entry_nodes).map( - (node_index) => { - const datum = all_traversal_data[node_index]; - const edge = datum.edge; - - const display = edge.stringify_source( - plugin.graph, - toNodeStringifyOptions(plugin, options.show_node_options), - ); - - const link = Links.ify(edge.source_path(plugin.graph), display, { - link_kind: options.link_kind, - }); - - const attr = edge.get_attribute_label( - plugin.graph, - options.show_attributes, - ); - - return `- ${link} ${attr}\n` + edge_tree_to_list_index_inner( - plugin, - all_traversal_data, - [datum], - options, - ); - }, - ).join("\n"); - } else { - const current_nodes = Array.from(tree.entry_nodes).map( - (node_index) => all_traversal_data[node_index], - ); - return edge_tree_to_list_index_inner( - plugin, - all_traversal_data, - current_nodes, - options, - ); - } + const current_nodes = Array.from(tree.entry_nodes).map( + (node_index) => all_traversal_data[node_index], + ); + return edge_tree_to_list_index_inner( + graph, + all_traversal_data, + current_nodes, + plugin_settings, + options, + ); }; export const edge_tree_to_list_index_inner = ( - plugin: BreadcrumbsPlugin, + graph: NoteGraph, all_traversal_data: FlatTraversalData[], current_nodes: FlatTraversalData[], + plugin_settings: BreadcrumbsSettings | undefined, options: Pick< Options, "link_kind" | "indent" | "show_node_options" | "show_attributes" @@ -117,29 +88,30 @@ export namespace ListIndex { const { edge, children, depth } = datum; const display = edge.stringify_target( - plugin.graph, - toNodeStringifyOptions(plugin, options.show_node_options), + graph, + toNodeStringifyOptions(plugin_settings, options.show_node_options), ); - const link = Links.ify(edge.target_path(plugin.graph), display, { + const link = Links.ify(edge.target_path(graph), display, { link_kind: options.link_kind, }); const attr = edge.get_attribute_label( - plugin.graph, + graph, options.show_attributes, ); - index += real_indent.repeat(depth - 1) + `- ${link} ${attr}\n`; + index += real_indent.repeat(depth - 1) + (attr ? `- ${link} (${attr})\n` : `- ${link}\n`); const new_children = Array.from(children).map( (child_id) => all_traversal_data[child_id], ); index += edge_tree_to_list_index_inner( - plugin, + graph, all_traversal_data, new_children, + plugin_settings, options, ); }); @@ -148,8 +120,9 @@ export namespace ListIndex { }; export const build = ( - plugin: BreadcrumbsPlugin, + graph: NoteGraph, start_node: string, + plugin_settings: BreadcrumbsSettings | undefined, options: Options, ) => { const traversal_options = new TraversalOptions( @@ -167,11 +140,11 @@ export namespace ListIndex { false, ); - const traversal_result = plugin.graph.rec_traverse_and_process( + const traversal_result = graph.rec_traverse_and_process( traversal_options, postprocess_options, ); - return edge_tree_to_list_index(plugin, traversal_result, options); + return edge_tree_to_list_index(graph, traversal_result, plugin_settings, options); }; } diff --git a/src/components/NestedEdgeList.svelte b/src/components/NestedEdgeList.svelte index 0b5c80ae..7b9ebf9b 100644 --- a/src/components/NestedEdgeList.svelte +++ b/src/components/NestedEdgeList.svelte @@ -31,7 +31,7 @@ }: Props = $props(); let node_stringify_options = toNodeStringifyOptions( - plugin, + plugin.settings, show_node_options, ); diff --git a/src/graph/utils.ts b/src/graph/utils.ts index c8416928..45c69bda 100644 --- a/src/graph/utils.ts +++ b/src/graph/utils.ts @@ -1,12 +1,12 @@ import type BreadcrumbsPlugin from "src/main"; -import type { ShowNodeOptions } from "src/interfaces/settings"; +import type { BreadcrumbsSettings, ShowNodeOptions } from "src/interfaces/settings"; import { NodeStringifyOptions } from "wasm/pkg/breadcrumbs_graph_wasm"; export function toNodeStringifyOptions( - plugin: BreadcrumbsPlugin, + settings: BreadcrumbsSettings | undefined, options: ShowNodeOptions, ): NodeStringifyOptions { - const { dendron_note } = plugin.settings.explicit_edge_sources; + const dendron_note = settings?.explicit_edge_sources.dendron_note ?? { enabled: false }; return new NodeStringifyOptions( options.ext, diff --git a/src/main.ts b/src/main.ts index 140f79fa..550b9f9d 100644 --- a/src/main.ts +++ b/src/main.ts @@ -28,7 +28,7 @@ import init, { RenameNoteGraphUpdate, AddNoteGraphUpdate, GCNodeData, -} from "../wasm/pkg"; +} from "../wasm/pkg/breadcrumbs_graph_wasm"; export enum BCEvent { GRAPH_UPDATE = "graph-update", diff --git a/tests/commands/list_index.test.ts b/tests/commands/list_index.test.ts index f37d1605..aa0d02be 100644 --- a/tests/commands/list_index.test.ts +++ b/tests/commands/list_index.test.ts @@ -1,105 +1,136 @@ -// import { ListIndex } from "src/commands/list_index"; -// import { BCGraph } from "src/graph/MyMultiGraph"; -// import { _mock_edge } from "tests/__mocks__/graph"; -// import { describe, expect, test } from "vitest"; +import { ListIndex } from "src/commands/list_index"; +import { describe, expect, test, beforeEach } from "vitest"; +import init, { create_graph, GCEdgeData, GCNodeData } from "wasm/pkg/breadcrumbs_graph_wasm"; +import fs from "node:fs/promises"; -// const edges = [ -// _mock_edge("index.md", "1.md", {}), -// _mock_edge("1.md", "1.1.md", {}), -// _mock_edge("1.1.md", "1.1.1.md", {}), -// _mock_edge("1.1.md", "1.1.2.md", {}), -// _mock_edge("1.md", "1.2.md", {}), -// _mock_edge("1.2.md", "1.2.1.md", {}), -// _mock_edge("1.2.md", "1.2.2.md", {}), -// _mock_edge("index.md", "2.md", {}), -// _mock_edge("2.md", "2.1.md", {}), -// _mock_edge("2.1.md", "2.1.1.md", {}), -// _mock_edge("2.1.md", "2.1.2.md", {}), -// _mock_edge("2.md", "2.2.md", {}), -// _mock_edge("2.2.md", "2.2.1.md", {}), -// _mock_edge("2.2.md", "2.2.2.md", {}), -// ]; +function getEdges() { + return [ + new GCEdgeData("index.md", "1.md", "down", ""), + new GCEdgeData("1.md", "1.1.md", "down", ""), + new GCEdgeData("1.1.md", "1.1.1.md", "down", ""), + new GCEdgeData("1.1.md", "1.1.2.md", "down", ""), + new GCEdgeData("1.md", "1.2.md", "down", ""), + new GCEdgeData("1.2.md", "1.2.1.md", "down", ""), + new GCEdgeData("1.2.md", "1.2.2.md", "down", ""), + new GCEdgeData("index.md", "2.md", "down", ""), + new GCEdgeData("2.md", "2.1.md", "down", ""), + new GCEdgeData("2.1.md", "2.1.1.md", "down", ""), + new GCEdgeData("2.1.md", "2.1.2.md", "down", ""), + new GCEdgeData("2.md", "2.2.md", "down", ""), + new GCEdgeData("2.2.md", "2.2.1.md", "down", ""), + new GCEdgeData("2.2.md", "2.2.2.md", "down", ""), + ]; +} -// describe("build", () => { -// test("binary-tree > defaults", () => { -// const graph = new BCGraph({ edges }); +function getNodes() { + return [ + new GCNodeData("index.md", [], true, false, false), + new GCNodeData("1.md", [], true, false, false), + new GCNodeData("1.1.md", [], true, false, false), + new GCNodeData("1.1.1.md", [], true, false, false), + new GCNodeData("1.1.2.md", [], true, false, false), + new GCNodeData("1.2.md", [], true, false, false), + new GCNodeData("1.2.1.md", [], true, false, false), + new GCNodeData("1.2.2.md", [], true, false, false), + new GCNodeData("2.md", [], true, false, false), + new GCNodeData("2.1.md", [], true, false, false), + new GCNodeData("2.1.1.md", [], true, false, false), + new GCNodeData("2.1.2.md", [], true, false, false), + new GCNodeData("2.2.md", [], true, false, false), + new GCNodeData("2.2.1.md", [], true, false, false), + new GCNodeData("2.2.2.md", [], true, false, false), + ] +} -// const list_index = ListIndex.build(graph, "index.md", { -// indent: " ", -// fields: ["down"], -// show_attributes: [], -// field_group_labels: [], -// link_kind: "none", -// edge_sort_id: { -// order: 1, -// field: "basename", -// }, -// show_node_options: { -// ext: false, -// alias: false, -// folder: false, -// }, -// }); -// expect(list_index).toBe( -// [ -// "- 1", -// " - 1.1", -// " - 1.1.1", -// " - 1.1.2", -// " - 1.2", -// " - 1.2.1", -// " - 1.2.2", -// "- 2", -// " - 2.1", -// " - 2.1.1", -// " - 2.1.2", -// " - 2.2", -// " - 2.2.1", -// " - 2.2.2", -// "", -// ].join("\n"), -// ); -// }); +beforeEach(async () => { + const wasmSource = await fs.readFile("wasm/pkg/breadcrumbs_graph_wasm_bg.wasm"); + const wasmModule = await WebAssembly.compile(wasmSource); + await init(wasmModule); +}); -// test("binary-tree > indent + show-attributes + link_kind + edge_sort_id", () => { -// const graph = new BCGraph({ edges }); +describe("build", () => { + test("binary-tree > defaults", () => { + const graph = create_graph(); + graph.build_graph(getNodes(), getEdges(), []); -// const list_index = ListIndex.build(graph, "index.md", { -// indent: ".", -// fields: ["down"], -// link_kind: "wiki", -// field_group_labels: [], -// show_attributes: ["explicit", "field"], -// edge_sort_id: { -// order: -1, -// field: "basename", -// }, -// show_node_options: { -// ext: true, -// alias: false, -// folder: false, -// }, -// }); + const list_index = ListIndex.build(graph, "index.md", undefined, { + indent: " ", + fields: ["down"], + show_attributes: [], + field_group_labels: [], + link_kind: "none", + edge_sort_id: { + order: 1, + field: "basename", + }, + show_node_options: { + ext: false, + alias: false, + folder: false, + }, + }); -// expect(list_index).toBe( -// [ -// "- [[2]] (field=down explicit=true)", -// ".- [[2.2]] (field=down explicit=true)", -// "..- [[2.2.2]] (field=down explicit=true)", -// "..- [[2.2.1]] (field=down explicit=true)", -// ".- [[2.1]] (field=down explicit=true)", -// "..- [[2.1.2]] (field=down explicit=true)", -// "..- [[2.1.1]] (field=down explicit=true)", -// "- [[1]] (field=down explicit=true)", -// ".- [[1.2]] (field=down explicit=true)", -// "..- [[1.2.2]] (field=down explicit=true)", -// "..- [[1.2.1]] (field=down explicit=true)", -// ".- [[1.1]] (field=down explicit=true)", -// "..- [[1.1.2]] (field=down explicit=true)", -// "..- [[1.1.1]] (field=down explicit=true)", -// "", -// ].join("\n"), -// ); -// }); -// }); + expect(list_index).toBe( + [ + "- 1", + " - 1.1", + " - 1.1.1", + " - 1.1.2", + " - 1.2", + " - 1.2.1", + " - 1.2.2", + "- 2", + " - 2.1", + " - 2.1.1", + " - 2.1.2", + " - 2.2", + " - 2.2.1", + " - 2.2.2", + "", + ].join("\n"), + ); + }); + + test("binary-tree > indent + show-attributes + link_kind + edge_sort_id", () => { + const graph = create_graph(); + graph.build_graph(getNodes(), getEdges(), []); + + const list_index = ListIndex.build(graph, "index.md", undefined, { + indent: ".", + fields: ["down"], + link_kind: "wiki", + field_group_labels: [], + show_attributes: ["field", "explicit"], + edge_sort_id: { + order: -1, + field: "basename", + }, + show_node_options: { + ext: true, + alias: false, + folder: false, + }, + }); + + expect(list_index).toBe( + [ + "- [[2]] (field=down explicit=true)", + ".- [[2.2]] (field=down explicit=true)", + "..- [[2.2.2]] (field=down explicit=true)", + "..- [[2.2.1]] (field=down explicit=true)", + ".- [[2.1]] (field=down explicit=true)", + "..- [[2.1.2]] (field=down explicit=true)", + "..- [[2.1.1]] (field=down explicit=true)", + "- [[1]] (field=down explicit=true)", + ".- [[1.2]] (field=down explicit=true)", + "..- [[1.2.2]] (field=down explicit=true)", + "..- [[1.2.1]] (field=down explicit=true)", + ".- [[1.1]] (field=down explicit=true)", + "..- [[1.1.2]] (field=down explicit=true)", + "..- [[1.1.1]] (field=down explicit=true)", + "", + ].join("\n"), + ); + }); +}); diff --git a/tests/commands/stats.test.ts b/tests/commands/stats.test.ts index f576ea64..f32180dd 100644 --- a/tests/commands/stats.test.ts +++ b/tests/commands/stats.test.ts @@ -1,9 +1,10 @@ // import { get_graph_stats } from "src/commands/stats"; // import { BCGraph } from "src/graph/MyMultiGraph"; // import { _mock_edge } from "tests/__mocks__/graph"; -// import { describe, expect, test } from "vitest"; +import { describe, expect, test } from "vitest"; -// describe("get_graph_stats", () => { + +describe("get_graph_stats", () => { // test("straight-line", () => { // const graph = new BCGraph({ // edges: [ @@ -58,4 +59,7 @@ // }, // }); // }); -// }); + test("placeholder", () => { + expect(true).toBe(true); + }) +}); diff --git a/tests/utils/mermaid.test.ts b/tests/utils/mermaid.test.ts index ed319efd..e481f27b 100644 --- a/tests/utils/mermaid.test.ts +++ b/tests/utils/mermaid.test.ts @@ -1,6 +1,6 @@ // import { Mermaid } from "src/utils/mermaid"; // import { _mock_edge } from "tests/__mocks__/graph"; -// import { describe, test } from "vitest"; +import { describe, expect, test } from "vitest"; // describe("from_edges", () => { // const edges = [ @@ -78,3 +78,9 @@ // }); // // TODO: I need to test more cases here + +describe("foo", () => { + test("placeholder", () => { + expect(true).toBe(true); + }); +}); \ No newline at end of file diff --git a/vite.config.mjs b/vite.config.mjs new file mode 100644 index 00000000..3cd803e7 --- /dev/null +++ b/vite.config.mjs @@ -0,0 +1,10 @@ +/// +import { defineConfig } from 'vite'; +import wasm from "vite-plugin-wasm"; + +export default defineConfig({ + plugins: [wasm()], + test: { + + } +}); \ No newline at end of file diff --git a/wasmPlugin.mjs b/wasmPlugin.mjs new file mode 100644 index 00000000..73b0e0ee --- /dev/null +++ b/wasmPlugin.mjs @@ -0,0 +1,27 @@ +import path from 'node:path'; +import fs from 'node:fs'; + +export const wasmPluginEsbuild = { + name: 'wasm', + setup(build) { + // Resolve ".wasm" files to a path with a namespace + build.onResolve({ filter: /\.wasm$/ }, args => { + if (args.resolveDir === '') { + return; // Ignore unresolvable paths + } + return { + path: path.isAbsolute(args.path) ? args.path : path.join(args.resolveDir, args.path), + namespace: 'wasm-binary', + }; + }); + + // Virtual modules in the "wasm-binary" namespace contain the + // actual bytes of the WebAssembly file. This uses esbuild's + // built-in "binary" loader instead of manually embedding the + // binary data inside JavaScript code ourselves. + build.onLoad({ filter: /.*/, namespace: 'wasm-binary' }, async args => ({ + contents: await fs.promises.readFile(args.path), + loader: 'binary', + })); + }, +}; \ No newline at end of file From 0da89b4c19b343ac3724e9b2700292c228406d94 Mon Sep 17 00:00:00 2001 From: Moritz Jung Date: Sun, 12 Jan 2025 22:27:50 +0100 Subject: [PATCH 62/65] add back mermaid testing --- tests/utils/mermaid.test.ts | 86 -------------------- wasm/Cargo.toml | 2 + wasm/src/edge_sorting.rs | 9 +++ wasm/src/mermaid.rs | 99 ++++++++++++++--------- wasm/tests/mermaid.rs | 154 ++++++++++++++++++++++++++++++++++++ 5 files changed, 228 insertions(+), 122 deletions(-) delete mode 100644 tests/utils/mermaid.test.ts create mode 100644 wasm/tests/mermaid.rs diff --git a/tests/utils/mermaid.test.ts b/tests/utils/mermaid.test.ts deleted file mode 100644 index e481f27b..00000000 --- a/tests/utils/mermaid.test.ts +++ /dev/null @@ -1,86 +0,0 @@ -// import { Mermaid } from "src/utils/mermaid"; -// import { _mock_edge } from "tests/__mocks__/graph"; -import { describe, expect, test } from "vitest"; - -// describe("from_edges", () => { -// const edges = [ -// _mock_edge("a.md", "b.md", { explicit: true, field: "up" }), -// _mock_edge("b.md", "c.md", { explicit: false, field: "down" }), -// ]; - -// test("!config", (t) => { -// t.expect(Mermaid.from_edges(edges).trim()).toBe( -// ` -// %%{ init: { "flowchart": {} } }%% -// flowchart LR -// \t0("a.md") -// \t1("b.md") -// \t2("c.md") - -// \t0 --> 1 -// \t1 -.-> 2`.trimStart(), -// ); -// }); - -// test("config.kind,direction,renderer", (t) => { -// t.expect( -// Mermaid.from_edges(edges, { -// kind: "graph", -// direction: "TB", -// renderer: "elk", -// }).trim(), -// ).toBe( -// ` -// %%{ init: { "flowchart": {"defaultRenderer":"elk"} } }%% -// graph TB -// \t0("a.md") -// \t1("b.md") -// \t2("c.md") - -// \t0 --> 1 -// \t1 -.-> 2`.trimStart(), -// ); -// }); - -// test("config.show_attributes", (t) => { -// t.expect( -// Mermaid.from_edges(edges, { show_attributes: ["field"] }).trim(), -// ).toBe( -// ` -// %%{ init: { "flowchart": {} } }%% -// flowchart LR -// \t0("a.md") -// \t1("b.md") -// \t2("c.md") - -// \t0 -->|"up"| 1 -// \t1 -.->|"down"| 2`.trimStart(), -// ); -// }); - -// test("config.click.class", (t) => { -// t.expect( -// Mermaid.from_edges(edges, { click: { method: "class" } }).trim(), -// ).toBe( -// ` -// %%{ init: { "flowchart": {} } }%% -// flowchart LR -// \t0("a.md") -// \t1("b.md") -// \t2("c.md") - -// \t0 --> 1 -// \t1 -.-> 2 - -// \tclass 0,1,2 internal-link`.trimStart(), -// ); -// }); -// }); - -// // TODO: I need to test more cases here - -describe("foo", () => { - test("placeholder", () => { - expect(true).toBe(true); - }); -}); \ No newline at end of file diff --git a/wasm/Cargo.toml b/wasm/Cargo.toml index 09957141..9f61a5b6 100644 --- a/wasm/Cargo.toml +++ b/wasm/Cargo.toml @@ -29,6 +29,8 @@ smallvec = "1.13.2" vec-collections = "0.4.3" enum_dispatch = "0.3.13" hashbrown = "0.15.2" +indoc = "2.0.5" +indexmap = "2.7.0" [dev-dependencies] wasm-bindgen-test = "0.3.34" diff --git a/wasm/src/edge_sorting.rs b/wasm/src/edge_sorting.rs index ac2001fa..d06bf3f5 100644 --- a/wasm/src/edge_sorting.rs +++ b/wasm/src/edge_sorting.rs @@ -150,6 +150,15 @@ impl EdgeSorter { } } +impl Default for EdgeSorter { + fn default() -> Self { + EdgeSorter { + field: SortField::Path, + reverse: false, + } + } +} + #[enum_dispatch] pub trait EdgeComparer { fn compare(&self, graph: &NoteGraph, a: &EdgeStruct, b: &EdgeStruct) -> std::cmp::Ordering; diff --git a/wasm/src/mermaid.rs b/wasm/src/mermaid.rs index 81dbead4..b586d516 100644 --- a/wasm/src/mermaid.rs +++ b/wasm/src/mermaid.rs @@ -1,5 +1,4 @@ -use std::collections::HashMap; - +use indexmap::IndexMap; use itertools::{EitherOrBoth, Itertools}; use petgraph::stable_graph::NodeIndex; use wasm_bindgen::prelude::*; @@ -13,7 +12,7 @@ use crate::{ utils::{NoteGraphError, Result}, }; -type AccumulatedEdgeMap<'a> = HashMap< +type AccumulatedEdgeMap<'a> = IndexMap< (NodeIndex, NodeIndex), ( NodeIndex, @@ -21,6 +20,7 @@ type AccumulatedEdgeMap<'a> = HashMap< Vec<&'a EdgeData>, Vec<&'a EdgeData>, ), + hashbrown::DefaultHashBuilder, >; #[derive(Default)] @@ -31,15 +31,24 @@ pub struct AccumulatedEdgeHashMap<'a> { #[wasm_bindgen] #[derive(Clone, Debug)] pub struct MermaidGraphOptions { - active_node: Option, - init_line: String, - chart_type: String, - direction: String, - collapse_opposing_edges: bool, - edge_label_attributes: Vec, - edge_sorter: Option, - node_label_fn: Option, - link_nodes: bool, + #[wasm_bindgen(skip)] + pub active_node: Option, + #[wasm_bindgen(skip)] + pub init_line: String, + #[wasm_bindgen(skip)] + pub chart_type: String, + #[wasm_bindgen(skip)] + pub direction: String, + #[wasm_bindgen(skip)] + pub collapse_opposing_edges: bool, + #[wasm_bindgen(skip)] + pub edge_label_attributes: Vec, + #[wasm_bindgen(skip)] + pub edge_sorter: Option, + #[wasm_bindgen(skip)] + pub node_label_fn: Option, + #[wasm_bindgen(skip)] + pub link_nodes: bool, } #[wasm_bindgen] @@ -75,6 +84,22 @@ impl MermaidGraphOptions { } } +impl Default for MermaidGraphOptions { + fn default() -> Self { + MermaidGraphOptions { + active_node: None, + init_line: "%%{ init: { \"flowchart\": {} } }%%".to_string(), + chart_type: "graph".to_string(), + direction: "LR".to_string(), + collapse_opposing_edges: true, + edge_label_attributes: vec!["field".to_string()], + edge_sorter: Some(EdgeSorter::default()), + node_label_fn: None, + link_nodes: false, + } + } +} + #[wasm_bindgen] #[derive(Clone, Debug)] pub struct MermaidGraphData { @@ -139,7 +164,11 @@ impl NoteGraph { ); // accumulate edges by direction, so that we can collapse them in the next step - let accumulated_edges = NoteGraph::int_accumulate_edges(self, edge_structs)?; + let accumulated_edges = NoteGraph::int_accumulate_edges( + self, + edge_structs, + diagram_options.collapse_opposing_edges, + )?; // utils::log(format!("{:#?}", accumulated_edges)); @@ -299,6 +328,7 @@ impl NoteGraph { pub fn int_accumulate_edges( graph: &NoteGraph, edges: Vec, + collapse_opposing_edges: bool, ) -> Result> { let mut accumulated_edges = AccumulatedEdgeHashMap::default(); @@ -311,32 +341,29 @@ impl NoteGraph { let forward_dir = (edge_struct.source_index, edge_struct.target_index); let entry1 = accumulated_edges.map.get_mut(&forward_dir); - match entry1 { - Some((_, _, forward, _)) => { - forward.push(edge_struct.edge_data_ref(graph).unwrap()); - } - None => { - let backward_dir = (edge_struct.target_index, edge_struct.source_index); + if let Some((_, _, forward, _)) = entry1 { + forward.push(edge_struct.edge_data_ref(graph).unwrap()); + continue; + } - let entry2 = accumulated_edges.map.get_mut(&backward_dir); - match entry2 { - Some((_, _, _, backward)) => { - backward.push(edge_struct.edge_data_ref(graph).unwrap()); - } - None => { - accumulated_edges.map.insert( - forward_dir, - ( - edge_struct.source_index, - edge_struct.target_index, - vec![edge_struct.edge_data_ref(graph).unwrap()], - Vec::new(), - ), - ); - } - } + if collapse_opposing_edges { + let backward_dir = (edge_struct.target_index, edge_struct.source_index); + + if let Some((_, _, _, backward)) = accumulated_edges.map.get_mut(&backward_dir) { + backward.push(edge_struct.edge_data_ref(graph).unwrap()); + continue; } } + + accumulated_edges.map.insert( + forward_dir, + ( + edge_struct.source_index, + edge_struct.target_index, + vec![edge_struct.edge_data_ref(graph).unwrap()], + Vec::new(), + ), + ); } Ok(accumulated_edges) diff --git a/wasm/tests/mermaid.rs b/wasm/tests/mermaid.rs new file mode 100644 index 00000000..2362b860 --- /dev/null +++ b/wasm/tests/mermaid.rs @@ -0,0 +1,154 @@ +extern crate wasm_bindgen_test; + +use breadcrumbs_graph_wasm::{ + data::{ + construction::{GCEdgeData, GCNodeData}, + rules::TransitiveGraphRule, + }, + graph::NoteGraph, + mermaid::MermaidGraphOptions, + traversal::options::TraversalOptions, +}; +use indoc::indoc; +use wasm_bindgen_test::*; + +fn get_test_graph() -> NoteGraph { + // a --up--> b --down--> c + // ^_______same________^ + + let nodes = vec![ + GCNodeData::new("a.md".to_string(), vec![], true, false, false), + GCNodeData::new("b.md".to_string(), vec![], true, false, false), + GCNodeData::new("c.md".to_string(), vec![], true, false, false), + ]; + + let edges = vec![ + GCEdgeData::new( + "a.md".to_string(), + "b.md".to_string(), + "up".to_string(), + "".to_string(), + ), + GCEdgeData::new( + "b.md".to_string(), + "c.md".to_string(), + "down".to_string(), + "".to_string(), + ), + ]; + + let rules = vec![ + TransitiveGraphRule::new( + "".to_string(), + vec!["up".to_string(), "down".to_string()], + "same".to_string(), + 5, + false, + false, + ), + TransitiveGraphRule::new( + "".to_string(), + vec!["same".to_string()], + "same".to_string(), + 5, + false, + true, + ), + ]; + + let mut graph = NoteGraph::new(); + graph.build_graph(nodes, edges, rules).unwrap(); + graph +} + +fn get_traversal_options() -> TraversalOptions { + TraversalOptions::new(vec!["a.md".to_string()], None, 5, false) +} + +#[wasm_bindgen_test] +fn test_default_config() { + let graph = get_test_graph(); + + let mermaid = graph + .generate_mermaid_graph(get_traversal_options(), MermaidGraphOptions::default()) + .unwrap(); + + assert_eq!( + mermaid.mermaid.trim(), + indoc! { + r#" + %%{ init: { "flowchart": {} } }%% + graph LR + 0("a.md") + 2("c.md") + 1("b.md") + 2 -.-|"same"| 0 + 0 -->|"up"| 1 + 1 -->|"down"| 2 + "# + } + .trim() + ); +} + +#[wasm_bindgen_test] +fn test_active_node() { + let graph = get_test_graph(); + + let mut options = MermaidGraphOptions::default(); + + options.active_node = Some("a.md".to_string()); + + let mermaid = graph + .generate_mermaid_graph(get_traversal_options(), options) + .unwrap(); + + assert_eq!( + mermaid.mermaid.trim(), + indoc! { + r#" + %%{ init: { "flowchart": {} } }%% + graph LR + 0("a.md") + 2("c.md") + 1("b.md") + 2 -.-|"same"| 0 + 0 -->|"up"| 1 + 1 -->|"down"| 2 + class 0 BC-active-node + "# + } + .trim() + ); +} + +#[wasm_bindgen_test] +fn test_collapse_edges() { + let graph = get_test_graph(); + + let mut options = MermaidGraphOptions::default(); + + options.collapse_opposing_edges = false; + + let mermaid = graph + .generate_mermaid_graph(get_traversal_options(), options) + .unwrap(); + + assert_eq!( + mermaid.mermaid.trim(), + indoc! { + r#" + %%{ init: { "flowchart": {} } }%% + graph LR + 0("a.md") + 2("c.md") + 1("b.md") + 2 -.->|"same"| 0 + 0 -->|"up"| 1 + 0 -.->|"same"| 2 + 1 -->|"down"| 2 + "# + } + .trim() + ); +} From 803628197a922bf2a341249e171cd9920b6980a4 Mon Sep 17 00:00:00 2001 From: Moritz Jung Date: Sun, 12 Jan 2025 22:57:12 +0100 Subject: [PATCH 63/65] fix build --- esbuild.config.mjs | 2 +- src/modals/CreateListIndexModal.ts | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/esbuild.config.mjs b/esbuild.config.mjs index 0f4994e6..c3c25d3a 100644 --- a/esbuild.config.mjs +++ b/esbuild.config.mjs @@ -3,7 +3,7 @@ import esbuild from "esbuild"; import esbuildSvelte from "esbuild-svelte"; import process from "process"; import { sveltePreprocess } from "svelte-preprocess"; -import { wasmPluginEsbuild } from "./wasmPlugin"; +import { wasmPluginEsbuild } from "./wasmPlugin.mjs"; const banner = `/* THIS IS A GENERATED/BUNDLED FILE BY ESBUILD diff --git a/src/modals/CreateListIndexModal.ts b/src/modals/CreateListIndexModal.ts index dfd5c914..1eda6b41 100644 --- a/src/modals/CreateListIndexModal.ts +++ b/src/modals/CreateListIndexModal.ts @@ -115,8 +115,9 @@ export class CreateListIndexModal extends Modal { log.debug("build_list_index options", this.options); const list_index = ListIndex.build( - plugin, + plugin.graph, this.active_file!.path, + plugin.settings, this.options, ); From a23b8e4d415981379254449419927763c6f6396a Mon Sep 17 00:00:00 2001 From: Moritz Jung Date: Mon, 13 Jan 2025 10:57:47 +0100 Subject: [PATCH 64/65] update to wasm-bindgen 0.2.100 --- .gitignore | 3 + esbuild.config.mjs | 9 +- package-lock.json | 101 ++++++++++++++++ package.json | 4 +- src/commands/jump/index.ts | 4 +- src/commands/list_index/index.ts | 21 +++- src/components/NestedEdgeList.svelte | 15 ++- .../button/CopyToClipboardButton.svelte | 2 +- .../codeblocks/CodeblockMarkmap.svelte | 48 ++++++-- .../codeblocks/CodeblockMermaid.svelte | 2 +- .../codeblocks/CodeblockTree.svelte | 20 +++- .../obsidian/RenderExternalCodeblock.svelte | 7 +- src/components/page_views/PrevNextView.svelte | 2 +- .../page_views/TrailViewGrid.svelte | 2 +- .../ShowAttributesSelectorMenu.svelte | 2 +- .../side_views/MatrixEdgeField.svelte | 2 +- src/graph/utils.ts | 9 +- src/main.ts | 2 +- src/utils/wasm_types.d.ts | 16 +-- tests/commands/list_index.test.ts | 19 +-- tests/commands/stats.test.ts | 111 +++++++++--------- vite.config.mjs | 10 +- wasm/Cargo.toml | 6 +- wasm/src/graph.rs | 64 +++++----- wasm/src/lib.rs | 2 +- wasm/src/update/graph.rs | 10 +- wasm/src/utils.rs | 8 +- 27 files changed, 331 insertions(+), 170 deletions(-) diff --git a/.gitignore b/.gitignore index c48d4e8d..167dbee5 100644 --- a/.gitignore +++ b/.gitignore @@ -24,3 +24,6 @@ data.json # test files coverage + +# build +meta.txt \ No newline at end of file diff --git a/esbuild.config.mjs b/esbuild.config.mjs index c3c25d3a..c7c8b3d5 100644 --- a/esbuild.config.mjs +++ b/esbuild.config.mjs @@ -4,6 +4,7 @@ import esbuildSvelte from "esbuild-svelte"; import process from "process"; import { sveltePreprocess } from "svelte-preprocess"; import { wasmPluginEsbuild } from "./wasmPlugin.mjs"; +import fs from "node:fs/promises"; const banner = `/* THIS IS A GENERATED/BUNDLED FILE BY ESBUILD @@ -40,13 +41,14 @@ const context = await esbuild.context({ logLevel: "info", sourcemap: prod ? false : "inline", minify: prod, + metafile: true, treeShaking: true, outfile: "main.js", - conditions: ['browser', prod ? "production" : "development"], + conditions: ["browser", prod ? "production" : "development"], plugins: [ esbuildSvelte({ cache: false, - compilerOptions: { css: 'injected', dev: !prod }, + compilerOptions: { css: "injected", dev: !prod }, preprocess: sveltePreprocess(), }), wasmPluginEsbuild, @@ -54,7 +56,8 @@ const context = await esbuild.context({ }); if (prod) { - await context.rebuild(); + const result = await context.rebuild(); + await fs.writeFile("meta.txt", JSON.stringify(result.metafile, null, "\t")); process.exit(0); } else { await context.watch(); diff --git a/package-lock.json b/package-lock.json index 2246edac..c6e9135a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -34,6 +34,7 @@ "prettier-plugin-svelte": "^3.3.2", "prettier-plugin-tailwindcss": "^0.5.11", "svelte": "^5.16.2", + "svelte-check": "^4.1.3", "svelte-preprocess": "^6.0.3", "tailwindcss": "^3.4.1", "tslib": "2.6.2", @@ -3995,6 +3996,15 @@ "node": "*" } }, + "node_modules/mri": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz", + "integrity": "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, "node_modules/mrmime": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.0.tgz", @@ -5182,6 +5192,18 @@ "queue-microtask": "^1.2.2" } }, + "node_modules/sade": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/sade/-/sade-1.8.1.tgz", + "integrity": "sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==", + "dev": true, + "dependencies": { + "mri": "^1.1.0" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/safe-array-concat": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.3.tgz", @@ -5794,6 +5816,85 @@ "node": ">=18" } }, + "node_modules/svelte-check": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/svelte-check/-/svelte-check-4.1.3.tgz", + "integrity": "sha512-IEMoQDH+TrPKwKeIyJim+PU8FxnzQMXsFHR/ldErkHpPXEGHCujHUXiR8jg6qDMqzsif5BbDOUFORltu87ex7g==", + "dev": true, + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.25", + "chokidar": "^4.0.1", + "fdir": "^6.2.0", + "picocolors": "^1.0.0", + "sade": "^1.7.4" + }, + "bin": { + "svelte-check": "bin/svelte-check" + }, + "engines": { + "node": ">= 18.0.0" + }, + "peerDependencies": { + "svelte": "^4.0.0 || ^5.0.0-next.0", + "typescript": ">=5.0.0" + } + }, + "node_modules/svelte-check/node_modules/chokidar": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "dev": true, + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/svelte-check/node_modules/fdir": { + "version": "6.4.2", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.2.tgz", + "integrity": "sha512-KnhMXsKSPZlAhp7+IjUkRZKPb4fUyccpDrdFXbi4QL1qkmFh9kVY09Yox+n4MaOb3lHZ1Tv829C3oaaXoMYPDQ==", + "dev": true, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/svelte-check/node_modules/picomatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", + "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "dev": true, + "optional": true, + "peer": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/svelte-check/node_modules/readdirp": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.1.tgz", + "integrity": "sha512-h80JrZu/MHUZCyHu5ciuoI0+WxsCxzxJTILn6Fs8rxSnFPh+UVHYfeIxK1nVGugMqkfC4vJcBOYbkfkwYK0+gw==", + "dev": true, + "engines": { + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, "node_modules/svelte-preprocess": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/svelte-preprocess/-/svelte-preprocess-6.0.3.tgz", diff --git a/package.json b/package.json index e5edf415..14d850cb 100644 --- a/package.json +++ b/package.json @@ -9,13 +9,14 @@ "watch:esbuild": "node esbuild.config.mjs", "dev": "npm-run-all --parallel watch:*", "build:css": "npx tailwindcss -i ./src/styles.css -o ./styles.css --minify", - "build:esbuild": "tsc -noEmit -skipLibCheck && node esbuild.config.mjs production", + "build:esbuild": "tsc -noEmit -skipLibCheck && svelte-check && node esbuild.config.mjs production", "build": "npm run build:css && npm run build:esbuild", "version:prod": "node version-bump.mjs && git add manifest.json versions.json package.json", "version:beta": "node version-bump-beta.mjs && git add manifest-beta.json versions.json package.json", "release:beta": "npm run version:beta && git commit -m 'release:beta' && git push origin master:master && git tag -a $npm_package_version -m \"$npm_package_version\" && git push --tags", "test": "vitest --config vite.config.mjs", "coverage:ui": "vitest run --config vite.config.mjs --coverage --coverage.include 'src/**' --coverage.reporter html && npx vite preview --outDir ./coverage --open", + "fmt": "npm run wasm:fmt && npx prettier --write ./src", "wasm:build": "cd wasm && wasm-pack build --target web", "wasm:dev": "cd wasm && wasm-pack build --dev --target web", "wasm:profile": "cd wasm && wasm-pack build --profiling --target web", @@ -46,6 +47,7 @@ "prettier-plugin-svelte": "^3.3.2", "prettier-plugin-tailwindcss": "^0.5.11", "svelte": "^5.16.2", + "svelte-check": "^4.1.3", "svelte-preprocess": "^6.0.3", "tailwindcss": "^3.4.1", "tslib": "2.6.2", diff --git a/src/commands/jump/index.ts b/src/commands/jump/index.ts index deaa6115..c979b95e 100644 --- a/src/commands/jump/index.ts +++ b/src/commands/jump/index.ts @@ -13,9 +13,7 @@ export const jump_to_neighbour = async ( const matches = plugin.graph .get_filtered_outgoing_edges(active_file.path, options.fields) .get_edges() - .filter( - (e) => e.target_path(plugin.graph) !== active_file.path, - ); + .filter((e) => e.target_path(plugin.graph) !== active_file.path); if (!matches.length) { new Notice( diff --git a/src/commands/list_index/index.ts b/src/commands/list_index/index.ts index 4d5d45ac..c76cec39 100644 --- a/src/commands/list_index/index.ts +++ b/src/commands/list_index/index.ts @@ -1,6 +1,9 @@ import type { EdgeSortId } from "src/const/graph"; import type { LinkKind } from "src/interfaces/links"; -import type { BreadcrumbsSettings, ShowNodeOptions } from "src/interfaces/settings"; +import type { + BreadcrumbsSettings, + ShowNodeOptions, +} from "src/interfaces/settings"; import { Links } from "src/utils/links"; import { toNodeStringifyOptions, type EdgeAttribute } from "src/graph/utils"; import { @@ -89,7 +92,10 @@ export namespace ListIndex { const display = edge.stringify_target( graph, - toNodeStringifyOptions(plugin_settings, options.show_node_options), + toNodeStringifyOptions( + plugin_settings, + options.show_node_options, + ), ); const link = Links.ify(edge.target_path(graph), display, { @@ -101,7 +107,9 @@ export namespace ListIndex { options.show_attributes, ); - index += real_indent.repeat(depth - 1) + (attr ? `- ${link} (${attr})\n` : `- ${link}\n`); + index += + real_indent.repeat(depth - 1) + + (attr ? `- ${link} (${attr})\n` : `- ${link}\n`); const new_children = Array.from(children).map( (child_id) => all_traversal_data[child_id], @@ -145,6 +153,11 @@ export namespace ListIndex { postprocess_options, ); - return edge_tree_to_list_index(graph, traversal_result, plugin_settings, options); + return edge_tree_to_list_index( + graph, + traversal_result, + plugin_settings, + options, + ); }; } diff --git a/src/components/NestedEdgeList.svelte b/src/components/NestedEdgeList.svelte index 7b9ebf9b..a5d8a24f 100644 --- a/src/components/NestedEdgeList.svelte +++ b/src/components/NestedEdgeList.svelte @@ -52,7 +52,12 @@ {#each items as item, i} {@const children = data.children_at_index(item)} - {@const render_data = data.rendering_obj_at_index(item, plugin.graph, node_stringify_options, show_attributes ?? []) as EdgeRenderingData} + {@const render_data = data.rendering_obj_at_index( + item, + plugin.graph, + node_stringify_options, + show_attributes ?? [], + ) as EdgeRenderingData} {#if children && render_data}
@@ -69,7 +74,7 @@ {node_stringify_options} cls="tree-item-inner-text" /> --> - + {#if show_attributes?.length} - + {/if} @@ -112,6 +115,6 @@
{/if} -
+ {/if} {/each} diff --git a/src/components/button/CopyToClipboardButton.svelte b/src/components/button/CopyToClipboardButton.svelte index ba866f17..2873657e 100644 --- a/src/components/button/CopyToClipboardButton.svelte +++ b/src/components/button/CopyToClipboardButton.svelte @@ -26,7 +26,7 @@ onclick={() => { copied = true; - copy_to_clipboard(typeof text === 'string' ? text : text(), options); + copy_to_clipboard(typeof text === "string" ? text : text(), options); setTimeout(() => (copied = false), 2_500); }} diff --git a/src/components/codeblocks/CodeblockMarkmap.svelte b/src/components/codeblocks/CodeblockMarkmap.svelte index 47d64571..1d914903 100644 --- a/src/components/codeblocks/CodeblockMarkmap.svelte +++ b/src/components/codeblocks/CodeblockMarkmap.svelte @@ -2,7 +2,7 @@ import type { ICodeblock } from "src/codeblocks/schema"; import { ListIndex } from "src/commands/list_index"; import { - toNodeStringifyOptions, + toNodeStringifyOptions, type EdgeAttrFilters, } from "src/graph/utils"; import type { BreadcrumbsError } from "src/interfaces/graph"; @@ -12,7 +12,13 @@ import CopyToClipboardButton from "../button/CopyToClipboardButton.svelte"; import RenderExternalCodeblock from "../obsidian/RenderExternalCodeblock.svelte"; import CodeblockErrors from "./CodeblockErrors.svelte"; - import { create_edge_sorter, FlatTraversalResult, NoteGraphError, TraversalOptions, TraversalPostprocessOptions } from "wasm/pkg/breadcrumbs_graph_wasm"; + import { + create_edge_sorter, + FlatTraversalResult, + NoteGraphError, + TraversalOptions, + TraversalPostprocessOptions, + } from "wasm/pkg/breadcrumbs_graph_wasm"; import { log } from "src/logger"; import { Links } from "src/utils/links"; @@ -87,19 +93,37 @@ let code = $derived.by(() => { if (data) { - const stringify_options = toNodeStringifyOptions(plugin, show_node_options); + const stringify_options = toNodeStringifyOptions( + plugin.settings, + show_node_options, + ); const node_data = plugin.graph.get_node(file_path)!; - const link = Links.ify(file_path, stringify_options.stringify_node(node_data), { - link_kind: plugin.settings.commands.list_index.default_options.link_kind, - }); + const link = Links.ify( + file_path, + stringify_options.stringify_node(node_data), + { + link_kind: + plugin.settings.commands.list_index.default_options + .link_kind, + }, + ); - return "# " + link + "\n" + ListIndex.edge_tree_to_list_index(plugin, data, { - ...plugin.settings.commands.list_index.default_options, - show_node_options, - show_attributes: options["show-attributes"] ?? [], - show_entry_nodes: false, - }); + return ( + "# " + + link + + "\n" + + ListIndex.edge_tree_to_list_index( + plugin.graph, + data, + plugin.settings, + { + ...plugin.settings.commands.list_index.default_options, + show_node_options, + show_attributes: options["show-attributes"] ?? [], + }, + ) + ); } else { return ""; } diff --git a/src/components/codeblocks/CodeblockMermaid.svelte b/src/components/codeblocks/CodeblockMermaid.svelte index 5b03f3c8..921d52fc 100644 --- a/src/components/codeblocks/CodeblockMermaid.svelte +++ b/src/components/codeblocks/CodeblockMermaid.svelte @@ -118,7 +118,7 @@ } code = code; - }; + } onMount(() => { update(); diff --git a/src/components/codeblocks/CodeblockTree.svelte b/src/components/codeblocks/CodeblockTree.svelte index 2851e53a..d8ef8598 100644 --- a/src/components/codeblocks/CodeblockTree.svelte +++ b/src/components/codeblocks/CodeblockTree.svelte @@ -85,7 +85,7 @@ "An error occurred while updating the codeblock tree. Check the console for more information (Ctrl + Shift + I)."; } } - }; + } onMount(() => { const timer = new Timer(); @@ -110,10 +110,18 @@
ListIndex.edge_tree_to_list_index(plugin, data, { - ...plugin.settings.commands.list_index.default_options, - show_attributes: options["show-attributes"] ?? [], - })} + text={() => + ListIndex.edge_tree_to_list_index( + plugin.graph, + data, + plugin.settings, + { + ...plugin.settings.commands.list_index + .default_options, + show_attributes: + options["show-attributes"] ?? [], + }, + )} />
@@ -122,7 +130,7 @@ diff --git a/src/components/selector/ShowAttributesSelectorMenu.svelte b/src/components/selector/ShowAttributesSelectorMenu.svelte index 425e5b51..13df264c 100644 --- a/src/components/selector/ShowAttributesSelectorMenu.svelte +++ b/src/components/selector/ShowAttributesSelectorMenu.svelte @@ -1,7 +1,7 @@ diff --git a/src/graph/utils.ts b/src/graph/utils.ts index 45c69bda..4410e117 100644 --- a/src/graph/utils.ts +++ b/src/graph/utils.ts @@ -1,12 +1,17 @@ import type BreadcrumbsPlugin from "src/main"; -import type { BreadcrumbsSettings, ShowNodeOptions } from "src/interfaces/settings"; +import type { + BreadcrumbsSettings, + ShowNodeOptions, +} from "src/interfaces/settings"; import { NodeStringifyOptions } from "wasm/pkg/breadcrumbs_graph_wasm"; export function toNodeStringifyOptions( settings: BreadcrumbsSettings | undefined, options: ShowNodeOptions, ): NodeStringifyOptions { - const dendron_note = settings?.explicit_edge_sources.dendron_note ?? { enabled: false }; + const dendron_note = settings?.explicit_edge_sources?.dendron_note ?? { + enabled: false, + }; return new NodeStringifyOptions( options.ext, diff --git a/src/main.ts b/src/main.ts index 550b9f9d..ed137009 100644 --- a/src/main.ts +++ b/src/main.ts @@ -66,7 +66,7 @@ export default class BreadcrumbsPlugin extends Plugin { }); // Init wasm - await init(wasmbin); + await init({ module_or_path: wasmbin }); this.graph = create_graph(); this.graph.set_update_callback(() => { // funny micro task so that the rust update function finishes before we access the graph again for the visualization diff --git a/src/utils/wasm_types.d.ts b/src/utils/wasm_types.d.ts index d722b2a0..33bb3602 100644 --- a/src/utils/wasm_types.d.ts +++ b/src/utils/wasm_types.d.ts @@ -1,9 +1,9 @@ interface EdgeRenderingData { - link_display: string; - link_path: string; - target_resolved: boolean; - explicit: boolean; - edge_source: string; - attribute_label: string; - has_cut_of_children: boolean; -} \ No newline at end of file + link_display: string; + link_path: string; + target_resolved: boolean; + explicit: boolean; + edge_source: string; + attribute_label: string; + has_cut_of_children: boolean; +} diff --git a/tests/commands/list_index.test.ts b/tests/commands/list_index.test.ts index aa0d02be..c0641636 100644 --- a/tests/commands/list_index.test.ts +++ b/tests/commands/list_index.test.ts @@ -1,6 +1,10 @@ import { ListIndex } from "src/commands/list_index"; import { describe, expect, test, beforeEach } from "vitest"; -import init, { create_graph, GCEdgeData, GCNodeData } from "wasm/pkg/breadcrumbs_graph_wasm"; +import init, { + create_graph, + GCEdgeData, + GCNodeData, +} from "wasm/pkg/breadcrumbs_graph_wasm"; import fs from "node:fs/promises"; function getEdges() { @@ -39,20 +43,21 @@ function getNodes() { new GCNodeData("2.2.md", [], true, false, false), new GCNodeData("2.2.1.md", [], true, false, false), new GCNodeData("2.2.2.md", [], true, false, false), - ] + ]; } - beforeEach(async () => { - const wasmSource = await fs.readFile("wasm/pkg/breadcrumbs_graph_wasm_bg.wasm"); - const wasmModule = await WebAssembly.compile(wasmSource); + const wasmSource = await fs.readFile( + "wasm/pkg/breadcrumbs_graph_wasm_bg.wasm", + ); + const wasmModule = await WebAssembly.compile(wasmSource); await init(wasmModule); }); describe("build", () => { test("binary-tree > defaults", () => { const graph = create_graph(); - graph.build_graph(getNodes(), getEdges(), []); + graph.build_graph(getNodes(), getEdges(), []); const list_index = ListIndex.build(graph, "index.md", undefined, { indent: " ", @@ -94,7 +99,7 @@ describe("build", () => { test("binary-tree > indent + show-attributes + link_kind + edge_sort_id", () => { const graph = create_graph(); - graph.build_graph(getNodes(), getEdges(), []); + graph.build_graph(getNodes(), getEdges(), []); const list_index = ListIndex.build(graph, "index.md", undefined, { indent: ".", diff --git a/tests/commands/stats.test.ts b/tests/commands/stats.test.ts index f32180dd..3f9ad3f7 100644 --- a/tests/commands/stats.test.ts +++ b/tests/commands/stats.test.ts @@ -3,63 +3,62 @@ // import { _mock_edge } from "tests/__mocks__/graph"; import { describe, expect, test } from "vitest"; - describe("get_graph_stats", () => { -// test("straight-line", () => { -// const graph = new BCGraph({ -// edges: [ -// _mock_edge("a", "b", {}), -// _mock_edge("b", "c", { -// explicit: true, -// field: "parent", -// source: "dataview_note", -// }), -// _mock_edge("c", "d", { -// explicit: false, -// field: "right", -// implied_kind: "transitive:cousin_is_sibling", -// round: 1, -// }), -// ], -// }); + // test("straight-line", () => { + // const graph = new BCGraph({ + // edges: [ + // _mock_edge("a", "b", {}), + // _mock_edge("b", "c", { + // explicit: true, + // field: "parent", + // source: "dataview_note", + // }), + // _mock_edge("c", "d", { + // explicit: false, + // field: "right", + // implied_kind: "transitive:cousin_is_sibling", + // round: 1, + // }), + // ], + // }); -// const stats = get_graph_stats(graph, { -// groups: [ -// { label: "ups", fields: ["parent"] }, -// { label: "rights", fields: ["right"] }, -// ], -// }); + // const stats = get_graph_stats(graph, { + // groups: [ + // { label: "ups", fields: ["parent"] }, + // { label: "rights", fields: ["right"] }, + // ], + // }); -// expect(stats).toStrictEqual({ -// nodes: { resolved: { true: 4 } }, -// edges: { -// field: { -// down: 1, -// parent: 1, -// right: 1, -// }, -// group: { -// ups: 1, -// rights: 1, -// }, -// source: { -// typed_link: 1, -// dataview_note: 1, -// }, -// explicit: { -// true: 2, -// false: 1, -// }, -// implied_kind: { -// "transitive:cousin_is_sibling": 1, -// }, -// round: { -// "1": 1, -// }, -// }, -// }); -// }); - test("placeholder", () => { - expect(true).toBe(true); - }) + // expect(stats).toStrictEqual({ + // nodes: { resolved: { true: 4 } }, + // edges: { + // field: { + // down: 1, + // parent: 1, + // right: 1, + // }, + // group: { + // ups: 1, + // rights: 1, + // }, + // source: { + // typed_link: 1, + // dataview_note: 1, + // }, + // explicit: { + // true: 2, + // false: 1, + // }, + // implied_kind: { + // "transitive:cousin_is_sibling": 1, + // }, + // round: { + // "1": 1, + // }, + // }, + // }); + // }); + test("placeholder", () => { + expect(true).toBe(true); + }); }); diff --git a/vite.config.mjs b/vite.config.mjs index 3cd803e7..dcda845f 100644 --- a/vite.config.mjs +++ b/vite.config.mjs @@ -1,10 +1,8 @@ /// -import { defineConfig } from 'vite'; +import { defineConfig } from "vite"; import wasm from "vite-plugin-wasm"; export default defineConfig({ - plugins: [wasm()], - test: { - - } -}); \ No newline at end of file + plugins: [wasm()], + test: {}, +}); diff --git a/wasm/Cargo.toml b/wasm/Cargo.toml index 9f61a5b6..9cd53d54 100644 --- a/wasm/Cargo.toml +++ b/wasm/Cargo.toml @@ -12,7 +12,7 @@ default = ["console_error_panic_hook"] test = [] [dependencies] -wasm-bindgen = "=0.2.92" +wasm-bindgen = "=0.2.100" # The `console_error_panic_hook` crate provides better debugging of panics by # logging them with `console.error`. This is great for development, but requires @@ -21,7 +21,7 @@ wasm-bindgen = "=0.2.92" console_error_panic_hook = { version = "0.1.7", optional = true } petgraph = "0.6.4" web-time = "1.1.0" -js-sys = "0.3.69" +js-sys = "0.3.77" itertools = "0.12.1" serde = { version = "1.0", features = ["derive"] } serde-wasm-bindgen = "0.6.5" @@ -33,7 +33,7 @@ indoc = "2.0.5" indexmap = "2.7.0" [dev-dependencies] -wasm-bindgen-test = "0.3.34" +wasm-bindgen-test = "0.3.50" [profile.release] opt-level = 3 # Highest level of optimization. diff --git a/wasm/src/graph.rs b/wasm/src/graph.rs index 47806339..19677ad0 100644 --- a/wasm/src/graph.rs +++ b/wasm/src/graph.rs @@ -88,16 +88,16 @@ impl NoteGraph { if let Some(callback) = &self.update_callback { match callback.call0(&JsValue::NULL) { Ok(_) => {} - Err(e) => LOGGER.warn(&format!( - "Error calling update notification function: {:?}", - e - )), - // LOGGER.with(|l| { - // l.warn(&format!( - // "Error calling update notification function: {:?}", - // e - // )) - // }), + // Err(e) => LOGGER.warn(&format!( + // "Error calling update notification function: {:?}", + // e + // )), + Err(e) => LOGGER.with(|l| { + l.warn(&format!( + "Error calling update notification function: {:?}", + e + )) + }), } } } @@ -110,7 +110,7 @@ impl NoteGraph { edges: Vec, transitive_rules: Vec, ) -> Result<()> { - LOGGER.info("Building Graph"); + LOGGER.with(|l| l.info("Building Graph")); self.graph = StableGraph::::default(); self.edge_types = VecSet::empty(); @@ -170,9 +170,10 @@ impl NoteGraph { self.graph.node_references().for_each(|node| { match f.call1(&this, &node.weight().clone().into()) { Ok(_) => {} - Err(e) => LOGGER.warn(&format!("Error calling node iteration callback: {:?}", e)), - // LOGGER.with(|l| l.warn(&format!("Error calling node iteration callback: {:?}", - // e))), + // Err(e) => LOGGER.warn(&format!("Error calling node iteration callback: {:?}", + // e)), + Err(e) => LOGGER + .with(|l| l.warn(&format!("Error calling node iteration callback: {:?}", e))), } }); } @@ -185,9 +186,10 @@ impl NoteGraph { self.graph.edge_references().for_each(|edge| { match f.call1(&this, &edge.weight().clone().into()) { Ok(_) => {} - Err(e) => LOGGER.warn(&format!("Error calling edge iteration callback: {:?}", e)), - // LOGGER.with(|l| l.warn(&format!("Error calling edge iteration callback: {:?}", - // e))), + // Err(e) => LOGGER.warn(&format!("Error calling edge iteration callback: {:?}", + // e)), + Err(e) => LOGGER + .with(|l| l.warn(&format!("Error calling edge iteration callback: {:?}", e))), } }); } @@ -272,8 +274,8 @@ impl NoteGraph { } pub fn log(&self) { - LOGGER.info(&format!("{:#?}", self.graph)); - // LOGGER.with(|l| l.info(&format!("{:#?}", self.graph))); + // LOGGER.info(&format!("{:#?}", self.graph)); + LOGGER.with(|l| l.info(&format!("{:#?}", self.graph))); } } @@ -438,18 +440,18 @@ impl NoteGraph { weight.explicit }); - LOGGER.debug(&format!( - "Removed {} implied edges, {} explicit edges remain", - edge_count - self.graph.edge_count(), - self.graph.edge_count() - )); - // LOGGER.with(|l| { - // l.debug(&format!( - // "Removed {} implied edges, {} explicit edges remain", - // edge_count - self.graph.edge_count(), - // self.graph.edge_count() - // )) - // }); + // LOGGER.debug(&format!( + // "Removed {} implied edges, {} explicit edges remain", + // edge_count - self.graph.edge_count(), + // self.graph.edge_count() + // )); + LOGGER.with(|l| { + l.debug(&format!( + "Removed {} implied edges, {} explicit edges remain", + edge_count - self.graph.edge_count(), + self.graph.edge_count() + )) + }); } /// Removes all unresolved notes with no incoming or outgoing edges. diff --git a/wasm/src/lib.rs b/wasm/src/lib.rs index 06d765c1..e0b7a036 100644 --- a/wasm/src/lib.rs +++ b/wasm/src/lib.rs @@ -12,7 +12,7 @@ use wasm_bindgen::prelude::*; pub fn create_graph() -> graph::NoteGraph { console_error_panic_hook::set_once(); - utils::LOGGER.debug("Hello, from WASM!"); + utils::LOGGER.with(|l| l.debug("Hello, from WASM!")); graph::NoteGraph::new() } diff --git a/wasm/src/update/graph.rs b/wasm/src/update/graph.rs index 991e3ac6..2ebe8cfe 100644 --- a/wasm/src/update/graph.rs +++ b/wasm/src/update/graph.rs @@ -54,10 +54,12 @@ impl UpdateableGraph for NoteGraph { let node_weight = self.int_get_node_weight_mut(index)?; if !node_weight.resolved { - LOGGER.warn(&format!( - "Attempted to remove unresolved node {} from the graph", - node_weight.path - )); + LOGGER.with(|l| { + l.warn(&format!( + "Attempted to remove unresolved node {} from the graph", + node_weight.path + )) + }); } node_weight.resolved = false; diff --git a/wasm/src/utils.rs b/wasm/src/utils.rs index e04ae343..fd77f8d1 100644 --- a/wasm/src/utils.rs +++ b/wasm/src/utils.rs @@ -7,7 +7,7 @@ use web_time::Instant; #[cfg(not(feature = "test"))] #[wasm_bindgen(module = "src/logger/index.ts")] extern "C" { - #[wasm_bindgen(js_name = log)] + #[wasm_bindgen(thread_local_v2, js_name = log)] pub static LOGGER: Logger; pub type Logger; @@ -26,7 +26,7 @@ extern "C" { #[cfg(feature = "test")] #[wasm_bindgen] extern "C" { - #[wasm_bindgen(js_name = console)] + #[wasm_bindgen(thread_local_v2, js_name = console)] pub static LOGGER: Logger; pub type Logger; @@ -78,7 +78,7 @@ impl PerfLogger { pub fn stop(&mut self) { if self.stopped() { - LOGGER.warn(&format!("PerfLogger {} is already stopped", self.name)); + LOGGER.with(|l| l.warn(&format!("PerfLogger {} is already stopped", self.name))); } else { self.elapsed = Some(self.start.elapsed().as_micros()); self.stop_split(); @@ -113,7 +113,7 @@ impl PerfLogger { } pub fn log(&mut self) { - LOGGER.debug(&self.get_log_message().join("\n")); + LOGGER.with(|l| l.debug(&self.get_log_message().join("\n"))); } } From 8ab4cdcc44273d228ac35dda6a6d6af562311cb8 Mon Sep 17 00:00:00 2001 From: Moritz Jung Date: Mon, 13 Jan 2025 12:59:25 +0100 Subject: [PATCH 65/65] minor improvements --- wasm/src/graph.rs | 31 ++++++++++++++++++------------- wasm/tests/graph.rs | 2 +- wasm/tests/mermaid.rs | 2 ++ wasm/tests/update.rs | 3 +++ 4 files changed, 24 insertions(+), 14 deletions(-) diff --git a/wasm/src/graph.rs b/wasm/src/graph.rs index 19677ad0..152a5542 100644 --- a/wasm/src/graph.rs +++ b/wasm/src/graph.rs @@ -316,6 +316,9 @@ impl NoteGraph { let mut edge_type_tracker = self.edge_types.clone(); let mut edges_to_add: Vec<(NGNodeIndex, NGNodeIndex, &TransitiveGraphRule)> = Vec::new(); + // we reuse these two vectors to avoid allocations + let mut node_vec_1: Vec = Vec::new(); + let mut node_vec_2: Vec = Vec::new(); for i in 1..=max_rounds { let round_perf_split = perf_split.start_split(format!("Round {}", i)); @@ -326,6 +329,8 @@ impl NoteGraph { break; } + round_perf_split.start_split(format!("Applying Rules")); + for rule in self.transitive_rules.iter() { // if there is any edge type that the graph doesn't have, we can skip the rule if rule @@ -350,41 +355,41 @@ impl NoteGraph { // loop) and check for all possible applications of that rule // for that node. for start_node in self.graph.node_indices() { + node_vec_1.clear(); + node_vec_2.clear(); + // We start with the start node. - let mut current_nodes = vec![start_node]; + node_vec_1.push(start_node); // Now we iterate the path of the rule and each step, for all current nodes, // we check for outgoing edges that match the edge type of the current element // of the rule path. for edge_type in rule.iter_path() { - // TODO: We are creating a new vec here in a hot loop, maybe we can only - // create it once and reuse it. - let mut next_nodes = Vec::new(); - - for current_node in current_nodes { - for edge in self.graph.edges(current_node) { + for current_node in &node_vec_1 { + for edge in self.graph.edges(*current_node) { if edge.weight().edge_type == *edge_type { - next_nodes.push(edge.target()); + node_vec_2.push(edge.target()); } } } - current_nodes = next_nodes; + std::mem::swap(&mut node_vec_1, &mut node_vec_2); + node_vec_2.clear(); } // Now we are left with end nodes. For each end node, there exists a path from // the start node to the end node that matches the rule. - for end_node in current_nodes { + for end_node in &node_vec_1 { // If the rule can't loop, that means the start and end nodes can't be the // same. - if !rule.can_loop() && start_node == end_node { + if !rule.can_loop() && start_node == *end_node { continue; } if rule.close_reversed() { - edges_to_add.push((end_node, start_node, rule)); + edges_to_add.push((*end_node, start_node, rule)); } else { - edges_to_add.push((start_node, end_node, rule)); + edges_to_add.push((start_node, *end_node, rule)); } } } diff --git a/wasm/tests/graph.rs b/wasm/tests/graph.rs index fe456507..1a3a5ff0 100644 --- a/wasm/tests/graph.rs +++ b/wasm/tests/graph.rs @@ -1,4 +1,4 @@ -// #![cfg(target_arch = "wasm32")] +#![cfg(target_arch = "wasm32")] extern crate wasm_bindgen_test; use breadcrumbs_graph_wasm::{data::rules::TransitiveGraphRule, graph::NoteGraph}; diff --git a/wasm/tests/mermaid.rs b/wasm/tests/mermaid.rs index 2362b860..9d71847d 100644 --- a/wasm/tests/mermaid.rs +++ b/wasm/tests/mermaid.rs @@ -1,3 +1,5 @@ +#![cfg(target_arch = "wasm32")] + extern crate wasm_bindgen_test; use breadcrumbs_graph_wasm::{ diff --git a/wasm/tests/update.rs b/wasm/tests/update.rs index df933bec..08ef7e0c 100644 --- a/wasm/tests/update.rs +++ b/wasm/tests/update.rs @@ -1,4 +1,7 @@ +#![cfg(target_arch = "wasm32")] + extern crate wasm_bindgen_test; + use breadcrumbs_graph_wasm::{ data::{ construction::{GCEdgeData, GCNodeData},