move environment variables to their own file, also massive features
This commit is contained in:
		
							parent
							
								
									7d650ef424
								
							
						
					
					
						commit
						41d0f148de
					
				@ -1,6 +1,6 @@
 | 
				
			|||||||
body {
 | 
					html {
 | 
				
			||||||
  background: rgb(0,131,77);
 | 
					  background: rgb(0,131,77);
 | 
				
			||||||
  background: radial-gradient(circle, rgba(0,131,77,1) 0%, rgba(17,66,0,1) 100%);
 | 
					  /*background: radial-gradient(circle, rgba(0,131,77,1) 0%, rgba(17,66,0,1) 100%);*/
 | 
				
			||||||
  font-family: sans-serif;
 | 
					  font-family: sans-serif;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -42,3 +42,13 @@ header hr {
 | 
				
			|||||||
  padding: 1rem;
 | 
					  padding: 1rem;
 | 
				
			||||||
  border-radius: 1rem;
 | 
					  border-radius: 1rem;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					input {
 | 
				
			||||||
 | 
					  background: none;
 | 
				
			||||||
 | 
					  border: none;
 | 
				
			||||||
 | 
					  padding: 0;
 | 
				
			||||||
 | 
					  font-size: unset;
 | 
				
			||||||
 | 
					  color: unset;
 | 
				
			||||||
 | 
					  width: auto;
 | 
				
			||||||
 | 
					  overflow: visible;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -8,6 +8,8 @@ import Profile from '/src/login/Profile.jsx';
 | 
				
			|||||||
import GhostPage from '/src/page/GhostPage.jsx';
 | 
					import GhostPage from '/src/page/GhostPage.jsx';
 | 
				
			||||||
import Live from '/src/embodied/Live.jsx';
 | 
					import Live from '/src/embodied/Live.jsx';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//import Browsing from '/src/entwined/Browsing.jsx';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import AuthProvider from '/src/AuthProvider.jsx';
 | 
					import AuthProvider from '/src/AuthProvider.jsx';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import './App.css';
 | 
					import './App.css';
 | 
				
			||||||
 | 
				
			|||||||
@ -42,6 +42,10 @@ export async function fetchPage(number) {
 | 
				
			|||||||
	return shoofetch(`${apiUrl}/page/${number}`, {method: 'GET'});
 | 
						return shoofetch(`${apiUrl}/page/${number}`, {method: 'GET'});
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export async function fetchPageAttributes(number) {
 | 
				
			||||||
 | 
					  return shoofetch(`${apiUrl}/page/${number}/attributes`, {method: 'GET'});
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export async function fetchPageHistory(number) {
 | 
					export async function fetchPageHistory(number) {
 | 
				
			||||||
  return shoofetch(`${apiUrl}/page/${number}/history`, {method: 'GET'});
 | 
					  return shoofetch(`${apiUrl}/page/${number}/history`, {method: 'GET'});
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -50,10 +54,10 @@ export async function fetchPageAtEdit(number, id) {
 | 
				
			|||||||
  return shoofetch(`${apiUrl}/page/${number}/${id}`, {method: 'GET'});
 | 
					  return shoofetch(`${apiUrl}/page/${number}/${id}`, {method: 'GET'});
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export async function postPage({id, title, description, type}) {
 | 
					export async function postPage({number, title, description, type}) {
 | 
				
			||||||
	return shoofetch(`${apiUrl}/page/${id}`, {
 | 
						return shoofetch(`${apiUrl}/page/${number}`, {
 | 
				
			||||||
        method: 'POST',
 | 
					        method: 'POST',
 | 
				
			||||||
        body: JSON.stringify({id: id, title: title, description: description, type: type}),
 | 
					        body: JSON.stringify({number: number, title: title, description: description, type: type}),
 | 
				
			||||||
    })
 | 
					    })
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -10,6 +10,7 @@
 | 
				
			|||||||
	left: 20%;
 | 
						left: 20%;
 | 
				
			||||||
	right: 20%;
 | 
						right: 20%;
 | 
				
			||||||
	border-radius: 1rem;
 | 
						border-radius: 1rem;
 | 
				
			||||||
 | 
						border: 1px solid gray;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
.commandline form {
 | 
					.commandline form {
 | 
				
			||||||
	display: flex;
 | 
						display: flex;
 | 
				
			||||||
@ -21,7 +22,7 @@
 | 
				
			|||||||
	font-size: 16pt;
 | 
						font-size: 16pt;
 | 
				
			||||||
	border: none;
 | 
						border: none;
 | 
				
			||||||
	padding: 2rem;
 | 
						padding: 2rem;
 | 
				
			||||||
 | 
						border-radius: 1rem;
 | 
				
			||||||
	background: transparent;
 | 
						background: transparent;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -1,18 +1,30 @@
 | 
				
			|||||||
 | 
					import { forwardRef } from 'react';
 | 
				
			||||||
import { useForm } from "react-hook-form";
 | 
					import { useForm } from "react-hook-form";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import './CommandEntry.css';
 | 
					import './CommandEntry.css';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function CommandEntry({ onSubmit }) {
 | 
					const CommandEntry = forwardRef(({ setCommand, onSubmitCommand, command }, ref) => {
 | 
				
			||||||
	const { register, handleSubmit, setValue } = useForm();
 | 
						const { register, handleSubmit, setValue } = useForm();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return (
 | 
						return (
 | 
				
			||||||
		<div className="commandline">
 | 
							<div className="commandline">
 | 
				
			||||||
			<form onSubmit={handleSubmit((data) => { setValue('command', ""); onSubmit(data.command); })}>
 | 
								<form onSubmit={
 | 
				
			||||||
				<input {...register("command")} autoFocus type="text" placeholder="Enter a command!" />
 | 
									handleSubmit((data) => { onSubmitCommand(data.command); })
 | 
				
			||||||
 | 
								}>
 | 
				
			||||||
 | 
									<input {...register("command")} 
 | 
				
			||||||
 | 
										ref={ref}
 | 
				
			||||||
 | 
										onChange={(e) => {
 | 
				
			||||||
 | 
											setCommand(e.target.value);
 | 
				
			||||||
 | 
											setValue("command", e.target.value);
 | 
				
			||||||
 | 
										}} 
 | 
				
			||||||
 | 
										autoFocus 
 | 
				
			||||||
 | 
										type="text" 
 | 
				
			||||||
 | 
										placeholder="Enter a command!" 
 | 
				
			||||||
 | 
										value={command} />
 | 
				
			||||||
				<button type="submit">Do</button>
 | 
									<button type="submit">Do</button>
 | 
				
			||||||
			</form>
 | 
								</form>
 | 
				
			||||||
		</div>
 | 
							</div>
 | 
				
			||||||
		);
 | 
							);
 | 
				
			||||||
}
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default CommandEntry;
 | 
					export default CommandEntry;
 | 
				
			||||||
@ -1,53 +1,132 @@
 | 
				
			|||||||
import { useState, useEffect } from 'react';
 | 
					import { useState, useEffect, useRef } from 'react';
 | 
				
			||||||
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
 | 
					import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
 | 
				
			||||||
import { useParams, useNavigate } from 'react-router-dom';
 | 
					import { useParams, useNavigate } from 'react-router-dom';
 | 
				
			||||||
import { apiUrl, wsUrl } from '../apiTools.jsx';
 | 
					import { apiUrl, wsUrl, fetchPage, fetchPageAttributes, fetchPageAtEdit, postPage, fetchCurrentVerbs } from '../apiTools.jsx';
 | 
				
			||||||
import { useFixLinks } from '../clientStuff.jsx';
 | 
					import { useFixLinks } from '../clientStuff.jsx';
 | 
				
			||||||
import { useLoggedIn } from '../AuthProvider.jsx';
 | 
					import { useLoggedIn } from '../AuthProvider.jsx';
 | 
				
			||||||
import useWebSocket, { ReadyState } from 'react-use-websocket';
 | 
					import useWebSocket, { ReadyState } from 'react-use-websocket';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import Page from '../page/Page.jsx';
 | 
					import Page from '../page/Page.jsx';
 | 
				
			||||||
 | 
					import ExtendedAttributes from '../page/ExtendedAttributes.jsx';
 | 
				
			||||||
import MessageFeed from './MessageFeed.jsx';
 | 
					import MessageFeed from './MessageFeed.jsx';
 | 
				
			||||||
import Sidebar from './Sidebar.jsx';
 | 
					import Sidebar from './Sidebar.jsx';
 | 
				
			||||||
import CommandEntry from './CommandEntry.jsx';
 | 
					import CommandEntry from './CommandEntry.jsx';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function Live({editing, ...props}) {
 | 
					function Live({...props}) {
 | 
				
			||||||
  const navigate = useNavigate();
 | 
					  const navigate = useNavigate();
 | 
				
			||||||
  const loggedIn = useLoggedIn();
 | 
					  const loggedIn = useLoggedIn();
 | 
				
			||||||
 | 
					  const queryClient = useQueryClient();
 | 
				
			||||||
  
 | 
					  
 | 
				
			||||||
 | 
					  const [ command, setCommand ] = useState('');
 | 
				
			||||||
  const [ messageHistory, setMessageHistory ] = useState([]);
 | 
					  const [ messageHistory, setMessageHistory ] = useState([]);
 | 
				
			||||||
  const [ currentNumber, setCurrentNumber ] = useState(1);
 | 
					  const [ currentNumber, setCurrentNumber ] = useState(1);
 | 
				
			||||||
 | 
					  const [ editing, setEditing ] = useState(false);
 | 
				
			||||||
  const [ connecting, setConnecting ] = useState(true);
 | 
					  const [ connecting, setConnecting ] = useState(true);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const commandEntryRef = useRef(null);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  //setting up the websocket and using its data!
 | 
				
			||||||
  const { sendMessage, lastMessage, lastJsonMessage, readyState } = useWebSocket(`${wsUrl}/embody`, {
 | 
					  const { sendMessage, lastMessage, lastJsonMessage, readyState } = useWebSocket(`${wsUrl}/embody`, {
 | 
				
			||||||
    onClose: () => setConnecting(false)
 | 
					    onClose: () => console.log("broke connection!"),
 | 
				
			||||||
 | 
					    onOpen: () => console.log("opened connection"),
 | 
				
			||||||
 | 
					    shouldReconnect: (closeEvent) => true,
 | 
				
			||||||
  }, connecting);
 | 
					  }, connecting);
 | 
				
			||||||
  
 | 
					  
 | 
				
			||||||
  useEffect(() => {
 | 
					  useEffect(() => console.log(lastMessage), [lastMessage]);
 | 
				
			||||||
    if (lastMessage !== null) {
 | 
					 | 
				
			||||||
      console.log(lastMessage);
 | 
					 | 
				
			||||||
      setMessageHistory((prev) => prev.concat(lastMessage));
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
      if (lastMessage.data.startsWith("location change to: ")) {
 | 
					  useEffect(() => {
 | 
				
			||||||
        const num = Number(lastMessage.data.replace("location change to: #", ""));
 | 
					    if (!(lastJsonMessage === null || lastJsonMessage === undefined)) {
 | 
				
			||||||
        setCurrentNumber(num);
 | 
					      setMessageHistory((prev) => prev.concat(lastJsonMessage));
 | 
				
			||||||
 | 
					      //if (lastJsonMessage["error"]) alert(lastJsonMessage["error"]);
 | 
				
			||||||
 | 
					      if (lastJsonMessage["setPageNumber"]) 
 | 
				
			||||||
 | 
					        setCurrentNumber(Number(lastJsonMessage["setPageNumber"]));
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					  }, [lastJsonMessage, lastMessage]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // first class object data values!
 | 
				
			||||||
 | 
					  const fetchPageQuery = useQuery({
 | 
				
			||||||
 | 
					    queryKey: ['page', currentNumber, null],
 | 
				
			||||||
 | 
					    queryFn: () => fetchPage(currentNumber),
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // setting up for editing and suchlike
 | 
				
			||||||
 | 
					  const [title, setTitle] = useState(null);
 | 
				
			||||||
 | 
					  useEffect(() => {
 | 
				
			||||||
 | 
					    setTitle(fetchPageQuery.data?.title);
 | 
				
			||||||
 | 
					  }, [fetchPageQuery.data?.title]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const [text, setText] = useState(null);
 | 
				
			||||||
 | 
					  useEffect(() => {
 | 
				
			||||||
 | 
					    setText(fetchPageQuery.data?.description);
 | 
				
			||||||
 | 
					  }, [fetchPageQuery.data?.description]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const [type, setType] = useState(false);
 | 
				
			||||||
 | 
					  useEffect(() => {
 | 
				
			||||||
 | 
					    setType(fetchPageQuery.data?.type == 1);
 | 
				
			||||||
 | 
					  }, [fetchPageQuery.data?.type]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // extended attribute store!
 | 
				
			||||||
 | 
					  const fetchAttributesQuery = useQuery({
 | 
				
			||||||
 | 
					    queryKey: ['attributes', currentNumber],
 | 
				
			||||||
 | 
					    queryFn: () => fetchPageAttributes(currentNumber)
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // verbs available to us
 | 
				
			||||||
 | 
					  const fetchVerbsQuery = useQuery({
 | 
				
			||||||
 | 
					      queryKey: ['my verbs'],
 | 
				
			||||||
 | 
					      queryFn: fetchCurrentVerbs,
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  let verbs = fetchVerbsQuery.data || [];
 | 
				
			||||||
 | 
					  if (!editing) verbs = ["edit this"].concat(verbs);
 | 
				
			||||||
 | 
					  else verbs = ["save changes"].concat(verbs);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const postMutation = useMutation({ // for changing the value when we're done with it
 | 
				
			||||||
 | 
					    mutationFn: postPage,
 | 
				
			||||||
 | 
					    onSettled: async (data, error, variables) => {
 | 
				
			||||||
 | 
					      queryClient.invalidateQueries(['page', variables.number, null])
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  function saveChanges() {
 | 
				
			||||||
 | 
					    postMutation.mutate({
 | 
				
			||||||
 | 
					      number: currentNumber, 
 | 
				
			||||||
 | 
					      title: title, 
 | 
				
			||||||
 | 
					      description: text,
 | 
				
			||||||
 | 
					      type: type ? 1 : 0,
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  function handleSubmitCommand() {
 | 
				
			||||||
 | 
					    const c = command.trim();
 | 
				
			||||||
 | 
					    if (c == "edit this") setEditing(true);
 | 
				
			||||||
 | 
					    else if (c == "save changes") {
 | 
				
			||||||
 | 
					      saveChanges();
 | 
				
			||||||
 | 
					      setEditing(false);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    else sendMessage(c);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    setCommand("");
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  }, [lastMessage]);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // spread this return value on <a/> elements in order to make them navigate 
 | 
					  // spread this return value on <a/> elements in order to make them navigate 
 | 
				
			||||||
  const commandLinkClick = (e) => {
 | 
					  const commandLinkClick = (e) => {
 | 
				
			||||||
    if (e.target.tagName != "A") { return; }
 | 
					    if (e.target.tagName != "A") return;
 | 
				
			||||||
    if (!e.target.href.includes(window.location.origin)) { return; }
 | 
					    if (!e.target.href.includes(window.location.origin)) return; 
 | 
				
			||||||
 | 
					    if (e.ctrlKey || e.metaKey || e.shiftKey || e.altKey) return; 
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    // if it was an <a> tag, if it's to a subdomain of this website, and a modifier wasn't held
 | 
				
			||||||
 | 
					    // then we should prevent default behavior and execute our own.
 | 
				
			||||||
    e.preventDefault();
 | 
					    e.preventDefault();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const localLink = e.target.href.replace(window.location.origin, "");
 | 
					    const localLink = e.target.href.replace(window.location.origin, "");
 | 
				
			||||||
    const targetString = localLink.replace("/", "");
 | 
					    const targetString = localLink.replace("/", "");
 | 
				
			||||||
    const targetNumber = Number(targetString);
 | 
					    const targetNumber = Number(targetString);
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    if (targetNumber != 0 && targetNumber != NaN) {
 | 
					    if (targetNumber != 0 && !isNaN(targetNumber)) {
 | 
				
			||||||
      const warpMessage = `warp #${targetString}`;
 | 
					      const warpMessage = `warp #${targetNumber}`;
 | 
				
			||||||
      console.log(`clicked a link, executing "warp #${targetString}"`);
 | 
					      console.log(`clicked a link, executing "warp #${targetNumber}"`);
 | 
				
			||||||
      sendMessage(warpMessage);
 | 
					      sendMessage(warpMessage);
 | 
				
			||||||
      return;
 | 
					      return;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@ -55,16 +134,46 @@ function Live({editing, ...props}) {
 | 
				
			|||||||
    navigate(e.target.href.replace(window.location.origin, ""));
 | 
					    navigate(e.target.href.replace(window.location.origin, ""));
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  useEffect(() => {
 | 
				
			||||||
 | 
					    window.history.replaceState(null, "", window.location.origin + "/" + currentNumber);
 | 
				
			||||||
 | 
					  }, [currentNumber]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  return (
 | 
					  return (
 | 
				
			||||||
    <>
 | 
					    <>
 | 
				
			||||||
      <Page editing={editing} number={currentNumber} linkClick={commandLinkClick} {...props} />
 | 
					      <div className="main-column">
 | 
				
			||||||
 | 
					        <Page 
 | 
				
			||||||
 | 
					          page={{...fetchPageQuery.data, description: text, title: title, type: type }}
 | 
				
			||||||
 | 
					          editing={editing} 
 | 
				
			||||||
 | 
					          linkClick={commandLinkClick} 
 | 
				
			||||||
 | 
					          onChangeTitle={(e) => setTitle(e.target.value)}
 | 
				
			||||||
 | 
					          onChangeText={setText}
 | 
				
			||||||
 | 
					          onChangeType={() => setType(!type)}
 | 
				
			||||||
 | 
					          {...props} />
 | 
				
			||||||
 | 
					        <ExtendedAttributes
 | 
				
			||||||
 | 
					          attributes={fetchAttributesQuery.data} />
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
      <MessageFeed messages={messageHistory}>
 | 
					      <MessageFeed messages={messageHistory}>
 | 
				
			||||||
        <button disabled={connecting} onClick={() => setConnecting(true)}>Reconnect</button>
 | 
					        <button disabled={connecting} onClick={() => setConnecting(true)}>
 | 
				
			||||||
 | 
					        {{
 | 
				
			||||||
 | 
					          [ReadyState.CONNECTING]: 'Connecting',
 | 
				
			||||||
 | 
					          [ReadyState.OPEN]: 'Open',
 | 
				
			||||||
 | 
					          [ReadyState.CLOSING]: 'Closing',
 | 
				
			||||||
 | 
					          [ReadyState.CLOSED]: 'Closed',
 | 
				
			||||||
 | 
					          [ReadyState.UNINSTANTIATED]: 'Uninstantiated',
 | 
				
			||||||
 | 
					        }[readyState]}</button>
 | 
				
			||||||
        <button onClick={()=> setMessageHistory([])}>Clear History</button>
 | 
					        <button onClick={()=> setMessageHistory([])}>Clear History</button>
 | 
				
			||||||
      </MessageFeed>
 | 
					      </MessageFeed>
 | 
				
			||||||
      <Sidebar pagenumber={currentNumber} hidden="true" sendWord={(word) => setCommand((command + " " + word).trim())}>
 | 
					      <Sidebar verbs={verbs} sendWord={(word) => {
 | 
				
			||||||
 | 
					        setCommand((command + " " + word).trim());
 | 
				
			||||||
 | 
					        commandEntryRef.current.focus();
 | 
				
			||||||
 | 
					        // maybe set focus to the command entry?
 | 
				
			||||||
 | 
					      }}>
 | 
				
			||||||
      </Sidebar>
 | 
					      </Sidebar>
 | 
				
			||||||
      <CommandEntry onSubmit={(w) => { console.log(w); sendMessage(w); } }/>
 | 
					      <CommandEntry 
 | 
				
			||||||
 | 
					        ref={commandEntryRef}
 | 
				
			||||||
 | 
					        setCommand={(val) => {setCommand(val)}} 
 | 
				
			||||||
 | 
					        onSubmitCommand={handleSubmitCommand} 
 | 
				
			||||||
 | 
					        command={command} />
 | 
				
			||||||
    </>
 | 
					    </>
 | 
				
			||||||
  );
 | 
					  );
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -17,7 +17,7 @@ function MessageFeed({messages=[], children}) {
 | 
				
			|||||||
			<ol ref={listContainer}>
 | 
								<ol ref={listContainer}>
 | 
				
			||||||
				{messages.map((message, idx) => 
 | 
									{messages.map((message, idx) => 
 | 
				
			||||||
					<li key={idx}>
 | 
										<li key={idx}>
 | 
				
			||||||
						{message.data} 
 | 
											{JSON.stringify(message)} 
 | 
				
			||||||
					</li>
 | 
										</li>
 | 
				
			||||||
				)}
 | 
									)}
 | 
				
			||||||
			</ol>
 | 
								</ol>
 | 
				
			||||||
 | 
				
			|||||||
@ -5,24 +5,17 @@ import { fetchCurrentVerbs } from '../apiTools.jsx';
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import './Sidebar.css';
 | 
					import './Sidebar.css';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function Sidebar({children, pagenumber, hidden=false, sendWord=(()=>null)}) {
 | 
					function Sidebar({children, verbs, hidden=true, sendWord=(()=>null)}) {
 | 
				
			||||||
	const [open, setOpen] = useState(!hidden);
 | 
						const [open, setOpen] = useState(!hidden);
 | 
				
			||||||
	const loggedIn = useLoggedIn();
 | 
						const loggedIn = useLoggedIn();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	const { isPending, isError, error, data } = useQuery({ // fetch the currrent values
 | 
					 | 
				
			||||||
	    queryKey: ['my verbs'],
 | 
					 | 
				
			||||||
	    queryFn: fetchCurrentVerbs,
 | 
					 | 
				
			||||||
	});
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	const verbs = data;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return (
 | 
						return (
 | 
				
			||||||
		<div className={`sidebar ${!open && "sidebar-hidden"}`}>
 | 
							<div className={`sidebar ${!open && "sidebar-hidden"}`}>
 | 
				
			||||||
			<button onClick={() => setOpen(!open)}>{open ? "hide" : "show"}</button>
 | 
								<button onClick={() => setOpen(!open)}>{open ? "hide" : "show"}</button>
 | 
				
			||||||
			<ul>
 | 
								<ul>
 | 
				
			||||||
				{(verbs || []).map( (name) => (
 | 
									{verbs.map( (name) => (
 | 
				
			||||||
					<li key={name}>
 | 
										<li key={name}>
 | 
				
			||||||
						<button onClick={() => sendWord(name)}>
 | 
											<button onClick={() => { console.log("clicked button for", name); sendWord(name);}}>
 | 
				
			||||||
							{name}
 | 
												{name}
 | 
				
			||||||
						</button>
 | 
											</button>
 | 
				
			||||||
					</li> 
 | 
										</li> 
 | 
				
			||||||
 | 
				
			|||||||
@ -1,7 +1,9 @@
 | 
				
			|||||||
import { useEffect } from 'react';
 | 
					import { useEffect } from 'react';
 | 
				
			||||||
import { SigmaContainer } from "@react-sigma/core";
 | 
					import { SigmaContainer, useRegisterEvents } from "@react-sigma/core";
 | 
				
			||||||
import "@react-sigma/core/lib/react-sigma.min.css";
 | 
					import "@react-sigma/core/lib/react-sigma.min.css";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import { useNavigate } from 'react-router-dom';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { useQuery } from '@tanstack/react-query';
 | 
					import { useQuery } from '@tanstack/react-query';
 | 
				
			||||||
import { fetchGraph } from '../apiTools.jsx';
 | 
					import { fetchGraph } from '../apiTools.jsx';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -22,7 +24,21 @@ export default function GraphRender() {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
			return () => supervisor.stop();
 | 
								return () => supervisor.stop();
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
						}, [data]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/*
 | 
				
			||||||
 | 
						const navigate = useNavigate();
 | 
				
			||||||
 | 
						const registerEvents = useRegisterEvents();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						useEffect(() => {
 | 
				
			||||||
 | 
					    console.log("register events");
 | 
				
			||||||
 | 
					    // Register the events
 | 
				
			||||||
 | 
					    registerEvents({
 | 
				
			||||||
 | 
					      // node events
 | 
				
			||||||
 | 
					      clickNode: (event) => navigate(`/${event.node}`),
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					  }, [registerEvents]);
 | 
				
			||||||
 | 
					  */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (isPending) return "Loading...";
 | 
						if (isPending) return "Loading...";
 | 
				
			||||||
	else if (error) return `Error encountered: ${error}`;
 | 
						else if (error) return `Error encountered: ${error}`;
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										37
									
								
								client/src/page/ExtendedAttributes.jsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								client/src/page/ExtendedAttributes.jsx
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,37 @@
 | 
				
			|||||||
 | 
					function ExtendedAttributes({attributes, ...props}) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!attributes) return;
 | 
				
			||||||
 | 
						return <>
 | 
				
			||||||
 | 
							<section className="page-contents">
 | 
				
			||||||
 | 
								{attributes.parent ? 
 | 
				
			||||||
 | 
									`The parent of this object is #${attributes.parent}.`
 | 
				
			||||||
 | 
									:
 | 
				
			||||||
 | 
									"This object has no parent."}
 | 
				
			||||||
 | 
							</section>
 | 
				
			||||||
 | 
							
 | 
				
			||||||
 | 
							{attributes.location && <section className="page-contents">
 | 
				
			||||||
 | 
								This object lives in #{JSON.stringify(attributes.location)}.
 | 
				
			||||||
 | 
							</section>}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							{attributes.contents && typeof attributes.contents.map === 'function' && <section className="page-contents">
 | 
				
			||||||
 | 
								<h4>Contents of this location:</h4>
 | 
				
			||||||
 | 
								<ul>
 | 
				
			||||||
 | 
									{attributes.contents.map((objectNum) => <li key={objectNum}>{objectNum}</li>)}
 | 
				
			||||||
 | 
								</ul>
 | 
				
			||||||
 | 
							</section>}
 | 
				
			||||||
 | 
							{attributes.verbs && <section className="page-contents">
 | 
				
			||||||
 | 
								<h4>Verbs defined on this object:</h4>
 | 
				
			||||||
 | 
								<ul>
 | 
				
			||||||
 | 
									{attributes.verbs.map((verbNum) => <li key={verbNum}>{verbNum}</li>)}
 | 
				
			||||||
 | 
								</ul>
 | 
				
			||||||
 | 
							</section>}
 | 
				
			||||||
 | 
							{attributes.prepositions && <section className="page-contents">
 | 
				
			||||||
 | 
								<h4>Prepositions this verb expects:</h4>
 | 
				
			||||||
 | 
								<ul>
 | 
				
			||||||
 | 
									{attributes.prepositions.map((prep) => <li key={prep}>{prep}</li>)}
 | 
				
			||||||
 | 
								</ul>
 | 
				
			||||||
 | 
							</section>}
 | 
				
			||||||
 | 
						</>
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default ExtendedAttributes;
 | 
				
			||||||
@ -1,29 +1,98 @@
 | 
				
			|||||||
import { useState } from 'react';
 | 
					import { useState, useEffect } from 'react';
 | 
				
			||||||
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
 | 
					import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
 | 
				
			||||||
import { useParams, useNavigate } from 'react-router-dom';
 | 
					import { useParams, useNavigate } from 'react-router-dom';
 | 
				
			||||||
import { apiUrl, fetchPage, fetchPageAtEdit, postPage, deletePage } from '../apiTools.jsx';
 | 
					import { apiUrl, fetchPage, fetchPageAttributes, postPage } from '../apiTools.jsx';
 | 
				
			||||||
import { useLoggedIn } from '../AuthProvider.jsx';
 | 
					import { useLoggedIn } from '../AuthProvider.jsx';
 | 
				
			||||||
import Page from './Page.jsx';
 | 
					import Page from './Page.jsx';
 | 
				
			||||||
 | 
					import ExtendedAttributes from './ExtendedAttributes.jsx';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import './Pages.css';
 | 
					import './Pages.css';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function GhostPage({editing, ...props}) {
 | 
					function GhostPage({editing, ...props}) {
 | 
				
			||||||
  const { pagenumber, editid } = useParams();
 | 
					  const { pagenumber, editid } = useParams();
 | 
				
			||||||
  const navigate = useNavigate();
 | 
					  const navigate = useNavigate();
 | 
				
			||||||
 | 
					  const loggedIn = useLoggedIn();
 | 
				
			||||||
 | 
					  const queryClient = useQueryClient();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const linkClick = (e) => {
 | 
					  const linkClick = (e) => {
 | 
				
			||||||
    if (e.target.tagName != "A") { return; }
 | 
					    if (e.target.tagName != "A") { return; }
 | 
				
			||||||
    if (!e.target.href.includes(window.location.origin)) { return; }
 | 
					    if (!e.target.href.includes(window.location.origin)) return;
 | 
				
			||||||
    e.preventDefault();
 | 
					    e.preventDefault();
 | 
				
			||||||
    navigate(e.target.href.replace(window.location.origin, ""));
 | 
					    navigate(e.target.href.replace(window.location.origin, ""));
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  return <Page 
 | 
					  const fetchPageQuery = useQuery({
 | 
				
			||||||
    number={pagenumber} 
 | 
					    queryKey: ['page', pagenumber, null],
 | 
				
			||||||
    editid={editid} 
 | 
					    queryFn: () => fetchPage(pagenumber),
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					  const fetchAttributesQuery = useQuery({
 | 
				
			||||||
 | 
					    queryKey: ['attributes', pagenumber],
 | 
				
			||||||
 | 
					    queryFn: () => fetchPageAttributes(pagenumber)
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const [title, setTitle] = useState(null);
 | 
				
			||||||
 | 
					  useEffect(() => {
 | 
				
			||||||
 | 
					    setTitle(fetchPageQuery.data?.title);
 | 
				
			||||||
 | 
					  }, [fetchPageQuery.data?.title]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const [text, setText] = useState(null);
 | 
				
			||||||
 | 
					  useEffect(() => {
 | 
				
			||||||
 | 
					    setText(fetchPageQuery.data?.description);
 | 
				
			||||||
 | 
					  }, [fetchPageQuery.data?.description]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const [type, setType] = useState(false);
 | 
				
			||||||
 | 
					  useEffect(() => {
 | 
				
			||||||
 | 
					    setType(fetchPageQuery.data?.type == 1);
 | 
				
			||||||
 | 
					  }, [fetchPageQuery.data?.type]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const postMutation = useMutation({ // for changing the value when we're done with it
 | 
				
			||||||
 | 
					    mutationFn: postPage,
 | 
				
			||||||
 | 
					    onSettled: async (data, error, variables) => {
 | 
				
			||||||
 | 
					      // Invalidate and refetch
 | 
				
			||||||
 | 
					      await queryClient.invalidateQueries(['page', variables.number, null])
 | 
				
			||||||
 | 
					      console.log("shoulda just invalidated the thing");
 | 
				
			||||||
 | 
					      navigate(`/${pagenumber}`);
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  function submitChanges(e) {
 | 
				
			||||||
 | 
					    postMutation.mutate({
 | 
				
			||||||
 | 
					      number: pagenumber, 
 | 
				
			||||||
 | 
					      title: title, 
 | 
				
			||||||
 | 
					      description: text,
 | 
				
			||||||
 | 
					      type: type ? 1 : 0,
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return <div className="main-column">
 | 
				
			||||||
 | 
					    <Page 
 | 
				
			||||||
 | 
					      page={{...fetchPageQuery.data, description: text, title: title, type: type }}
 | 
				
			||||||
      editing={editing} 
 | 
					      editing={editing} 
 | 
				
			||||||
      linkClick={linkClick}
 | 
					      linkClick={linkClick}
 | 
				
			||||||
    {...props}/>;
 | 
					      onChangeTitle={(e) => setTitle(e.target.value)}
 | 
				
			||||||
 | 
					      onChangeText={setText}
 | 
				
			||||||
 | 
					      onChangeType={() => setType(!type)}
 | 
				
			||||||
 | 
					      {...props}/>
 | 
				
			||||||
 | 
					    <ExtendedAttributes
 | 
				
			||||||
 | 
					      attributes={fetchAttributesQuery.data} />
 | 
				
			||||||
 | 
					    <button
 | 
				
			||||||
 | 
					      onClick={() => navigate(`/${pagenumber}/history`)}>
 | 
				
			||||||
 | 
					      History
 | 
				
			||||||
 | 
					    </button>
 | 
				
			||||||
 | 
					    {editing && (
 | 
				
			||||||
 | 
					      <button 
 | 
				
			||||||
 | 
					        disabled={postMutation.isPending}
 | 
				
			||||||
 | 
					        onClick={submitChanges}>
 | 
				
			||||||
 | 
					        {postMutation.isPending ? "Updating..." : "Update"}
 | 
				
			||||||
 | 
					      </button>)}
 | 
				
			||||||
 | 
					    {!editing && !editid && (
 | 
				
			||||||
 | 
					      <button
 | 
				
			||||||
 | 
					        disabled={!loggedIn}
 | 
				
			||||||
 | 
					        onClick={() => navigate(`/${pagenumber}/edit`, {replace: true})}>
 | 
				
			||||||
 | 
					        Edit Page 
 | 
				
			||||||
 | 
					      </button>)}
 | 
				
			||||||
 | 
					  </div>;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default GhostPage;
 | 
					export default GhostPage;
 | 
				
			||||||
@ -1,121 +1,73 @@
 | 
				
			|||||||
import { useState, useEffect } from 'react';
 | 
					import { useState, useEffect } from 'react';
 | 
				
			||||||
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
 | 
					import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
 | 
				
			||||||
import { useParams, useNavigate } from 'react-router-dom';
 | 
					import { apiUrl, fetchPage, fetchPageAtEdit, postPage } from '../apiTools.jsx';
 | 
				
			||||||
import { apiUrl, fetchPage, fetchPageAtEdit, postPage, deletePage } from '../apiTools.jsx';
 | 
					 | 
				
			||||||
import { useFixLinks } from '../clientStuff.jsx';
 | 
					import { useFixLinks } from '../clientStuff.jsx';
 | 
				
			||||||
import { useLoggedIn } from '../AuthProvider.jsx';
 | 
					
 | 
				
			||||||
import { MDXEditor, headingsPlugin, quotePlugin, linkPlugin, diffSourcePlugin } from '@mdxeditor/editor';
 | 
					import { MDXEditor, 
 | 
				
			||||||
 | 
					  headingsPlugin, quotePlugin, listsPlugin, 
 | 
				
			||||||
 | 
					  thematicBreakPlugin, linkPlugin, diffSourcePlugin } from '@mdxeditor/editor';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import './Pages.css';
 | 
					import './Pages.css';
 | 
				
			||||||
import 'highlight.js/styles/a11y-dark.min.css';
 | 
					import 'highlight.js/styles/a11y-dark.min.css';
 | 
				
			||||||
import '@mdxeditor/editor/style.css';
 | 
					import '@mdxeditor/editor/style.css';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function Page({ editing, number, editid=null, linkClick=()=>{} }) {
 | 
					function Page({ 
 | 
				
			||||||
  const queryClient = useQueryClient();
 | 
					    page=null,
 | 
				
			||||||
  const navigate = useNavigate();
 | 
					    editing, historical, 
 | 
				
			||||||
  const loggedIn = useLoggedIn();
 | 
					    linkClick=()=>{},
 | 
				
			||||||
 | 
					    onChangeTitle=()=>{},
 | 
				
			||||||
  const fetchQuery = useQuery({ // fetch the currrent values
 | 
					    onChangeType=()=>{},
 | 
				
			||||||
    queryKey: ['page', number, editid],
 | 
					    onChangeText=()=>{},
 | 
				
			||||||
    queryFn: () => editid ? fetchPageAtEdit(number, editid) : fetchPage(number)
 | 
					     }) {
 | 
				
			||||||
  })
 | 
					  let {title, description, html, lua, time, author, type} = page || {};
 | 
				
			||||||
 | 
					  if (!title) title = "";
 | 
				
			||||||
  const postMutation = useMutation({ // for changing the value when we're done with it
 | 
					 | 
				
			||||||
    mutationFn: ({id, title, description, type}) => postPage({id, title, description, type}),
 | 
					 | 
				
			||||||
    onSettled: async (data, error, variables) => {
 | 
					 | 
				
			||||||
      // Invalidate and refetch
 | 
					 | 
				
			||||||
      queryClient.invalidateQueries({ queryKey: ['page', variables.id, null] })
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  const readyToShow = !(fetchQuery.error || fetchQuery.isPending);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  let {id, title, description, html, lua, time, author, type} = fetchQuery.data || {};
 | 
					 | 
				
			||||||
  const [verb, setVerb] = useState(false);
 | 
					 | 
				
			||||||
  const [enteredText, setText] = useState("...");
 | 
					 | 
				
			||||||
  if (!title) title = "[no title]";
 | 
					 | 
				
			||||||
  if (!html) html = "[body missing]";
 | 
					  if (!html) html = "[body missing]";
 | 
				
			||||||
  if (!lua) lua = "[no definition]";
 | 
					  if (!lua) lua = "[no definition]";
 | 
				
			||||||
  if (!description) description = "[body missing]";
 | 
					  if (!description) description = "[body missing]";
 | 
				
			||||||
  useEffect(() => {
 | 
					 | 
				
			||||||
    setVerb(type == 1);
 | 
					 | 
				
			||||||
    setText(description);
 | 
					 | 
				
			||||||
  }, [type, description])
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  function submitChanges(e) {
 | 
					  return (<>
 | 
				
			||||||
    const newTitle = document.querySelector('span').innerHTML;
 | 
					 | 
				
			||||||
    postMutation.mutate({
 | 
					 | 
				
			||||||
      id: number, 
 | 
					 | 
				
			||||||
      title: newTitle, 
 | 
					 | 
				
			||||||
      description: enteredText,
 | 
					 | 
				
			||||||
      type: verb ? 1 : 0,
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
    navigate(`/${number}`, {replace: true})
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  return (
 | 
					 | 
				
			||||||
    <div className="main-column">
 | 
					 | 
				
			||||||
      <header>
 | 
					      <header>
 | 
				
			||||||
        <h1>
 | 
					        <h1>
 | 
				
			||||||
          <a href="/" onClick={linkClick}>🌳</a>
 | 
					          <a href="/" onClick={linkClick}>🌳</a>
 | 
				
			||||||
          {number}. 
 | 
					          {page?.number}. 
 | 
				
			||||||
          <span 
 | 
					          <input disabled={!editing} value={title} onChange={onChangeTitle}/> 
 | 
				
			||||||
              contentEditable={editing} 
 | 
					 | 
				
			||||||
              dangerouslySetInnerHTML={{__html: readyToShow ? title : "..." }} />
 | 
					 | 
				
			||||||
        </h1>
 | 
					        </h1>
 | 
				
			||||||
        <p>{readyToShow && editid && `saved at ${time} by user #${author}`}</p>
 | 
					        { historical && <p>saved at {time} by user #{author}</p> }
 | 
				
			||||||
        { editing ? <label>
 | 
					        { editing ? 
 | 
				
			||||||
          <input type="checkbox" checked={verb} onChange={() => setVerb(!verb)}/>
 | 
					            <label>
 | 
				
			||||||
          {verb ? "verb" : "noun"}
 | 
					              <input type="checkbox" checked={type} onChange={onChangeType}/>
 | 
				
			||||||
        </label> : <br/> }
 | 
					              {type ? "verb" : "noun"}
 | 
				
			||||||
 | 
					            </label> 
 | 
				
			||||||
 | 
					            : 
 | 
				
			||||||
 | 
					            <br/> 
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
        <hr/>
 | 
					        <hr/>
 | 
				
			||||||
      </header>
 | 
					      </header>
 | 
				
			||||||
      <section className="page-contents">
 | 
					      <section className="page-contents">
 | 
				
			||||||
        { readyToShow ? 
 | 
					        { editing ?
 | 
				
			||||||
          (
 | 
					 | 
				
			||||||
            editing ?
 | 
					 | 
				
			||||||
            <MDXEditor 
 | 
					            <MDXEditor 
 | 
				
			||||||
              markdown={description} 
 | 
					              markdown={description} 
 | 
				
			||||||
              plugins={[
 | 
					              plugins={[
 | 
				
			||||||
                headingsPlugin(), 
 | 
					                headingsPlugin(), 
 | 
				
			||||||
                quotePlugin(),
 | 
					                quotePlugin(),
 | 
				
			||||||
 | 
					                listsPlugin(),
 | 
				
			||||||
 | 
					                thematicBreakPlugin(),
 | 
				
			||||||
                linkPlugin(),
 | 
					                linkPlugin(),
 | 
				
			||||||
                diffSourcePlugin({ diffMarkdown: 'ahhhh do not look upon me!', viewMode: 'source' }),
 | 
					                diffSourcePlugin({ diffMarkdown: 'ahhhh do not look upon me!', viewMode: 'source' }),
 | 
				
			||||||
                ]} 
 | 
					                ]} 
 | 
				
			||||||
                onChange={(md) => setText(md)}
 | 
					              onChange={onChangeText}
 | 
				
			||||||
              />
 | 
					              />
 | 
				
			||||||
            :
 | 
					            :
 | 
				
			||||||
              (
 | 
					            ( type ? 
 | 
				
			||||||
                verb ? 
 | 
					 | 
				
			||||||
                <pre><code dangerouslySetInnerHTML={{__html: lua.replace(/\n/g, "<br>").replace(/  /g, "  ")}}></code></pre>
 | 
					                <pre><code dangerouslySetInnerHTML={{__html: lua.replace(/\n/g, "<br>").replace(/  /g, "  ")}}></code></pre>
 | 
				
			||||||
                :
 | 
					                :
 | 
				
			||||||
                <div 
 | 
					                <div 
 | 
				
			||||||
                  dangerouslySetInnerHTML={{__html: html}} 
 | 
					                  dangerouslySetInnerHTML={{__html: html}} 
 | 
				
			||||||
                  onClick={linkClick} />
 | 
					                  onClick={linkClick} />
 | 
				
			||||||
            )
 | 
					            )
 | 
				
			||||||
          )
 | 
					 | 
				
			||||||
          :
 | 
					 | 
				
			||||||
          "..."
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
      </section>
 | 
					      </section>
 | 
				
			||||||
      <button
 | 
					    </>
 | 
				
			||||||
        onClick={() => navigate(`/${number}/history`)}>
 | 
					 | 
				
			||||||
        History
 | 
					 | 
				
			||||||
      </button>
 | 
					 | 
				
			||||||
      {editing && (
 | 
					 | 
				
			||||||
        <button 
 | 
					 | 
				
			||||||
          disabled={postMutation.isPending}
 | 
					 | 
				
			||||||
          onClick={submitChanges}>
 | 
					 | 
				
			||||||
          {postMutation.isPending ? "Updating..." : "Update"}
 | 
					 | 
				
			||||||
        </button>)}
 | 
					 | 
				
			||||||
      {!editing && !editid && (
 | 
					 | 
				
			||||||
        <button
 | 
					 | 
				
			||||||
          disabled={!loggedIn}
 | 
					 | 
				
			||||||
          onClick={() => navigate(`/${number}/edit`)}>
 | 
					 | 
				
			||||||
          Edit Page 
 | 
					 | 
				
			||||||
        </button>)}
 | 
					 | 
				
			||||||
    </div>
 | 
					 | 
				
			||||||
  );
 | 
					  );
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -2,9 +2,12 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
I think `npm start` in each directory should run the app
 | 
					I think `npm start` in each directory should run the app
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Environoment variables!
 | 
				
			||||||
 | 
					- `SESSION_DATA_PASSWORD` for encrypting session cookies
 | 
				
			||||||
 | 
					- `USER_CREATION_PASSWORD` to set the extra password required for account creation
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# build log
 | 
					# build log
 | 
				
			||||||
 | 
					
 | 
				
			||||||
- `brew tap libsql/sqld`
 | 
					 | 
				
			||||||
- `brew install sqld-beta`
 | 
					- `brew install sqld-beta`
 | 
				
			||||||
- to run cast `npm start` in each directory
 | 
					- to run cast `npm start` in each directory
 | 
				
			||||||
- `node initialize_db.js` `node populate_db.js`
 | 
					- `node initialize_db.js` `node populate_db.js`
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										2
									
								
								server/.env
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								server/.env
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,2 @@
 | 
				
			|||||||
 | 
					USER_CREATION_PASSWORD="a softer birdsong"
 | 
				
			||||||
 | 
					SESSION_DATA_SECRET="dontcheckmeintoversioncontrolpleeeeasealskdfjsdf"
 | 
				
			||||||
@ -2,19 +2,44 @@ const { JSDOM } = require("jsdom");
 | 
				
			|||||||
const graphology = require("graphology");
 | 
					const graphology = require("graphology");
 | 
				
			||||||
const { circular } = require('graphology-layout');
 | 
					const { circular } = require('graphology-layout');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const graphQueryString = `
 | 
				
			||||||
 | 
						select p.number, p.html, p.time, a.contents
 | 
				
			||||||
 | 
						from pages p
 | 
				
			||||||
 | 
						left join attributes a on a.number = p.number
 | 
				
			||||||
 | 
						where time >= (
 | 
				
			||||||
 | 
							select max(s.time) 
 | 
				
			||||||
 | 
							from pages s
 | 
				
			||||||
 | 
							where s.number = p.number
 | 
				
			||||||
 | 
						)
 | 
				
			||||||
 | 
						order by p.time desc
 | 
				
			||||||
 | 
						`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function graphFromList(allTheStuff) {
 | 
					function graphFromList(allTheStuff) {
 | 
				
			||||||
	const graph = new graphology.Graph();
 | 
						const graph = new graphology.Graph();
 | 
				
			||||||
	for (const {number, html} of allTheStuff) {
 | 
						for (const {number, html} of allTheStuff) {
 | 
				
			||||||
		if (!graph.hasNode(number)) graph.addNode(number);
 | 
							if (!graph.hasNode(number)) graph.addNode(number);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for (const {number, html} of allTheStuff) {
 | 
						for (const {number, html, contents} of allTheStuff) {
 | 
				
			||||||
		const { document } = (new JSDOM(html)).window;
 | 
							const { document } = (new JSDOM(html)).window;
 | 
				
			||||||
		const links = document.querySelectorAll('a');
 | 
							const links = document.querySelectorAll('a');
 | 
				
			||||||
		links.forEach((link) => {
 | 
							links.forEach((link) => {
 | 
				
			||||||
			const referent = link.href.replace("/","");
 | 
								const referent = link.href.replace("/","");
 | 
				
			||||||
			graph.mergeEdge(number, referent);
 | 
								graph.mergeEdge(number, referent, {color: "white"});
 | 
				
			||||||
		});
 | 
							});
 | 
				
			||||||
 | 
							
 | 
				
			||||||
 | 
							if (typeof contents?.verbs?.forEach === 'function') {
 | 
				
			||||||
 | 
								contents?.verbs?.forEach((verbNumber) => {
 | 
				
			||||||
 | 
									graph.mergeEdge(number, verbNumber, {color: "red"})
 | 
				
			||||||
 | 
								});
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (typeof contents?.contents?.forEach === 'function') {
 | 
				
			||||||
 | 
								contents?.contents?.forEach((nounNumber) => {
 | 
				
			||||||
 | 
									graph.mergeEdge(number, nounNumber, {color: "green"});
 | 
				
			||||||
 | 
								});
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	circular.assign(graph);
 | 
						circular.assign(graph);
 | 
				
			||||||
@ -22,4 +47,4 @@ function graphFromList(allTheStuff) {
 | 
				
			|||||||
	return graph;
 | 
						return graph;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
module.exports = { graphFromList: graphFromList };
 | 
					module.exports = { graphFromList, graphQueryString };
 | 
				
			||||||
@ -18,6 +18,10 @@ async function makeLua() {
 | 
				
			|||||||
	lua.global.set('lookUpObject', luaSafe(lookUpObject));
 | 
						lua.global.set('lookUpObject', luaSafe(lookUpObject));
 | 
				
			||||||
	lua.global.set('lookUpObjectAttributes', luaSafe(lookUpObjectAttributes));
 | 
						lua.global.set('lookUpObjectAttributes', luaSafe(lookUpObjectAttributes));
 | 
				
			||||||
	lua.global.set('interpret', interpret);
 | 
						lua.global.set('interpret', interpret);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// let's get cheeky with it
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						lua.global.set('console_log', console.log);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
makeLua();
 | 
					makeLua();
 | 
				
			||||||
@ -33,6 +37,7 @@ function interpret(context, player, command) {
 | 
				
			|||||||
	//		may? be null if it's executing from a script? used for output.
 | 
						//		may? be null if it's executing from a script? used for output.
 | 
				
			||||||
	//		so arguably 
 | 
						//		so arguably 
 | 
				
			||||||
	// command: the full string typed in by the player
 | 
						// command: the full string typed in by the player
 | 
				
			||||||
 | 
						console.log(context, player, command);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	const socket = sockets.get(player)
 | 
						const socket = sockets.get(player)
 | 
				
			||||||
	const wordsAndQuotes = tokenizeQuotes(command.trim());
 | 
						const wordsAndQuotes = tokenizeQuotes(command.trim());
 | 
				
			||||||
@ -55,12 +60,12 @@ function interpret(context, player, command) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
			const [first, second, third, ...rest] = words;
 | 
								const [first, second, third, ...rest] = words;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			executeVerb(command, player, verb, prepMap, player, second, third, ...rest)
 | 
								return executeVerb(command, player, verb, prepMap, context, player, second, third, ...rest);
 | 
				
			||||||
		} else {
 | 
							} else {
 | 
				
			||||||
			interpret(1,1, `system_send_message '{"error": "verb ${verbId} not found in ${fullCommand}"}' to ${player}`);
 | 
								interpret(1,1, `system_send_message '{"error": "verb ${verbId} not found in ${fullCommand}"}' to ${player}`);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	} catch (error) {
 | 
						} catch (error) {
 | 
				
			||||||
		interpret(1,1, `system_send_message '{"error": "error found: ${error}"}' to ${player}`);
 | 
							executeVerb('', player, 2, {to: player}, null, `{"error": "error found: ${error}"}`);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/*
 | 
						/*
 | 
				
			||||||
@ -75,22 +80,16 @@ function interpret(context, player, command) {
 | 
				
			|||||||
	*/
 | 
						*/
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
async function executeVerb(fullCommand, outputObject, verbId, prepositions, subject, object, ...rest) {
 | 
					async function executeVerb(fullCommand, outputObject, verbId, prepositions, context, subject, object, ...rest) {
 | 
				
			||||||
	if (verbId == 2) {
 | 
						if (verbId == 2) {
 | 
				
			||||||
		// todo: make this more intelligently get the rright thing to send
 | 
							// todo: make this more intelligently get the rright thing to send
 | 
				
			||||||
		let theObject;
 | 
					 | 
				
			||||||
		try {
 | 
					 | 
				
			||||||
			theObject = verifyObjectReference(object);
 | 
					 | 
				
			||||||
		} catch (error) {
 | 
					 | 
				
			||||||
			theObject = verifyObjectReference(outputObject);
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		if (prepositions["to"]) {
 | 
							if (prepositions["to"]) {
 | 
				
			||||||
			let destination = verifyObjectReference(prepositions["to"])
 | 
								let destination = verifyObjectReference(prepositions["to"])
 | 
				
			||||||
			return sockets.get(destination)?.send(object);
 | 
								return sockets.get(destination)?.send(object);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		return sockets
 | 
							return sockets
 | 
				
			||||||
			.get(theObject)
 | 
								.get(outputObject)
 | 
				
			||||||
			?.send(`full command: ${fullCommand}. verb id: ${verbId}. subject: ${subject} object: ${object}`); 
 | 
								?.send(`{"message": "missing \"to\" clause trying to execute command: ${fullCommand}"}`); 
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	const fullVerb = lookUpObject(verbId);
 | 
						const fullVerb = lookUpObject(verbId);
 | 
				
			||||||
@ -102,19 +101,36 @@ async function executeVerb(fullCommand, outputObject, verbId, prepositions, subj
 | 
				
			|||||||
	const verbName = "verb" + Math.random().toString(36).substring(2);
 | 
						const verbName = "verb" + Math.random().toString(36).substring(2);
 | 
				
			||||||
	const body = fullVerb.description.replace(/ /g, " ");
 | 
						const body = fullVerb.description.replace(/ /g, " ");
 | 
				
			||||||
	const verbDeclaration = `
 | 
						const verbDeclaration = `
 | 
				
			||||||
		function ${verbName} (fullCommand, outputObject, prepositionMap, subject, object, ...)
 | 
							function ${verbName} (fullCommand, outputObject, prepositionMap, context, subject, object, ...)
 | 
				
			||||||
			${body}
 | 
								${body}
 | 
				
			||||||
		end`;
 | 
							end`;
 | 
				
			||||||
	console.log("verb we're running:");
 | 
						console.log("verb we're running:");
 | 
				
			||||||
	console.log(verbDeclaration);
 | 
						console.log(verbDeclaration);
 | 
				
			||||||
	await lua.doString(verbDeclaration);
 | 
						await lua.doString(verbDeclaration).catch((e) => {
 | 
				
			||||||
 | 
								console.log("found an error heyyoyyoyoyoyo", e);
 | 
				
			||||||
 | 
								let msg = {"error": "syntax error defining a verb!", "errorObject": e};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								sockets.get(outputObject)?.send(JSON.stringify(msg));
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
	
 | 
						
 | 
				
			||||||
	let returnValue = await lua.global.get(verbName)(fullCommand, outputObject, prepositions, subject, object, ...rest);
 | 
						let func = lua.global.get(verbName);
 | 
				
			||||||
 | 
						if (typeof func === "function") {
 | 
				
			||||||
 | 
							let returnValue;
 | 
				
			||||||
 | 
							try { 
 | 
				
			||||||
 | 
								returnValue = 
 | 
				
			||||||
 | 
									lua.global.get(verbName)(fullCommand, outputObject, prepositions, context, subject, object, ...rest)
 | 
				
			||||||
 | 
							} catch (error) {
 | 
				
			||||||
 | 
								sockets.get(outputObject)?.send(JSON.stringify({"error": "error executing a verb!"}));
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
		// maybe unset it so we dno't have an ever-growing set of functions cluttering up the space?
 | 
							// maybe unset it so we dno't have an ever-growing set of functions cluttering up the space?
 | 
				
			||||||
		//lua.global.set(verbName, null); 
 | 
							//lua.global.set(verbName, null); 
 | 
				
			||||||
		
 | 
							
 | 
				
			||||||
		return returnValue;
 | 
							return returnValue;
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							sockets.get(outputObject)?.send(JSON.stringify({"error": "error defining a verb!"}));
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return null;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const objectQuery = db.prepare('select * from pages where number=? order by time desc');
 | 
					const objectQuery = db.prepare('select * from pages where number=? order by time desc');
 | 
				
			||||||
@ -173,7 +189,7 @@ function findVerbOnObject(word, object) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
			if (!fullVerb) continue;
 | 
								if (!fullVerb) continue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			// test our word against 
 | 
								// test our word against each verb in turn
 | 
				
			||||||
			if (word.toLowerCase() == fullVerb.title.toLowerCase()) {
 | 
								if (word.toLowerCase() == fullVerb.title.toLowerCase()) {
 | 
				
			||||||
				return verbId;
 | 
									return verbId;
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
@ -189,8 +205,7 @@ function findAllVerbsOnObject(object) {
 | 
				
			|||||||
	let focus = object;
 | 
						let focus = object;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	while (focus) {
 | 
						while (focus) {
 | 
				
			||||||
		const newVerbs = (getAttribute(focus, "verbs") || []).filter((verb) => !(verb in verbs));
 | 
							verbs = concatWithoutDuplicates(verbs, getAttribute(focus, "verbs"));
 | 
				
			||||||
		verbs = verbs.concat(newVerbs);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
		focus = getAttribute(focus, "parent");
 | 
							focus = getAttribute(focus, "parent");
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@ -225,6 +240,7 @@ function getAttribute(obj, attributeName) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	let attributeStore = pullAttribute.get(verifyObjectReference(obj));
 | 
						let attributeStore = pullAttribute.get(verifyObjectReference(obj));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!attributeStore || !attributeStore.contents) return undefined;
 | 
				
			||||||
	let contents = JSON.parse(attributeStore.contents);
 | 
						let contents = JSON.parse(attributeStore.contents);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (contents.hasOwnProperty(attributeName)) {
 | 
						if (contents.hasOwnProperty(attributeName)) {
 | 
				
			||||||
@ -256,11 +272,29 @@ function setAttribute(obj, attributeName, value) {
 | 
				
			|||||||
	const attributeStore = pullAttribute.get(verifyObjectReference(obj));
 | 
						const attributeStore = pullAttribute.get(verifyObjectReference(obj));
 | 
				
			||||||
	const contents = JSON.parse(attributeStore.contents);
 | 
						const contents = JSON.parse(attributeStore.contents);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (isEmptyObject(value) && isArray(contents[attributes]))
 | 
				
			||||||
 | 
							contents[attributeName] = [];
 | 
				
			||||||
 | 
						else
 | 
				
			||||||
		contents[attributeName] = value;
 | 
							contents[attributeName] = value;
 | 
				
			||||||
 | 
						// hacky fix so that an empty array can't be replaced with an empty object
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	
 | 
						
 | 
				
			||||||
	insertAttribute.run({...attributeStore, contents: JSON.stringify(contents)});
 | 
						insertAttribute.run({...attributeStore, contents: JSON.stringify(contents)});
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function isArray(obj) {
 | 
				
			||||||
 | 
						return Object.prototype.toString.apply(value) === '[object Array]';
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					function isEmptyObject(obj) {
 | 
				
			||||||
 | 
						if (typeof obj !== "obj") return false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for (const prop in obj) {
 | 
				
			||||||
 | 
							if (Object.hasOwn(obj, prop)) return true;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return true;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function deleteAttribute(obj, attributeName) {
 | 
					function deleteAttribute(obj, attributeName) {
 | 
				
			||||||
	if (!verifyObjectReference(obj)) return undefined;
 | 
						if (!verifyObjectReference(obj)) return undefined;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -288,8 +322,10 @@ function verifyObjectReference(obj) {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function concatWithoutDuplicates(a, b) {
 | 
					function concatWithoutDuplicates(a, b) {
 | 
				
			||||||
	let b2 = b.filter((x) => !(x in a));
 | 
						let c = []
 | 
				
			||||||
	return a.concat(b2);
 | 
						a?.forEach((x) => { if (!c.includes(x)) c.push(x) })
 | 
				
			||||||
 | 
						b?.forEach((x) => { if (!c.includes(x)) c.push(x) });
 | 
				
			||||||
 | 
						return c;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
function luaSafe(func) {
 | 
					function luaSafe(func) {
 | 
				
			||||||
	return (...all) => {
 | 
						return (...all) => {
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										342
									
								
								server/package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										342
									
								
								server/package-lock.json
									
									
									
										generated
									
									
									
								
							@ -1,15 +1,14 @@
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
  "name": "forest-server",
 | 
					  "name": "forest-server",
 | 
				
			||||||
  "version": "1.0.0",
 | 
					  "version": "0.0.1",
 | 
				
			||||||
  "lockfileVersion": 3,
 | 
					  "lockfileVersion": 3,
 | 
				
			||||||
  "requires": true,
 | 
					  "requires": true,
 | 
				
			||||||
  "packages": {
 | 
					  "packages": {
 | 
				
			||||||
    "": {
 | 
					    "": {
 | 
				
			||||||
      "name": "forest-server",
 | 
					      "name": "forest-server",
 | 
				
			||||||
      "version": "1.0.0",
 | 
					      "version": "0.0.1",
 | 
				
			||||||
      "license": "ISC",
 | 
					      "license": "ISC",
 | 
				
			||||||
      "dependencies": {
 | 
					      "dependencies": {
 | 
				
			||||||
        "@libsql/client": "^0.14.0",
 | 
					 | 
				
			||||||
        "argon2": "^0.41.1",
 | 
					        "argon2": "^0.41.1",
 | 
				
			||||||
        "better-sqlite3": "^11.3.0",
 | 
					        "better-sqlite3": "^11.3.0",
 | 
				
			||||||
        "better-sqlite3-session-store": "^0.1.0",
 | 
					        "better-sqlite3-session-store": "^0.1.0",
 | 
				
			||||||
@ -23,148 +22,12 @@
 | 
				
			|||||||
        "graphology-layout-forceatlas2": "^0.10.1",
 | 
					        "graphology-layout-forceatlas2": "^0.10.1",
 | 
				
			||||||
        "highlight.js": "^11.10.0",
 | 
					        "highlight.js": "^11.10.0",
 | 
				
			||||||
        "jsdom": "^25.0.0",
 | 
					        "jsdom": "^25.0.0",
 | 
				
			||||||
 | 
					        "node": "^22.10.0",
 | 
				
			||||||
        "nodemon": "^3.1.5",
 | 
					        "nodemon": "^3.1.5",
 | 
				
			||||||
        "showdown": "^2.1.0",
 | 
					        "showdown": "^2.1.0",
 | 
				
			||||||
        "wasmoon": "^1.16.0"
 | 
					        "wasmoon": "^1.16.0"
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "node_modules/@libsql/client": {
 | 
					 | 
				
			||||||
      "version": "0.14.0",
 | 
					 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/@libsql/client/-/client-0.14.0.tgz",
 | 
					 | 
				
			||||||
      "integrity": "sha512-/9HEKfn6fwXB5aTEEoMeFh4CtG0ZzbncBb1e++OCdVpgKZ/xyMsIVYXm0w7Pv4RUel803vE6LwniB3PqD72R0Q==",
 | 
					 | 
				
			||||||
      "dependencies": {
 | 
					 | 
				
			||||||
        "@libsql/core": "^0.14.0",
 | 
					 | 
				
			||||||
        "@libsql/hrana-client": "^0.7.0",
 | 
					 | 
				
			||||||
        "js-base64": "^3.7.5",
 | 
					 | 
				
			||||||
        "libsql": "^0.4.4",
 | 
					 | 
				
			||||||
        "promise-limit": "^2.7.0"
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    "node_modules/@libsql/core": {
 | 
					 | 
				
			||||||
      "version": "0.14.0",
 | 
					 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/@libsql/core/-/core-0.14.0.tgz",
 | 
					 | 
				
			||||||
      "integrity": "sha512-nhbuXf7GP3PSZgdCY2Ecj8vz187ptHlZQ0VRc751oB2C1W8jQUXKKklvt7t1LJiUTQBVJuadF628eUk+3cRi4Q==",
 | 
					 | 
				
			||||||
      "dependencies": {
 | 
					 | 
				
			||||||
        "js-base64": "^3.7.5"
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    "node_modules/@libsql/darwin-arm64": {
 | 
					 | 
				
			||||||
      "version": "0.4.5",
 | 
					 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/@libsql/darwin-arm64/-/darwin-arm64-0.4.5.tgz",
 | 
					 | 
				
			||||||
      "integrity": "sha512-xLdnn0NrgSk6OMi716FFs/27Hs33jtSd2fkKi/72Ey/qBtPWcB1BMurDQekzi0yAcfQTjGqIz7tpOibyjiEPyQ==",
 | 
					 | 
				
			||||||
      "cpu": [
 | 
					 | 
				
			||||||
        "arm64"
 | 
					 | 
				
			||||||
      ],
 | 
					 | 
				
			||||||
      "optional": true,
 | 
					 | 
				
			||||||
      "os": [
 | 
					 | 
				
			||||||
        "darwin"
 | 
					 | 
				
			||||||
      ]
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    "node_modules/@libsql/darwin-x64": {
 | 
					 | 
				
			||||||
      "version": "0.4.5",
 | 
					 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/@libsql/darwin-x64/-/darwin-x64-0.4.5.tgz",
 | 
					 | 
				
			||||||
      "integrity": "sha512-rZsEWj0H7oCqd5Y2pe0RzKmuQXC2OB1RbnFy4CvjeAjT6MP6mFp+Vx9mTCAUuJMhuoSVMsFPUJRpAQznl9E3Tg==",
 | 
					 | 
				
			||||||
      "cpu": [
 | 
					 | 
				
			||||||
        "x64"
 | 
					 | 
				
			||||||
      ],
 | 
					 | 
				
			||||||
      "optional": true,
 | 
					 | 
				
			||||||
      "os": [
 | 
					 | 
				
			||||||
        "darwin"
 | 
					 | 
				
			||||||
      ]
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    "node_modules/@libsql/hrana-client": {
 | 
					 | 
				
			||||||
      "version": "0.7.0",
 | 
					 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/@libsql/hrana-client/-/hrana-client-0.7.0.tgz",
 | 
					 | 
				
			||||||
      "integrity": "sha512-OF8fFQSkbL7vJY9rfuegK1R7sPgQ6kFMkDamiEccNUvieQ+3urzfDFI616oPl8V7T9zRmnTkSjMOImYCAVRVuw==",
 | 
					 | 
				
			||||||
      "dependencies": {
 | 
					 | 
				
			||||||
        "@libsql/isomorphic-fetch": "^0.3.1",
 | 
					 | 
				
			||||||
        "@libsql/isomorphic-ws": "^0.1.5",
 | 
					 | 
				
			||||||
        "js-base64": "^3.7.5",
 | 
					 | 
				
			||||||
        "node-fetch": "^3.3.2"
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    "node_modules/@libsql/isomorphic-fetch": {
 | 
					 | 
				
			||||||
      "version": "0.3.1",
 | 
					 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/@libsql/isomorphic-fetch/-/isomorphic-fetch-0.3.1.tgz",
 | 
					 | 
				
			||||||
      "integrity": "sha512-6kK3SUK5Uu56zPq/Las620n5aS9xJq+jMBcNSOmjhNf/MUvdyji4vrMTqD7ptY7/4/CAVEAYDeotUz60LNQHtw==",
 | 
					 | 
				
			||||||
      "engines": {
 | 
					 | 
				
			||||||
        "node": ">=18.0.0"
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    "node_modules/@libsql/isomorphic-ws": {
 | 
					 | 
				
			||||||
      "version": "0.1.5",
 | 
					 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/@libsql/isomorphic-ws/-/isomorphic-ws-0.1.5.tgz",
 | 
					 | 
				
			||||||
      "integrity": "sha512-DtLWIH29onUYR00i0GlQ3UdcTRC6EP4u9w/h9LxpUZJWRMARk6dQwZ6Jkd+QdwVpuAOrdxt18v0K2uIYR3fwFg==",
 | 
					 | 
				
			||||||
      "dependencies": {
 | 
					 | 
				
			||||||
        "@types/ws": "^8.5.4",
 | 
					 | 
				
			||||||
        "ws": "^8.13.0"
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    "node_modules/@libsql/linux-arm64-gnu": {
 | 
					 | 
				
			||||||
      "version": "0.4.5",
 | 
					 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/@libsql/linux-arm64-gnu/-/linux-arm64-gnu-0.4.5.tgz",
 | 
					 | 
				
			||||||
      "integrity": "sha512-VR09iu6KWGJ6fauCn59u/jJ9OA+/A2yQ0dr2HDN2zkRueLC6D2oGYt4gPfLZPFKf+WJpVMtIhNfd+Ru9MMaFkA==",
 | 
					 | 
				
			||||||
      "cpu": [
 | 
					 | 
				
			||||||
        "arm64"
 | 
					 | 
				
			||||||
      ],
 | 
					 | 
				
			||||||
      "optional": true,
 | 
					 | 
				
			||||||
      "os": [
 | 
					 | 
				
			||||||
        "linux"
 | 
					 | 
				
			||||||
      ]
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    "node_modules/@libsql/linux-arm64-musl": {
 | 
					 | 
				
			||||||
      "version": "0.4.5",
 | 
					 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/@libsql/linux-arm64-musl/-/linux-arm64-musl-0.4.5.tgz",
 | 
					 | 
				
			||||||
      "integrity": "sha512-74hvD5ej4rBshhxFGNYU16a3m8B/NjIPvhlZ/flG1Oeydfo6AuUXSSNFi+H5+zi9/uWuzyz5TLVeQcraoUV10A==",
 | 
					 | 
				
			||||||
      "cpu": [
 | 
					 | 
				
			||||||
        "arm64"
 | 
					 | 
				
			||||||
      ],
 | 
					 | 
				
			||||||
      "optional": true,
 | 
					 | 
				
			||||||
      "os": [
 | 
					 | 
				
			||||||
        "linux"
 | 
					 | 
				
			||||||
      ]
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    "node_modules/@libsql/linux-x64-gnu": {
 | 
					 | 
				
			||||||
      "version": "0.4.5",
 | 
					 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/@libsql/linux-x64-gnu/-/linux-x64-gnu-0.4.5.tgz",
 | 
					 | 
				
			||||||
      "integrity": "sha512-gb5WObGO3+rbuG8h9font1N02iF+zgYAgY0wNa8BNiZ5A9UolZKFxiqGFS7eHaAYfemHJKKTT+aAt3X2p5TibA==",
 | 
					 | 
				
			||||||
      "cpu": [
 | 
					 | 
				
			||||||
        "x64"
 | 
					 | 
				
			||||||
      ],
 | 
					 | 
				
			||||||
      "optional": true,
 | 
					 | 
				
			||||||
      "os": [
 | 
					 | 
				
			||||||
        "linux"
 | 
					 | 
				
			||||||
      ]
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    "node_modules/@libsql/linux-x64-musl": {
 | 
					 | 
				
			||||||
      "version": "0.4.5",
 | 
					 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/@libsql/linux-x64-musl/-/linux-x64-musl-0.4.5.tgz",
 | 
					 | 
				
			||||||
      "integrity": "sha512-JfyE6OVC5X4Nr4cFF77VhB1o+hBRxAqYT9YdeqnWdAQSYc/ASi5HnRALLAQEsGacFPZZ32pixfraQmPE3iJFfw==",
 | 
					 | 
				
			||||||
      "cpu": [
 | 
					 | 
				
			||||||
        "x64"
 | 
					 | 
				
			||||||
      ],
 | 
					 | 
				
			||||||
      "optional": true,
 | 
					 | 
				
			||||||
      "os": [
 | 
					 | 
				
			||||||
        "linux"
 | 
					 | 
				
			||||||
      ]
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    "node_modules/@libsql/win32-x64-msvc": {
 | 
					 | 
				
			||||||
      "version": "0.4.5",
 | 
					 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/@libsql/win32-x64-msvc/-/win32-x64-msvc-0.4.5.tgz",
 | 
					 | 
				
			||||||
      "integrity": "sha512-57GGurNJhOhq3XIopLdGnCoQ4kQAcmbmzzFoC4tpvDE/KSbwZ/13zqJWhQA41nMGk/PKM1XKfKmbIybKx1+eqA==",
 | 
					 | 
				
			||||||
      "cpu": [
 | 
					 | 
				
			||||||
        "x64"
 | 
					 | 
				
			||||||
      ],
 | 
					 | 
				
			||||||
      "optional": true,
 | 
					 | 
				
			||||||
      "os": [
 | 
					 | 
				
			||||||
        "win32"
 | 
					 | 
				
			||||||
      ]
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    "node_modules/@neon-rs/load": {
 | 
					 | 
				
			||||||
      "version": "0.0.4",
 | 
					 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/@neon-rs/load/-/load-0.0.4.tgz",
 | 
					 | 
				
			||||||
      "integrity": "sha512-kTPhdZyTQxB+2wpiRcFWrDcejc4JI6tkPuS7UZCG4l6Zvc5kU/gGQ/ozvHTh1XR5tS+UlfAfGuPajjzQjCiHCw=="
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    "node_modules/@phc/format": {
 | 
					    "node_modules/@phc/format": {
 | 
				
			||||||
      "version": "1.0.0",
 | 
					      "version": "1.0.0",
 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/@phc/format/-/format-1.0.0.tgz",
 | 
					      "resolved": "https://registry.npmjs.org/@phc/format/-/format-1.0.0.tgz",
 | 
				
			||||||
@ -178,22 +41,6 @@
 | 
				
			|||||||
      "resolved": "https://registry.npmjs.org/@types/emscripten/-/emscripten-1.39.10.tgz",
 | 
					      "resolved": "https://registry.npmjs.org/@types/emscripten/-/emscripten-1.39.10.tgz",
 | 
				
			||||||
      "integrity": "sha512-TB/6hBkYQJxsZHSqyeuO1Jt0AB/bW6G7rHt9g7lML7SOF6lbgcHvw/Lr+69iqN0qxgXLhWKScAon73JNnptuDw=="
 | 
					      "integrity": "sha512-TB/6hBkYQJxsZHSqyeuO1Jt0AB/bW6G7rHt9g7lML7SOF6lbgcHvw/Lr+69iqN0qxgXLhWKScAon73JNnptuDw=="
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "node_modules/@types/node": {
 | 
					 | 
				
			||||||
      "version": "22.7.3",
 | 
					 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/@types/node/-/node-22.7.3.tgz",
 | 
					 | 
				
			||||||
      "integrity": "sha512-qXKfhXXqGTyBskvWEzJZPUxSslAiLaB6JGP1ic/XTH9ctGgzdgYguuLP1C601aRTSDNlLb0jbKqXjZ48GNraSA==",
 | 
					 | 
				
			||||||
      "dependencies": {
 | 
					 | 
				
			||||||
        "undici-types": "~6.19.2"
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    "node_modules/@types/ws": {
 | 
					 | 
				
			||||||
      "version": "8.5.12",
 | 
					 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.12.tgz",
 | 
					 | 
				
			||||||
      "integrity": "sha512-3tPRkv1EtkDpzlgyKyI8pGsGZAGPEaXeu0DOj5DI25Ja91bdAYddYHbADRYVrZMRbfW+1l5YwXVDKohDJNQxkQ==",
 | 
					 | 
				
			||||||
      "dependencies": {
 | 
					 | 
				
			||||||
        "@types/node": "*"
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    "node_modules/accepts": {
 | 
					    "node_modules/accepts": {
 | 
				
			||||||
      "version": "1.3.8",
 | 
					      "version": "1.3.8",
 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
 | 
					      "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
 | 
				
			||||||
@ -520,9 +367,9 @@
 | 
				
			|||||||
      }
 | 
					      }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "node_modules/cookie": {
 | 
					    "node_modules/cookie": {
 | 
				
			||||||
      "version": "0.6.0",
 | 
					      "version": "0.7.1",
 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz",
 | 
					      "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz",
 | 
				
			||||||
      "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==",
 | 
					      "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==",
 | 
				
			||||||
      "engines": {
 | 
					      "engines": {
 | 
				
			||||||
        "node": ">= 0.6"
 | 
					        "node": ">= 0.6"
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
@ -563,14 +410,6 @@
 | 
				
			|||||||
        "node": ">=18"
 | 
					        "node": ">=18"
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "node_modules/data-uri-to-buffer": {
 | 
					 | 
				
			||||||
      "version": "4.0.1",
 | 
					 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz",
 | 
					 | 
				
			||||||
      "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==",
 | 
					 | 
				
			||||||
      "engines": {
 | 
					 | 
				
			||||||
        "node": ">= 12"
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    "node_modules/data-urls": {
 | 
					    "node_modules/data-urls": {
 | 
				
			||||||
      "version": "5.0.0",
 | 
					      "version": "5.0.0",
 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-5.0.0.tgz",
 | 
					      "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-5.0.0.tgz",
 | 
				
			||||||
@ -760,16 +599,16 @@
 | 
				
			|||||||
      }
 | 
					      }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "node_modules/express": {
 | 
					    "node_modules/express": {
 | 
				
			||||||
      "version": "4.21.0",
 | 
					      "version": "4.21.1",
 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/express/-/express-4.21.0.tgz",
 | 
					      "resolved": "https://registry.npmjs.org/express/-/express-4.21.1.tgz",
 | 
				
			||||||
      "integrity": "sha512-VqcNGcj/Id5ZT1LZ/cfihi3ttTn+NJmkli2eZADigjq29qTlWi/hAQ43t/VLPq8+UX06FCEx3ByOYet6ZFblng==",
 | 
					      "integrity": "sha512-YSFlK1Ee0/GC8QaO91tHcDxJiE/X4FbpAyQWkxAvG6AXCuR65YzK8ua6D9hvi/TzUfZMpc+BwuM1IPw8fmQBiQ==",
 | 
				
			||||||
      "dependencies": {
 | 
					      "dependencies": {
 | 
				
			||||||
        "accepts": "~1.3.8",
 | 
					        "accepts": "~1.3.8",
 | 
				
			||||||
        "array-flatten": "1.1.1",
 | 
					        "array-flatten": "1.1.1",
 | 
				
			||||||
        "body-parser": "1.20.3",
 | 
					        "body-parser": "1.20.3",
 | 
				
			||||||
        "content-disposition": "0.5.4",
 | 
					        "content-disposition": "0.5.4",
 | 
				
			||||||
        "content-type": "~1.0.4",
 | 
					        "content-type": "~1.0.4",
 | 
				
			||||||
        "cookie": "0.6.0",
 | 
					        "cookie": "0.7.1",
 | 
				
			||||||
        "cookie-signature": "1.0.6",
 | 
					        "cookie-signature": "1.0.6",
 | 
				
			||||||
        "debug": "2.6.9",
 | 
					        "debug": "2.6.9",
 | 
				
			||||||
        "depd": "2.0.0",
 | 
					        "depd": "2.0.0",
 | 
				
			||||||
@ -801,11 +640,11 @@
 | 
				
			|||||||
      }
 | 
					      }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "node_modules/express-session": {
 | 
					    "node_modules/express-session": {
 | 
				
			||||||
      "version": "1.18.0",
 | 
					      "version": "1.18.1",
 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/express-session/-/express-session-1.18.0.tgz",
 | 
					      "resolved": "https://registry.npmjs.org/express-session/-/express-session-1.18.1.tgz",
 | 
				
			||||||
      "integrity": "sha512-m93QLWr0ju+rOwApSsyso838LQwgfs44QtOP/WBiwtAgPIo/SAh1a5c6nn2BR6mFNZehTpqKDESzP+fRHVbxwQ==",
 | 
					      "integrity": "sha512-a5mtTqEaZvBCL9A9aqkrtfz+3SMDhOVUnjafjo+s7A9Txkq+SVX2DLvSp1Zrv4uCXa3lMSK3viWnh9Gg07PBUA==",
 | 
				
			||||||
      "dependencies": {
 | 
					      "dependencies": {
 | 
				
			||||||
        "cookie": "0.6.0",
 | 
					        "cookie": "0.7.2",
 | 
				
			||||||
        "cookie-signature": "1.0.7",
 | 
					        "cookie-signature": "1.0.7",
 | 
				
			||||||
        "debug": "2.6.9",
 | 
					        "debug": "2.6.9",
 | 
				
			||||||
        "depd": "~2.0.0",
 | 
					        "depd": "~2.0.0",
 | 
				
			||||||
@ -818,6 +657,14 @@
 | 
				
			|||||||
        "node": ">= 0.8.0"
 | 
					        "node": ">= 0.8.0"
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					    "node_modules/express-session/node_modules/cookie": {
 | 
				
			||||||
 | 
					      "version": "0.7.2",
 | 
				
			||||||
 | 
					      "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz",
 | 
				
			||||||
 | 
					      "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==",
 | 
				
			||||||
 | 
					      "engines": {
 | 
				
			||||||
 | 
					        "node": ">= 0.6"
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
    "node_modules/express-session/node_modules/cookie-signature": {
 | 
					    "node_modules/express-session/node_modules/cookie-signature": {
 | 
				
			||||||
      "version": "1.0.7",
 | 
					      "version": "1.0.7",
 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.7.tgz",
 | 
					      "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.7.tgz",
 | 
				
			||||||
@ -857,28 +704,6 @@
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "node_modules/fetch-blob": {
 | 
					 | 
				
			||||||
      "version": "3.2.0",
 | 
					 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz",
 | 
					 | 
				
			||||||
      "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==",
 | 
					 | 
				
			||||||
      "funding": [
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
          "type": "github",
 | 
					 | 
				
			||||||
          "url": "https://github.com/sponsors/jimmywarting"
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
          "type": "paypal",
 | 
					 | 
				
			||||||
          "url": "https://paypal.me/jimmywarting"
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
      ],
 | 
					 | 
				
			||||||
      "dependencies": {
 | 
					 | 
				
			||||||
        "node-domexception": "^1.0.0",
 | 
					 | 
				
			||||||
        "web-streams-polyfill": "^3.0.3"
 | 
					 | 
				
			||||||
      },
 | 
					 | 
				
			||||||
      "engines": {
 | 
					 | 
				
			||||||
        "node": "^12.20 || >= 14.13"
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    "node_modules/file-uri-to-path": {
 | 
					    "node_modules/file-uri-to-path": {
 | 
				
			||||||
      "version": "1.0.0",
 | 
					      "version": "1.0.0",
 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz",
 | 
					      "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz",
 | 
				
			||||||
@ -925,17 +750,6 @@
 | 
				
			|||||||
        "node": ">= 6"
 | 
					        "node": ">= 6"
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "node_modules/formdata-polyfill": {
 | 
					 | 
				
			||||||
      "version": "4.0.10",
 | 
					 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz",
 | 
					 | 
				
			||||||
      "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==",
 | 
					 | 
				
			||||||
      "dependencies": {
 | 
					 | 
				
			||||||
        "fetch-blob": "^3.1.2"
 | 
					 | 
				
			||||||
      },
 | 
					 | 
				
			||||||
      "engines": {
 | 
					 | 
				
			||||||
        "node": ">=12.20.0"
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    "node_modules/forwarded": {
 | 
					    "node_modules/forwarded": {
 | 
				
			||||||
      "version": "0.2.0",
 | 
					      "version": "0.2.0",
 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
 | 
					      "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
 | 
				
			||||||
@ -1331,11 +1145,6 @@
 | 
				
			|||||||
      "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz",
 | 
					      "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz",
 | 
				
			||||||
      "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ=="
 | 
					      "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ=="
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "node_modules/js-base64": {
 | 
					 | 
				
			||||||
      "version": "3.7.7",
 | 
					 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-3.7.7.tgz",
 | 
					 | 
				
			||||||
      "integrity": "sha512-7rCnleh0z2CkXhH67J8K1Ytz0b2Y+yxTPL+/KOJoa20hfnVQ/3/T6W/KflYI4bRHRagNeXeU2bkNGI3v1oS/lw=="
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    "node_modules/jsdom": {
 | 
					    "node_modules/jsdom": {
 | 
				
			||||||
      "version": "25.0.0",
 | 
					      "version": "25.0.0",
 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-25.0.0.tgz",
 | 
					      "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-25.0.0.tgz",
 | 
				
			||||||
@ -1383,42 +1192,6 @@
 | 
				
			|||||||
        "node": ">= 0.6"
 | 
					        "node": ">= 0.6"
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "node_modules/libsql": {
 | 
					 | 
				
			||||||
      "version": "0.4.5",
 | 
					 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/libsql/-/libsql-0.4.5.tgz",
 | 
					 | 
				
			||||||
      "integrity": "sha512-sorTJV6PNt94Wap27Sai5gtVLIea4Otb2LUiAUyr3p6BPOScGMKGt5F1b5X/XgkNtcsDKeX5qfeBDj+PdShclQ==",
 | 
					 | 
				
			||||||
      "cpu": [
 | 
					 | 
				
			||||||
        "x64",
 | 
					 | 
				
			||||||
        "arm64",
 | 
					 | 
				
			||||||
        "wasm32"
 | 
					 | 
				
			||||||
      ],
 | 
					 | 
				
			||||||
      "os": [
 | 
					 | 
				
			||||||
        "darwin",
 | 
					 | 
				
			||||||
        "linux",
 | 
					 | 
				
			||||||
        "win32"
 | 
					 | 
				
			||||||
      ],
 | 
					 | 
				
			||||||
      "dependencies": {
 | 
					 | 
				
			||||||
        "@neon-rs/load": "^0.0.4",
 | 
					 | 
				
			||||||
        "detect-libc": "2.0.2"
 | 
					 | 
				
			||||||
      },
 | 
					 | 
				
			||||||
      "optionalDependencies": {
 | 
					 | 
				
			||||||
        "@libsql/darwin-arm64": "0.4.5",
 | 
					 | 
				
			||||||
        "@libsql/darwin-x64": "0.4.5",
 | 
					 | 
				
			||||||
        "@libsql/linux-arm64-gnu": "0.4.5",
 | 
					 | 
				
			||||||
        "@libsql/linux-arm64-musl": "0.4.5",
 | 
					 | 
				
			||||||
        "@libsql/linux-x64-gnu": "0.4.5",
 | 
					 | 
				
			||||||
        "@libsql/linux-x64-musl": "0.4.5",
 | 
					 | 
				
			||||||
        "@libsql/win32-x64-msvc": "0.4.5"
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    "node_modules/libsql/node_modules/detect-libc": {
 | 
					 | 
				
			||||||
      "version": "2.0.2",
 | 
					 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.2.tgz",
 | 
					 | 
				
			||||||
      "integrity": "sha512-UX6sGumvvqSaXgdKGUsgZWqcUyIXZ/vZTrlRT/iobiKhGL0zL4d3osHj3uqllWJK+i+sixDS/3COVEOFbupFyw==",
 | 
					 | 
				
			||||||
      "engines": {
 | 
					 | 
				
			||||||
        "node": ">=8"
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    "node_modules/media-typer": {
 | 
					    "node_modules/media-typer": {
 | 
				
			||||||
      "version": "0.3.0",
 | 
					      "version": "0.3.0",
 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
 | 
					      "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
 | 
				
			||||||
@ -1534,6 +1307,21 @@
 | 
				
			|||||||
        "node": ">= 0.6"
 | 
					        "node": ">= 0.6"
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					    "node_modules/node": {
 | 
				
			||||||
 | 
					      "version": "22.10.0",
 | 
				
			||||||
 | 
					      "resolved": "https://registry.npmjs.org/node/-/node-22.10.0.tgz",
 | 
				
			||||||
 | 
					      "integrity": "sha512-xiXybf2ElhQcbN3DXfUNKxxHK3fsQrxqNS6m7fu9u5E2YaolF2GfuNJV1+xR2CBraTPtvGablAocjKQ8Ey8gyQ==",
 | 
				
			||||||
 | 
					      "hasInstallScript": true,
 | 
				
			||||||
 | 
					      "dependencies": {
 | 
				
			||||||
 | 
					        "node-bin-setup": "^1.0.0"
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      "bin": {
 | 
				
			||||||
 | 
					        "node": "bin/node"
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      "engines": {
 | 
				
			||||||
 | 
					        "npm": ">=5.0.0"
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
    "node_modules/node-abi": {
 | 
					    "node_modules/node-abi": {
 | 
				
			||||||
      "version": "3.68.0",
 | 
					      "version": "3.68.0",
 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.68.0.tgz",
 | 
					      "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.68.0.tgz",
 | 
				
			||||||
@ -1553,40 +1341,10 @@
 | 
				
			|||||||
        "node": "^18 || ^20 || >= 21"
 | 
					        "node": "^18 || ^20 || >= 21"
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "node_modules/node-domexception": {
 | 
					    "node_modules/node-bin-setup": {
 | 
				
			||||||
      "version": "1.0.0",
 | 
					      "version": "1.1.3",
 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz",
 | 
					      "resolved": "https://registry.npmjs.org/node-bin-setup/-/node-bin-setup-1.1.3.tgz",
 | 
				
			||||||
      "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==",
 | 
					      "integrity": "sha512-opgw9iSCAzT2+6wJOETCpeRYAQxSopqQ2z+N6BXwIMsQQ7Zj5M8MaafQY8JMlolRR6R1UXg2WmhKp0p9lSOivg=="
 | 
				
			||||||
      "funding": [
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
          "type": "github",
 | 
					 | 
				
			||||||
          "url": "https://github.com/sponsors/jimmywarting"
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
          "type": "github",
 | 
					 | 
				
			||||||
          "url": "https://paypal.me/jimmywarting"
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
      ],
 | 
					 | 
				
			||||||
      "engines": {
 | 
					 | 
				
			||||||
        "node": ">=10.5.0"
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    "node_modules/node-fetch": {
 | 
					 | 
				
			||||||
      "version": "3.3.2",
 | 
					 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz",
 | 
					 | 
				
			||||||
      "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==",
 | 
					 | 
				
			||||||
      "dependencies": {
 | 
					 | 
				
			||||||
        "data-uri-to-buffer": "^4.0.0",
 | 
					 | 
				
			||||||
        "fetch-blob": "^3.1.4",
 | 
					 | 
				
			||||||
        "formdata-polyfill": "^4.0.10"
 | 
					 | 
				
			||||||
      },
 | 
					 | 
				
			||||||
      "engines": {
 | 
					 | 
				
			||||||
        "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
 | 
					 | 
				
			||||||
      },
 | 
					 | 
				
			||||||
      "funding": {
 | 
					 | 
				
			||||||
        "type": "opencollective",
 | 
					 | 
				
			||||||
        "url": "https://opencollective.com/node-fetch"
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "node_modules/node-gyp-build": {
 | 
					    "node_modules/node-gyp-build": {
 | 
				
			||||||
      "version": "4.8.2",
 | 
					      "version": "4.8.2",
 | 
				
			||||||
@ -1770,11 +1528,6 @@
 | 
				
			|||||||
        "node": ">=10"
 | 
					        "node": ">=10"
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "node_modules/promise-limit": {
 | 
					 | 
				
			||||||
      "version": "2.7.0",
 | 
					 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/promise-limit/-/promise-limit-2.7.0.tgz",
 | 
					 | 
				
			||||||
      "integrity": "sha512-7nJ6v5lnJsXwGprnGXga4wx6d1POjvi5Qmf1ivTRxTjH4Z/9Czja/UCMLVmB9N93GeWOU93XaFaEt6jbuoagNw=="
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    "node_modules/proxy-addr": {
 | 
					    "node_modules/proxy-addr": {
 | 
				
			||||||
      "version": "2.0.7",
 | 
					      "version": "2.0.7",
 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
 | 
					      "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
 | 
				
			||||||
@ -2271,11 +2024,6 @@
 | 
				
			|||||||
      "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz",
 | 
					      "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz",
 | 
				
			||||||
      "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA=="
 | 
					      "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA=="
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "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=="
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    "node_modules/universalify": {
 | 
					    "node_modules/universalify": {
 | 
				
			||||||
      "version": "0.2.0",
 | 
					      "version": "0.2.0",
 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz",
 | 
					      "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz",
 | 
				
			||||||
@ -2344,14 +2092,6 @@
 | 
				
			|||||||
        "wasmoon": "bin/wasmoon"
 | 
					        "wasmoon": "bin/wasmoon"
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "node_modules/web-streams-polyfill": {
 | 
					 | 
				
			||||||
      "version": "3.3.3",
 | 
					 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz",
 | 
					 | 
				
			||||||
      "integrity": "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==",
 | 
					 | 
				
			||||||
      "engines": {
 | 
					 | 
				
			||||||
        "node": ">= 8"
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    "node_modules/webidl-conversions": {
 | 
					    "node_modules/webidl-conversions": {
 | 
				
			||||||
      "version": "7.0.0",
 | 
					      "version": "7.0.0",
 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz",
 | 
					      "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz",
 | 
				
			||||||
 | 
				
			|||||||
@ -1,10 +1,10 @@
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
  "name": "forest-server",
 | 
					  "name": "forest-server",
 | 
				
			||||||
  "version": "1.0.0",
 | 
					  "version": "0.0.1",
 | 
				
			||||||
  "description": "the backend for a wiki/mud",
 | 
					  "description": "the backend for a wiki/mud",
 | 
				
			||||||
  "main": "server.js",
 | 
					  "main": "server.js",
 | 
				
			||||||
  "scripts": {
 | 
					  "scripts": {
 | 
				
			||||||
    "start": "nodemon server.js",
 | 
					    "start": "node --watch --env-file=.env server.js",
 | 
				
			||||||
    "test": "echo \"Error: no test specified\" && exit 1"
 | 
					    "test": "echo \"Error: no test specified\" && exit 1"
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  "repository": {
 | 
					  "repository": {
 | 
				
			||||||
@ -14,7 +14,6 @@
 | 
				
			|||||||
  "author": "",
 | 
					  "author": "",
 | 
				
			||||||
  "license": "ISC",
 | 
					  "license": "ISC",
 | 
				
			||||||
  "dependencies": {
 | 
					  "dependencies": {
 | 
				
			||||||
    "@libsql/client": "^0.14.0",
 | 
					 | 
				
			||||||
    "argon2": "^0.41.1",
 | 
					    "argon2": "^0.41.1",
 | 
				
			||||||
    "better-sqlite3": "^11.3.0",
 | 
					    "better-sqlite3": "^11.3.0",
 | 
				
			||||||
    "better-sqlite3-session-store": "^0.1.0",
 | 
					    "better-sqlite3-session-store": "^0.1.0",
 | 
				
			||||||
@ -28,6 +27,7 @@
 | 
				
			|||||||
    "graphology-layout-forceatlas2": "^0.10.1",
 | 
					    "graphology-layout-forceatlas2": "^0.10.1",
 | 
				
			||||||
    "highlight.js": "^11.10.0",
 | 
					    "highlight.js": "^11.10.0",
 | 
				
			||||||
    "jsdom": "^25.0.0",
 | 
					    "jsdom": "^25.0.0",
 | 
				
			||||||
 | 
					    "node": "^22.10.0",
 | 
				
			||||||
    "nodemon": "^3.1.5",
 | 
					    "nodemon": "^3.1.5",
 | 
				
			||||||
    "showdown": "^2.1.0",
 | 
					    "showdown": "^2.1.0",
 | 
				
			||||||
    "wasmoon": "^1.16.0"
 | 
					    "wasmoon": "^1.16.0"
 | 
				
			||||||
 | 
				
			|||||||
@ -1,4 +1,4 @@
 | 
				
			|||||||
const { graphFromList } = require('../graphStuff.js');
 | 
					const { graphFromList, graphQueryString } = require('../graphStuff.js');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const sqlite = require('better-sqlite3');
 | 
					const sqlite = require('better-sqlite3');
 | 
				
			||||||
const db = new sqlite('the_big_db.db', { verbose: console.log });
 | 
					const db = new sqlite('the_big_db.db', { verbose: console.log });
 | 
				
			||||||
@ -15,22 +15,18 @@ app.use(user_routes);
 | 
				
			|||||||
const live_connection_routes = require('./live.js');
 | 
					const live_connection_routes = require('./live.js');
 | 
				
			||||||
app.use(live_connection_routes);
 | 
					app.use(live_connection_routes);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const graphQuery = db.prepare(`
 | 
					 | 
				
			||||||
	select p.number, p.html, p.time 
 | 
					 | 
				
			||||||
	from pages p
 | 
					 | 
				
			||||||
	where time >= (
 | 
					 | 
				
			||||||
		select max(s.time) 
 | 
					 | 
				
			||||||
		from pages s
 | 
					 | 
				
			||||||
		where s.number = p.number
 | 
					 | 
				
			||||||
	)
 | 
					 | 
				
			||||||
	order by p.time desc
 | 
					 | 
				
			||||||
	`);
 | 
					 | 
				
			||||||
app.get('/graph', (req, res) => {
 | 
					app.get('/graph', (req, res) => {
 | 
				
			||||||
	try {
 | 
						try {
 | 
				
			||||||
		const rows = graphQuery.all();
 | 
							const rows = db.prepare(graphQueryString).all();
 | 
				
			||||||
		const graph = graphFromList(rows);
 | 
							const parsedRows = rows.map((row) => {
 | 
				
			||||||
 | 
								let newRow = row;
 | 
				
			||||||
 | 
								newRow.contents = JSON.parse(row.contents);
 | 
				
			||||||
 | 
								return newRow;
 | 
				
			||||||
 | 
							});
 | 
				
			||||||
 | 
							const graph = graphFromList(parsedRows);
 | 
				
			||||||
		res.status(200).json(graph);
 | 
							res.status(200).json(graph);
 | 
				
			||||||
	} catch (error) {
 | 
						} catch (error) {
 | 
				
			||||||
 | 
							console.log("error:", error);
 | 
				
			||||||
		res.status(500).json({"error": error});
 | 
							res.status(500).json({"error": error});
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
				
			|||||||
@ -11,8 +11,6 @@ const { interpret, sockets, lookUpObject,
 | 
				
			|||||||
  getAttribute, setAttribute, hasOwnAttribute, deleteAttribute,
 | 
					  getAttribute, setAttribute, hasOwnAttribute, deleteAttribute,
 | 
				
			||||||
  findAllVerbsInArea } = require('../interpreter.js');
 | 
					  findAllVerbsInArea } = require('../interpreter.js');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
app.ws('/embody', (ws, req) => {
 | 
					app.ws('/embody', (ws, req) => {
 | 
				
			||||||
  const character = req.session.characterId;
 | 
					  const character = req.session.characterId;
 | 
				
			||||||
  if (!character) {
 | 
					  if (!character) {
 | 
				
			||||||
@ -23,9 +21,10 @@ app.ws('/embody', (ws, req) => {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	sockets.set(character, ws);
 | 
						sockets.set(character, ws);
 | 
				
			||||||
  console.log("sending location change, should get attribute for 30");
 | 
					  console.log("sending location change, should get attribute for 30");
 | 
				
			||||||
  ws.send(`location change to: #${getAttribute(character, "location")}`);
 | 
					  ws.send(JSON.stringify({setPageNumber: getAttribute(character, "location")}));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  ws.on('message', (msg) => { 
 | 
					  ws.on('message', (msg) => { 
 | 
				
			||||||
 | 
					    console.log("received message: ", msg);
 | 
				
			||||||
  	const location = getAttribute(character, "location");
 | 
					  	const location = getAttribute(character, "location");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    interpret(location, character, msg);
 | 
					    interpret(location, character, msg);
 | 
				
			||||||
 | 
				
			|||||||
@ -46,23 +46,42 @@ app.post('/page/new', loginRequired, (req, res) => {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const getPageQuery = db.prepare('select * from pages where number=? order by time desc');
 | 
				
			||||||
app.get('/page/:number', (req, res) => {
 | 
					app.get('/page/:number', (req, res) => {
 | 
				
			||||||
	try {
 | 
						try {
 | 
				
			||||||
		const page = db.prepare('select * from pages where number=:number order by time desc').get(req.params);
 | 
							const page = getPageQuery.get(req.params.number);
 | 
				
			||||||
		if (page === undefined) res.status(404).json({"error": "page not found"});
 | 
							if (!page) res.status(404).json({"error": "page not found"});
 | 
				
			||||||
		else res.status(200).json(page);
 | 
							else res.status(200).json(page);
 | 
				
			||||||
  } catch (error) {
 | 
					  } catch (error) {
 | 
				
			||||||
  	res.status(500).json({"error": error});
 | 
					  	res.status(500).json({"error": error});
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					const getAttributesQuery = db.prepare('select * from attributes where number=?');
 | 
				
			||||||
 | 
					app.get('/page/:number/attributes', (req, res) => {
 | 
				
			||||||
 | 
						try {
 | 
				
			||||||
 | 
							const page = getPageQuery.get(req.params.number);
 | 
				
			||||||
 | 
							if (!page) {
 | 
				
			||||||
 | 
								res.status(404).json({"error": "page not found"});
 | 
				
			||||||
 | 
								return;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							const attributes = getAttributesQuery.get(req.params.number);
 | 
				
			||||||
 | 
							if (!attributes) res.status(200).json({});
 | 
				
			||||||
 | 
							else if (attributes.contents) res.status(200).json(JSON.parse(attributes.contents));
 | 
				
			||||||
 | 
							else res.status(200).json({});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  } catch (error) {
 | 
				
			||||||
 | 
					  	res.status(500).json({"error": error});
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const pageInsertQuery = db.prepare('insert into pages (number, title, description, html, lua, author, type) values (?, ?, ?, ?, ?, ?, ?)')
 | 
				
			||||||
app.post('/page/:number', loginRequired, (req, res) => {
 | 
					app.post('/page/:number', loginRequired, (req, res) => {
 | 
				
			||||||
	try {
 | 
						try {
 | 
				
			||||||
		const html = converter.makeHtml(req.body.description);
 | 
							const html = converter.makeHtml(req.body.description);
 | 
				
			||||||
		const lua =  hljs.highlight(req.body.description, {language: 'lua'}).value;
 | 
							const lua =  hljs.highlight(req.body.description, {language: 'lua'}).value;
 | 
				
			||||||
		console.log(lua);
 | 
					
 | 
				
			||||||
		const changes = db.prepare('insert into pages (number, title, description, html, lua, author, type) values (?, ?, ?, ?, ?, ?, ?)')
 | 
							const changes = pageInsertQuery.run(req.params.number, req.body.title, req.body.description, html, lua, req.session.userId, req.body.type ? 1 : 0);
 | 
				
			||||||
			.run(req.params.number, req.body.title, req.body.description, html, lua, req.session.userId, req.body.type ? 1 : 0);
 | 
					 | 
				
			||||||
		res.status(200).json(changes);
 | 
							res.status(200).json(changes);
 | 
				
			||||||
	} catch (error) {
 | 
						} catch (error) {
 | 
				
			||||||
		console.log(error);
 | 
							console.log(error);
 | 
				
			||||||
@ -70,6 +89,16 @@ app.post('/page/:number', loginRequired, (req, res) => {
 | 
				
			|||||||
 	}
 | 
					 	}
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const attributesUpdateQuery = db.prepare('update attributes set contents=? where number=?')
 | 
				
			||||||
 | 
					app.post('/page/:number/attributes', loginRequired, (req, res) => {
 | 
				
			||||||
 | 
						try {
 | 
				
			||||||
 | 
							const changes = attributesUpdateQuery.run(req.body, req.params.number);
 | 
				
			||||||
 | 
							res.status(200).json(changes);
 | 
				
			||||||
 | 
						} catch (error) {
 | 
				
			||||||
 | 
							res.status(500).json({"error": error});
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
app.delete('/page/:number', loginRequired, (req, res) => {
 | 
					app.delete('/page/:number', loginRequired, (req, res) => {
 | 
				
			||||||
	try {
 | 
						try {
 | 
				
			||||||
		db.prepare('delete from pages where number = ?').run(req.params.number);
 | 
							db.prepare('delete from pages where number = ?').run(req.params.number);
 | 
				
			||||||
 | 
				
			|||||||
@ -23,6 +23,7 @@ app.post('/register', async (req, res) => {
 | 
				
			|||||||
		const hash = await argon2.hash(password);
 | 
							const hash = await argon2.hash(password);
 | 
				
			||||||
		const inserted = db.prepare('insert into users (name, password) values (?, ?)').run(name, hash);
 | 
							const inserted = db.prepare('insert into users (name, password) values (?, ?)').run(name, hash);
 | 
				
			||||||
		res.status(200).json(inserted);
 | 
							res.status(200).json(inserted);
 | 
				
			||||||
 | 
							// todo: create an object associated with that players
 | 
				
			||||||
	} catch (error) {
 | 
						} catch (error) {
 | 
				
			||||||
		res.status(500).json({"error": error});
 | 
							res.status(500).json({"error": error});
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
				
			|||||||
@ -22,8 +22,9 @@ app.use(session({
 | 
				
			|||||||
      intervalMs: 15*60*1000
 | 
					      intervalMs: 15*60*1000
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }),
 | 
					  }),
 | 
				
			||||||
  secret: "dno'tt check me into versino control",
 | 
					  secret: process.env.SESSION_DATA_SECRET,
 | 
				
			||||||
  resave: false
 | 
					  resave: false,
 | 
				
			||||||
 | 
					  saveUninitialized: false
 | 
				
			||||||
}));
 | 
					}));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
app.use("/api", apiRoutes);
 | 
					app.use("/api", apiRoutes);
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
		Reference in New Issue
	
	Block a user