{isSuccess ? (
-
+
) : (
- <>ID {id}>
+ <>
+ ID {id} ({status})
+ >
)}
- {isEditingTitle ? (
-
- ) : (
-
setIsEditingTitle(true)}
- >
- {title ?? (untitled)}
-
- )}
+
- {isSuccess ? (
-
- ) : (
- <>Status: {status}>
- )}
+ {Component && }
{id}
@@ -91,25 +47,27 @@ export default function NodeDisplay({ id }: NodeDisplayProps) {
);
}
-function NodeDisplayHeaderLoaded({ id, data }) {
+function NodeDisplayHeaderLoaded({ idx, id, data }) {
+ const openNode = useOpenNode();
return (
<>
- Type {data.type} · Last updated{" "}
-
+ {idx === 0 || (
+
+ )}
+
+ Type {data.type}{" "}
+ {data.created_at && (
+ <>
+ · Last updated
+ >
+ )}
+
>
);
}
-
-function NodeDisplayLoaded({ id, data }) {
- switch (data.type) {
- case "panorama/journal/page":
- return
;
-
- default:
- return (
- <>
- Don't know how to render node of type
{data.type}
- >
- );
- }
-}
diff --git a/app/src/components/SearchBar.tsx b/app/src/components/SearchBar.tsx
index 299c144..5254ca4 100644
--- a/app/src/components/SearchBar.tsx
+++ b/app/src/components/SearchBar.tsx
@@ -93,21 +93,23 @@ function SearchMenu({ results }) {
return (
- {results.map((result) => (
-
- ))}
+ {results.map((result) => {
+ return (
+
+ );
+ })}
);
}
diff --git a/app/src/components/Sidebar.module.scss b/app/src/components/Sidebar.module.scss
index 8b46c91..7076b4e 100644
--- a/app/src/components/Sidebar.module.scss
+++ b/app/src/components/Sidebar.module.scss
@@ -12,6 +12,8 @@
padding: 6px;
font-size: 0.95em;
border-radius: 4px;
+ border: none;
+ background-color: unset;
&:hover {
background-color: rgba(255, 255, 255, 0.2);
diff --git a/app/src/components/Sidebar.tsx b/app/src/components/Sidebar.tsx
index 6a359ec..724b56b 100644
--- a/app/src/components/Sidebar.tsx
+++ b/app/src/components/Sidebar.tsx
@@ -3,11 +3,13 @@ import styles from "./Sidebar.module.scss";
import classNames from "classnames";
import EmailIcon from "@mui/icons-material/Email";
import SettingsIcon from "@mui/icons-material/Settings";
+import { useOpenNode } from "../App";
export const sidebarExpandedAtom = atom(false);
export default function Sidebar() {
const sidebarExpanded = useAtomValue(sidebarExpandedAtom);
+ const openNode = useOpenNode();
return (
-
+
+
diff --git a/app/src/components/nodes/JournalPage.module.scss b/app/src/components/nodes/JournalPage.module.scss
index 7c9c0a4..d4b3798 100644
--- a/app/src/components/nodes/JournalPage.module.scss
+++ b/app/src/components/nodes/JournalPage.module.scss
@@ -4,6 +4,18 @@
flex-direction: column;
}
+.title {
+ padding: 12px;
+ border: none;
+ border-bottom: 1px solid lightgray;
+ outline: none;
+ font-size: 1.2em;
+}
+
+.untitled {
+ color: gray;
+}
+
.mdContent {
flex-grow: 1;
display: flex;
diff --git a/app/src/components/nodes/JournalPage.tsx b/app/src/components/nodes/JournalPage.tsx
index 76d805a..06abf2d 100644
--- a/app/src/components/nodes/JournalPage.tsx
+++ b/app/src/components/nodes/JournalPage.tsx
@@ -1,16 +1,18 @@
-import { useEffect, useRef, useState } from "react";
+import { useCallback, useEffect, useRef, useState } from "react";
import MDEditor, { PreviewType } from "@uiw/react-md-editor";
-import { usePrevious, useDebounce } from "@uidotdev/usehooks";
+import { usePrevious } from "@uidotdev/usehooks";
import { useQueryClient } from "@tanstack/react-query";
import styles from "./JournalPage.module.scss";
import remarkMath from "remark-math";
import rehypeKatex from "rehype-katex";
-import remarkEmbedder from "@remark-embedder/core";
import { parse as parseDate, format as formatDate } from "date-fns";
+import { useDebounce } from "use-debounce";
export interface JournalPageProps {
id: string;
data: {
+ day?: string;
+ title?: string;
content: string;
};
}
@@ -19,13 +21,19 @@ export default function JournalPage({ id, data }: JournalPageProps) {
const { day } = data;
const queryClient = useQueryClient();
const [value, setValue] = useState(() => data.content);
- const valueToSave = useDebounce(value, 1000);
+ const [valueToSave] = useDebounce(value, 1000, {
+ leading: true,
+ trailing: true,
+ });
const previous = usePrevious(valueToSave);
const changed = valueToSave !== previous;
const [mode, setMode] = useState
("preview");
+ const [title, setTitle] = useState(() => data.title);
+ const [isEditingTitle, setIsEditingTitle] = useState(false);
useEffect(() => {
if (changed) {
+ // console.log("OLD", previous, "NEW", valueToSave);
(async () => {
console.log("Saving...");
const resp = await fetch(`http://localhost:5195/node/${id}`, {
@@ -47,23 +55,63 @@ export default function JournalPage({ id, data }: JournalPageProps) {
}
}, [id, changed, valueToSave, queryClient]);
- return (
-
- {day && }
+ const saveChangedTitle = useCallback(() => {
+ (async () => {
+ const resp = await fetch(`http://localhost:5195/node/${id}`, {
+ method: "POST",
+ headers: {
+ "Content-Type": "application/json",
+ },
+ body: JSON.stringify({ title: title }),
+ });
+ setIsEditingTitle(false);
+ })();
+ }, [title, id]);
- newValue !== undefined && setValue(newValue)}
- preview={mode}
- visibleDragbar={false}
- onDoubleClick={() => setMode("live")}
- previewOptions={{
- remarkPlugins: [remarkMath],
- rehypePlugins: [rehypeKatex],
- }}
- />
-
+ return (
+ <>
+ {isEditingTitle ? (
+
+ ) : (
+ setIsEditingTitle(true)}
+ >
+ {title ?? (untitled)}
+
+ )}
+
+ {day && }
+
+ newValue !== undefined && setValue(newValue)}
+ preview={mode}
+ visibleDragbar={false}
+ onDoubleClick={() => setMode("live")}
+ previewOptions={{
+ remarkPlugins: [remarkMath],
+ rehypePlugins: [rehypeKatex],
+ }}
+ />
+
+ >
);
}
diff --git a/app/src/components/nodes/Mail.module.scss b/app/src/components/nodes/Mail.module.scss
new file mode 100644
index 0000000..18d3395
--- /dev/null
+++ b/app/src/components/nodes/Mail.module.scss
@@ -0,0 +1,25 @@
+.container {
+ display: flex;
+ flex-direction: column;
+}
+
+.header {
+ display: flex;
+ align-items: center;
+ padding: 2px 12px;
+
+ .title {
+ font-size: 1rem;
+ }
+}
+
+.settings {
+ h2 {
+ margin: 0;
+ }
+}
+
+.mailList {
+ flex-grow: 1;
+ border-top: 1px solid lightgray;
+}
\ No newline at end of file
diff --git a/app/src/components/nodes/Mail.tsx b/app/src/components/nodes/Mail.tsx
new file mode 100644
index 0000000..257eff6
--- /dev/null
+++ b/app/src/components/nodes/Mail.tsx
@@ -0,0 +1,134 @@
+import { useCallback, useState } from "react";
+import styles from "./Mail.module.scss";
+import { Formik } from "formik";
+import SettingsIcon from "@mui/icons-material/Settings";
+import { useQuery, useQueryClient } from "@tanstack/react-query";
+
+export default function Mail() {
+ const [showSettings, setShowSettings] = useState(false);
+
+ return (
+
+
+
+ {showSettings ? <>Settings> : <>Mail>}
+
+
+
+
+
+ {showSettings && (
+
+
+
+ )}
+
+
+
+ );
+}
+
+function MailConfig() {
+ const queryClient = useQueryClient();
+ const config = useQuery({
+ queryKey: ["mailConfigs"],
+ queryFn: fetchMailConfig,
+ });
+
+ const { isSuccess, data } = config;
+
+ return (
+ <>
+
+ Add a new config
+ {
+ (async () => {
+ const resp = await fetch("http://localhost:5195/node", {
+ method: "PUT",
+ headers: {
+ "Content-Type": "application/json",
+ },
+ body: JSON.stringify({
+ type: "panorama/mail/config",
+ extra_data: {
+ "panorama/mail/config/imap_hostname": values.imapHostname,
+ "panorama/mail/config/imap_port": values.imapPort,
+ "panorama/mail/config/imap_username": values.imapUsername,
+ "panorama/mail/config/imap_password": values.imapPassword,
+ },
+ }),
+ });
+ const data = await resp.json();
+ console.log("result", data);
+ queryClient.invalidateQueries({ queryKey: ["mailConfigs"] });
+ })();
+ }}
+ initialValues={{
+ imapHostname: "",
+ imapPort: 993,
+ imapUsername: "",
+ imapPassword: "",
+ }}
+ >
+ {({ values, handleSubmit, handleChange, handleBlur }) => (
+
+ )}
+
+
+
+
+ {isSuccess && (
+
+ {data.map((config) => (
+ - {JSON.stringify(config)}
+ ))}
+
+ )}
+
+ >
+ );
+}
+
+async function fetchMailConfig() {
+ const resp = await fetch("http://localhost:5195/mail/config");
+ const data = await resp.json();
+ return data.configs;
+}
diff --git a/app/src/global.scss b/app/src/global.scss
index e43dedc..cb9ead2 100644
--- a/app/src/global.scss
+++ b/app/src/global.scss
@@ -16,4 +16,8 @@ html,
.spacer {
flex-grow: 1;
+}
+
+* {
+ box-sizing: border-box;
}
\ No newline at end of file
diff --git a/app/src/lib/getNode.ts b/app/src/lib/getNode.ts
new file mode 100644
index 0000000..915b8fa
--- /dev/null
+++ b/app/src/lib/getNode.ts
@@ -0,0 +1,34 @@
+import { Component, FC } from "react";
+import JournalPage from "../components/nodes/JournalPage";
+import Mail from "../components/nodes/Mail";
+import { QueryFunctionContext } from "@tanstack/react-query";
+
+export interface RenderProps {
+ id: string;
+ data: any;
+}
+
+export interface NodeDescriptor {
+ render: FC;
+ data: {
+ type: string;
+ } & any;
+}
+
+export async function getNode({
+ queryKey,
+}: QueryFunctionContext): Promise {
+ const [, node_id] = queryKey;
+ switch (node_id) {
+ case "panorama/mail":
+ return { data: { type: "panorama/mail" }, render: Mail };
+ default: {
+ const resp = await fetch(`http://localhost:5195/node/${node_id}`);
+ const data = await resp.json();
+ return {
+ data: { ...data, type: "panorama/journal/page" },
+ render: JournalPage,
+ };
+ }
+ }
+}
diff --git a/crates/panorama-daemon/src/export.rs b/crates/panorama-daemon/src/export.rs
index f34f951..6e2e7c6 100644
--- a/crates/panorama-daemon/src/export.rs
+++ b/crates/panorama-daemon/src/export.rs
@@ -47,8 +47,6 @@ pub async fn export(State(state): State) -> AppResult<()> {
relation_columns.insert(relation_name.clone(), columns);
}
- println!("columns: {relation_columns:?}");
-
let base_dir = PathBuf::from("export");
fs::create_dir_all(&base_dir);
@@ -68,7 +66,6 @@ pub async fn export(State(state): State) -> AppResult<()> {
.join(", ");
let query = format!("?[{columns}] := *{relation_name} {{ {columns} }}");
- println!("Query: {query}");
let result = tx.run_script(&query, Default::default())?;
writer.write_record(result.headers).unwrap();
diff --git a/crates/panorama-daemon/src/journal.rs b/crates/panorama-daemon/src/journal.rs
index fd3bbcc..c9c681e 100644
--- a/crates/panorama-daemon/src/journal.rs
+++ b/crates/panorama-daemon/src/journal.rs
@@ -10,7 +10,6 @@ pub async fn get_todays_journal_id(
State(state): State,
) -> AppResult> {
let today = todays_date();
- println!("Getting journal id for {today}!");
let result = state.db.run_script(
"
@@ -22,8 +21,6 @@ pub async fn get_todays_journal_id(
ScriptMutability::Immutable,
)?;
- println!("Result: {:?}", result);
-
// TODO: Do this check on the server side
if result.rows.len() == 0 {
// Insert a new one
diff --git a/crates/panorama-daemon/src/mail.rs b/crates/panorama-daemon/src/mail.rs
index e69de29..3e8516e 100644
--- a/crates/panorama-daemon/src/mail.rs
+++ b/crates/panorama-daemon/src/mail.rs
@@ -0,0 +1,53 @@
+use axum::{extract::State, Json};
+use cozo::{DbInstance, ScriptMutability};
+use serde_json::Value;
+
+use crate::{error::AppResult, AppState};
+
+pub async fn get_mail_config(
+ State(state): State,
+) -> AppResult> {
+ let configs = fetch_mail_configs(&state.db)?;
+ Ok(Json(json!({
+ "configs": configs,
+ })))
+}
+
+pub async fn mail_loop(db: DbInstance) {
+ // Fetch the mail configs
+}
+
+#[derive(Serialize)]
+struct MailConfig {
+ node_id: String,
+ imap_hostname: String,
+ imap_port: u16,
+ imap_username: String,
+ imap_password: String,
+}
+
+fn fetch_mail_configs(db: &DbInstance) -> AppResult> {
+ let result = db.run_script(
+ "
+ ?[node_id, imap_hostname, imap_port, imap_username, imap_password] :=
+ *node{ id: node_id },
+ *mail_config{ node_id, imap_hostname, imap_port, imap_username, imap_password }
+ ",
+ Default::default(),
+ ScriptMutability::Immutable,
+ )?;
+
+ let result = result
+ .rows
+ .into_iter()
+ .map(|row| MailConfig {
+ node_id: row[0].get_str().unwrap().to_owned(),
+ imap_hostname: row[1].get_str().unwrap().to_owned(),
+ imap_port: row[2].get_int().unwrap() as u16,
+ imap_username: row[3].get_str().unwrap().to_owned(),
+ imap_password: row[4].get_str().unwrap().to_owned(),
+ })
+ .collect::>();
+
+ Ok(result)
+}
diff --git a/crates/panorama-daemon/src/main.rs b/crates/panorama-daemon/src/main.rs
index e8bd0a4..838d232 100644
--- a/crates/panorama-daemon/src/main.rs
+++ b/crates/panorama-daemon/src/main.rs
@@ -10,6 +10,7 @@ extern crate sugars;
mod error;
mod export;
mod journal;
+mod mail;
mod migrations;
mod node;
mod query_builder;
@@ -31,6 +32,7 @@ use tower_http::cors::{self, CorsLayer};
use crate::{
export::export,
journal::get_todays_journal_id,
+ mail::{get_mail_config, mail_loop},
migrations::run_migrations,
node::{create_node, get_node, node_types, search_nodes, update_node},
};
@@ -56,6 +58,8 @@ async fn main() -> Result<()> {
run_migrations(&db).await?;
+ tokio::spawn(mail_loop(db.clone()));
+
let state = AppState { db };
let cors = CorsLayer::new()
@@ -73,6 +77,7 @@ async fn main() -> Result<()> {
.route("/node/:id", post(update_node))
.route("/node/types", get(node_types))
.route("/journal/get_todays_journal_id", get(get_todays_journal_id))
+ .route("/mail/config", get(get_mail_config))
.layer(ServiceBuilder::new().layer(cors))
.with_state(state);
@@ -88,7 +93,8 @@ pub fn ensure_ok(s: &str) -> Result<()> {
let status = status.as_object().unwrap();
let ok = status.get("ok").unwrap().as_bool().unwrap_or(false);
if !ok {
- bail!("shit (error: {s})")
+ let display = status.get("display").unwrap().as_str().unwrap();
+ bail!("shit (error: {display})")
}
Ok(())
}
diff --git a/crates/panorama-daemon/src/migrations.rs b/crates/panorama-daemon/src/migrations.rs
index f4cd53d..742b8a4 100644
--- a/crates/panorama-daemon/src/migrations.rs
+++ b/crates/panorama-daemon/src/migrations.rs
@@ -51,6 +51,7 @@ pub async fn run_migrations(db: &DbInstance) -> Result<()> {
&format!("{{\"version\":{}}}", idx),
false,
);
+
ensure_ok(&result)?;
println!("succeeded migration {idx}!");
@@ -128,7 +129,11 @@ fn migration_01(db: &DbInstance) -> Result<()> {
}
{
?[key, relation, field_name, type] <- [
- ['panorama/journal/page/content', 'journal', 'content', 'string']
+ ['panorama/journal/page/content', 'journal', 'content', 'string'],
+ ['panorama/mail/config/imap_hostname', 'mail_config', 'imap_hostname', 'string'],
+ ['panorama/mail/config/imap_port', 'mail_config', 'imap_port', 'int'],
+ ['panorama/mail/config/imap_username', 'mail_config', 'imap_username', 'string'],
+ ['panorama/mail/config/imap_password', 'mail_config', 'imap_password', 'string'],
]
:put fqkey_to_dbkey { key, relation, field_name, type }
}
@@ -144,6 +149,20 @@ fn migration_01(db: &DbInstance) -> Result<()> {
filters: [Lowercase, Stemmer('english'), Stopwords('en')],
}
}
+
+ # Mail
+ {
+ :create mail_config {
+ node_id: String
+ =>
+ imap_hostname: String,
+ imap_port: Int,
+ imap_username: String,
+ imap_password: String,
+ }
+ }
+
+ # Calendar
",
"",
false,
diff --git a/crates/panorama-daemon/src/node.rs b/crates/panorama-daemon/src/node.rs
index cec251f..99a667a 100644
--- a/crates/panorama-daemon/src/node.rs
+++ b/crates/panorama-daemon/src/node.rs
@@ -70,7 +70,6 @@ pub async fn update_node(
Path(node_id): Path,
Json(update_data): Json,
) -> AppResult> {
- println!("Update data: {:?}", update_data);
let node_id_data = DataValue::from(node_id.clone());
// TODO: Combine these into the same script
@@ -158,7 +157,6 @@ pub async fn create_node(
) -> AppResult> {
let node_id = Uuid::now_v7();
let node_id = node_id.to_string();
- println!("Opts: {opts:?}");
let tx = state.db.multi_transaction(true);
@@ -179,15 +177,17 @@ pub async fn create_node(
let result_by_relation = result
.iter()
.into_group_map_by(|(key, (relation, field_name, ty))| relation);
- println!("Result by relation: {result_by_relation:?}");
for (relation, fields) in result_by_relation.iter() {
let fields_mapping = fields
.into_iter()
- .map(|(key, (_, field_name, _))| {
+ .map(|(key, (_, field_name, ty))| {
let new_value = extra_data.get(*key).unwrap();
// TODO: Make this more generic
- let new_value = DataValue::from(new_value.as_str().unwrap());
+ let new_value = match ty.as_str() {
+ "int" => DataValue::from(new_value.as_i64().unwrap()),
+ _ => DataValue::from(new_value.as_str().unwrap()),
+ };
(field_name.to_owned(), new_value)
})
.collect::>();
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index f56528c..312502d 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -47,6 +47,9 @@ importers:
date-fns:
specifier: ^3.6.0
version: 3.6.0
+ formik:
+ specifier: ^2.4.6
+ version: 2.4.6(react@18.3.1)
hast-util-to-jsx-runtime:
specifier: ^2.3.0
version: 2.3.0
@@ -1257,6 +1260,13 @@ packages:
'@types/unist': 3.0.2
dev: false
+ /@types/hoist-non-react-statics@3.3.5:
+ resolution: {integrity: sha512-SbcrWzkKBw2cdwRTwQAswfpB9g9LJWfjtUeW/jvNwbhC8cpmmNYVePa+ncbUe0rGTQ7G3Ff6mYUN2VMfLVr+Sg==}
+ dependencies:
+ '@types/react': 18.3.3
+ hoist-non-react-statics: 3.3.2
+ dev: false
+
/@types/katex@0.16.7:
resolution: {integrity: sha512-HMwFiRujE5PjrgwHQ25+bsLJgowjGjm5Z8FVSf0N6PwgJrwxH0QxzHYDcKsTfV3wva0vzrpqMTJS2jXPr5BMEQ==}
dev: false
@@ -1567,6 +1577,11 @@ packages:
character-entities: 2.0.2
dev: false
+ /deepmerge@2.2.1:
+ resolution: {integrity: sha512-R9hc1Xa/NOBi9WRVUWg19rl1UB7Tt4kuPd+thNJgFZoxXsTz7ncaPaeIm+40oSGuP33DfMb4sZt1QIGiJzC4EA==}
+ engines: {node: '>=0.10.0'}
+ dev: false
+
/dequal@2.0.3:
resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==}
engines: {node: '>=6'}
@@ -1674,6 +1689,22 @@ packages:
resolution: {integrity: sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==}
dev: false
+ /formik@2.4.6(react@18.3.1):
+ resolution: {integrity: sha512-A+2EI7U7aG296q2TLGvNapDNTZp1khVt5Vk0Q/fyfSROss0V/V6+txt2aJnwEos44IxTCW/LYAi/zgWzlevj+g==}
+ peerDependencies:
+ react: '>=16.8.0'
+ dependencies:
+ '@types/hoist-non-react-statics': 3.3.5
+ deepmerge: 2.2.1
+ hoist-non-react-statics: 3.3.2
+ lodash: 4.17.21
+ lodash-es: 4.17.21
+ react: 18.3.1
+ react-fast-compare: 2.0.4
+ tiny-warning: 1.0.3
+ tslib: 2.6.2
+ dev: false
+
/fsevents@2.3.3:
resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
@@ -2101,6 +2132,14 @@ packages:
resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==}
dev: false
+ /lodash-es@4.17.21:
+ resolution: {integrity: sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==}
+ dev: false
+
+ /lodash@4.17.21:
+ resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==}
+ dev: false
+
/longest-streak@3.1.0:
resolution: {integrity: sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==}
dev: false
@@ -2713,6 +2752,10 @@ packages:
scheduler: 0.23.2
dev: false
+ /react-fast-compare@2.0.4:
+ resolution: {integrity: sha512-suNP+J1VU1MWFKcyt7RtjiSWUjvidmQSlqu+eHslq+342xCbGTYmC0mEhPCOHxlW0CywylOC1u2DFAT+bv4dBw==}
+ dev: false
+
/react-is@16.13.1:
resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==}
dev: false
@@ -3099,6 +3142,10 @@ packages:
resolution: {integrity: sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew==}
dev: false
+ /tiny-warning@1.0.3:
+ resolution: {integrity: sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==}
+ dev: false
+
/to-fast-properties@2.0.0:
resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==}
engines: {node: '>=4'}
@@ -3122,6 +3169,10 @@ packages:
resolution: {integrity: sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==}
dev: false
+ /tslib@2.6.2:
+ resolution: {integrity: sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==}
+ dev: false
+
/typescript@5.4.5:
resolution: {integrity: sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==}
engines: {node: '>=14.17'}